├── UI_Screenshots
├── 1426736056.png
├── 1426736071.png
├── 1426736084.png
├── 1426736092.png
├── 1426736099.png
├── 1426736115.png
├── 1426736131.png
├── 1426736139.png
├── 1426736147.png
├── 1426736156.png
├── 1426736164.png
├── 1426736171.png
├── 1426736179.png
├── 1426736191.png
├── 1426736199.png
├── 1426736207.png
├── 1426736217.png
├── 1426736236.png
├── 1426736245.png
├── 1426736253.png
├── 1426736262.png
├── 1426736271.png
├── 1426736279.png
├── 1426736288.png
├── 1426736297.png
├── 1426736307.png
├── 1426736314.png
├── 1426736323.png
├── 1426736331.png
├── 1426736341.png
├── 1426736348.png
├── 1426736379.png
├── 1426736386.png
├── 1426736393.png
├── 1426736404.png
├── 1426736416.png
├── 1426736423.png
├── 1426736430.png
├── 1426736436.png
├── 1426736463.png
├── 1426736470.png
└── 1426736477.png
├── README.md
├── CityWebServer
├── packages.config
├── Models
│ ├── PolicyInfo.cs
│ ├── ChirperMessage.cs
│ ├── CityInfo.cs
│ ├── PublicTransportLine.cs
│ ├── PopulationGroup.cs
│ ├── Economy.cs
│ └── DistrictInfo.cs
├── Helpers
│ ├── EnumExtensions.cs
│ ├── CitizenExtensions.cs
│ ├── NameValueCollectionExtensions.cs
│ ├── DistrictExtensions.cs
│ ├── TemplateHelper.cs
│ ├── ConfigurationHelper.cs
│ └── ApacheMimeTypes.cs
├── UserModInfo.cs
├── RequestHandlers
│ ├── MessageRequestHandler.cs
│ ├── BudgetRequestHandler.cs
│ ├── BuildingRequestHandler.cs
│ ├── VehicleRequestHandler.cs
│ ├── TransportRequestHandler.cs
│ └── CityInfoRequestHandler.cs
├── Properties
│ └── AssemblyInfo.cs
├── Retrievers
│ └── ChirpRetriever.cs
├── WebServer.cs
├── wwwroot
│ ├── script.js
│ ├── index.html
│ ├── knockout.mapping-latest.js
│ └── bootstrap.min.js
├── WebsiteButton.cs
├── CityWebServer.csproj
└── IntegratedWebServer.cs
├── CityWebServer.Extensibility
├── packages.config
├── ILogAppender.cs
├── IResponseFormatter.cs
├── IWebServer.cs
├── LogAppenderEventArgs.cs
├── ResponseFormatters
│ ├── PlainTextResponseFormatter.cs
│ ├── HtmlResponseFormatter.cs
│ └── JsonResponseFormatter.cs
├── Properties
│ └── AssemblyInfo.cs
├── IRequestHandler.cs
├── RestfulRequestHandlerBase.cs
├── CityWebServer.Extensibility.csproj
└── RequestHandlerBase.cs
├── Assemblies
└── _PUT_ASSEMBLIES_HERE.txt
├── SampleWebServerExtension
├── UserModInfo.cs
├── SampleRequestHandler.cs
├── Properties
│ └── AssemblyInfo.cs
└── SampleWebServerExtension.csproj
├── CityWebServer.sln
└── .gitignore
/UI_Screenshots/1426736056.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rychard/CityWebServer/HEAD/UI_Screenshots/1426736056.png
--------------------------------------------------------------------------------
/UI_Screenshots/1426736071.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rychard/CityWebServer/HEAD/UI_Screenshots/1426736071.png
--------------------------------------------------------------------------------
/UI_Screenshots/1426736084.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rychard/CityWebServer/HEAD/UI_Screenshots/1426736084.png
--------------------------------------------------------------------------------
/UI_Screenshots/1426736092.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rychard/CityWebServer/HEAD/UI_Screenshots/1426736092.png
--------------------------------------------------------------------------------
/UI_Screenshots/1426736099.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rychard/CityWebServer/HEAD/UI_Screenshots/1426736099.png
--------------------------------------------------------------------------------
/UI_Screenshots/1426736115.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rychard/CityWebServer/HEAD/UI_Screenshots/1426736115.png
--------------------------------------------------------------------------------
/UI_Screenshots/1426736131.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rychard/CityWebServer/HEAD/UI_Screenshots/1426736131.png
--------------------------------------------------------------------------------
/UI_Screenshots/1426736139.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rychard/CityWebServer/HEAD/UI_Screenshots/1426736139.png
--------------------------------------------------------------------------------
/UI_Screenshots/1426736147.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rychard/CityWebServer/HEAD/UI_Screenshots/1426736147.png
--------------------------------------------------------------------------------
/UI_Screenshots/1426736156.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rychard/CityWebServer/HEAD/UI_Screenshots/1426736156.png
--------------------------------------------------------------------------------
/UI_Screenshots/1426736164.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rychard/CityWebServer/HEAD/UI_Screenshots/1426736164.png
--------------------------------------------------------------------------------
/UI_Screenshots/1426736171.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rychard/CityWebServer/HEAD/UI_Screenshots/1426736171.png
--------------------------------------------------------------------------------
/UI_Screenshots/1426736179.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rychard/CityWebServer/HEAD/UI_Screenshots/1426736179.png
--------------------------------------------------------------------------------
/UI_Screenshots/1426736191.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rychard/CityWebServer/HEAD/UI_Screenshots/1426736191.png
--------------------------------------------------------------------------------
/UI_Screenshots/1426736199.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rychard/CityWebServer/HEAD/UI_Screenshots/1426736199.png
--------------------------------------------------------------------------------
/UI_Screenshots/1426736207.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rychard/CityWebServer/HEAD/UI_Screenshots/1426736207.png
--------------------------------------------------------------------------------
/UI_Screenshots/1426736217.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rychard/CityWebServer/HEAD/UI_Screenshots/1426736217.png
--------------------------------------------------------------------------------
/UI_Screenshots/1426736236.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rychard/CityWebServer/HEAD/UI_Screenshots/1426736236.png
--------------------------------------------------------------------------------
/UI_Screenshots/1426736245.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rychard/CityWebServer/HEAD/UI_Screenshots/1426736245.png
--------------------------------------------------------------------------------
/UI_Screenshots/1426736253.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rychard/CityWebServer/HEAD/UI_Screenshots/1426736253.png
--------------------------------------------------------------------------------
/UI_Screenshots/1426736262.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rychard/CityWebServer/HEAD/UI_Screenshots/1426736262.png
--------------------------------------------------------------------------------
/UI_Screenshots/1426736271.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rychard/CityWebServer/HEAD/UI_Screenshots/1426736271.png
--------------------------------------------------------------------------------
/UI_Screenshots/1426736279.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rychard/CityWebServer/HEAD/UI_Screenshots/1426736279.png
--------------------------------------------------------------------------------
/UI_Screenshots/1426736288.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rychard/CityWebServer/HEAD/UI_Screenshots/1426736288.png
--------------------------------------------------------------------------------
/UI_Screenshots/1426736297.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rychard/CityWebServer/HEAD/UI_Screenshots/1426736297.png
--------------------------------------------------------------------------------
/UI_Screenshots/1426736307.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rychard/CityWebServer/HEAD/UI_Screenshots/1426736307.png
--------------------------------------------------------------------------------
/UI_Screenshots/1426736314.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rychard/CityWebServer/HEAD/UI_Screenshots/1426736314.png
--------------------------------------------------------------------------------
/UI_Screenshots/1426736323.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rychard/CityWebServer/HEAD/UI_Screenshots/1426736323.png
--------------------------------------------------------------------------------
/UI_Screenshots/1426736331.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rychard/CityWebServer/HEAD/UI_Screenshots/1426736331.png
--------------------------------------------------------------------------------
/UI_Screenshots/1426736341.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rychard/CityWebServer/HEAD/UI_Screenshots/1426736341.png
--------------------------------------------------------------------------------
/UI_Screenshots/1426736348.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rychard/CityWebServer/HEAD/UI_Screenshots/1426736348.png
--------------------------------------------------------------------------------
/UI_Screenshots/1426736379.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rychard/CityWebServer/HEAD/UI_Screenshots/1426736379.png
--------------------------------------------------------------------------------
/UI_Screenshots/1426736386.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rychard/CityWebServer/HEAD/UI_Screenshots/1426736386.png
--------------------------------------------------------------------------------
/UI_Screenshots/1426736393.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rychard/CityWebServer/HEAD/UI_Screenshots/1426736393.png
--------------------------------------------------------------------------------
/UI_Screenshots/1426736404.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rychard/CityWebServer/HEAD/UI_Screenshots/1426736404.png
--------------------------------------------------------------------------------
/UI_Screenshots/1426736416.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rychard/CityWebServer/HEAD/UI_Screenshots/1426736416.png
--------------------------------------------------------------------------------
/UI_Screenshots/1426736423.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rychard/CityWebServer/HEAD/UI_Screenshots/1426736423.png
--------------------------------------------------------------------------------
/UI_Screenshots/1426736430.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rychard/CityWebServer/HEAD/UI_Screenshots/1426736430.png
--------------------------------------------------------------------------------
/UI_Screenshots/1426736436.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rychard/CityWebServer/HEAD/UI_Screenshots/1426736436.png
--------------------------------------------------------------------------------
/UI_Screenshots/1426736463.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rychard/CityWebServer/HEAD/UI_Screenshots/1426736463.png
--------------------------------------------------------------------------------
/UI_Screenshots/1426736470.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rychard/CityWebServer/HEAD/UI_Screenshots/1426736470.png
--------------------------------------------------------------------------------
/UI_Screenshots/1426736477.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rychard/CityWebServer/HEAD/UI_Screenshots/1426736477.png
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # CityWebServer
2 | Adds a web server to Cities: Skylines
3 |
4 | 
5 |
--------------------------------------------------------------------------------
/CityWebServer/packages.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/CityWebServer.Extensibility/packages.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/CityWebServer.Extensibility/ILogAppender.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace CityWebServer.Extensibility
4 | {
5 | public interface ILogAppender
6 | {
7 | event EventHandler LogMessage;
8 | }
9 | }
--------------------------------------------------------------------------------
/CityWebServer/Models/PolicyInfo.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace CityWebServer.Models
4 | {
5 | public class PolicyInfo
6 | {
7 | public String Name { get; set; }
8 |
9 | public Boolean Enabled { get; set; }
10 | }
11 | }
--------------------------------------------------------------------------------
/CityWebServer.Extensibility/IResponseFormatter.cs:
--------------------------------------------------------------------------------
1 | using System.Net;
2 |
3 | namespace CityWebServer.Extensibility
4 | {
5 | public abstract class IResponseFormatter
6 | {
7 | public abstract void WriteContent(HttpListenerResponse response);
8 | }
9 | }
--------------------------------------------------------------------------------
/CityWebServer/Models/ChirperMessage.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace CityWebServer.Models
4 | {
5 | public class ChirperMessage
6 | {
7 | public int SenderID { get; set; }
8 |
9 | public String SenderName { get; set; }
10 |
11 | public String Text { get; set; }
12 | }
13 | }
--------------------------------------------------------------------------------
/CityWebServer.Extensibility/IWebServer.cs:
--------------------------------------------------------------------------------
1 | namespace CityWebServer.Extensibility
2 | {
3 | public interface IWebServer
4 | {
5 | ///
6 | /// Gets an array containing all currently registered request handlers.
7 | ///
8 | IRequestHandler[] RequestHandlers { get; }
9 | }
10 | }
--------------------------------------------------------------------------------
/CityWebServer/Helpers/EnumExtensions.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 |
4 | namespace CityWebServer.Helpers
5 | {
6 | public static class EnumHelper
7 | {
8 | public static IEnumerable GetValues()
9 | {
10 | return (T[])Enum.GetValues(typeof(T));
11 | }
12 | }
13 | }
--------------------------------------------------------------------------------
/CityWebServer.Extensibility/LogAppenderEventArgs.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace CityWebServer.Extensibility
4 | {
5 | public class LogAppenderEventArgs : EventArgs
6 | {
7 | public String LogLine { get; set; }
8 |
9 | public LogAppenderEventArgs(String logLine)
10 | {
11 | LogLine = logLine;
12 | }
13 | }
14 | }
--------------------------------------------------------------------------------
/CityWebServer/Models/CityInfo.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace CityWebServer.Models
4 | {
5 | public class CityInfo
6 | {
7 | public String Name { get; set; }
8 |
9 | public DateTime Time { get; set; }
10 |
11 | public DistrictInfo GlobalDistrict { get; set; }
12 |
13 | public DistrictInfo[] Districts { get; set; }
14 | }
15 | }
--------------------------------------------------------------------------------
/CityWebServer/Helpers/CitizenExtensions.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using ColossalFramework;
3 |
4 | namespace CityWebServer.Helpers
5 | {
6 | public static class CitizenExtensions
7 | {
8 | public static String GetName(this Citizen citizen)
9 | {
10 | return Singleton.instance.GetCitizenName(citizen.m_instance);
11 | }
12 | }
13 | }
--------------------------------------------------------------------------------
/CityWebServer/Models/PublicTransportLine.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace CityWebServer.Models
4 | {
5 | public class PublicTransportLine
6 | {
7 | public String Name { get; set; }
8 |
9 | public int VehicleCount { get; set; }
10 |
11 | public int StopCount { get; set; }
12 |
13 | public PopulationGroup[] Passengers { get; set; }
14 | }
15 | }
--------------------------------------------------------------------------------
/CityWebServer/UserModInfo.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using ICities;
3 |
4 | namespace CityWebServer
5 | {
6 | public class UserModInfo : IUserMod
7 | {
8 | public String Name
9 | {
10 | get { return "Integrated Web Server"; }
11 | }
12 |
13 | public String Description
14 | {
15 | get { return "Host a web-server allowing you to communicate with the game via a web-browser."; }
16 | }
17 | }
18 | }
--------------------------------------------------------------------------------
/Assemblies/_PUT_ASSEMBLIES_HERE.txt:
--------------------------------------------------------------------------------
1 | This directory must contain the following files:
2 |
3 | - Assembly-CSharp.dll
4 | - ColossalManaged.dll
5 | - ICities.dll
6 | - UnityEngine.dll
7 | - UnityEngine.UI.dll
8 |
9 | These files can be found in the following location:
10 |
11 | \SteamApps\common\Cities_Skylines\Cities_Data\Managed
12 |
13 | Copy those files into this directory.
14 |
15 | (I personally prefer to create symbolic links, but copying them is the easier solution.)
--------------------------------------------------------------------------------
/CityWebServer/Models/PopulationGroup.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace CityWebServer.Models
4 | {
5 | public class PopulationGroup
6 | {
7 | public String Name { get; set; }
8 |
9 | public int Amount { get; set; }
10 |
11 | public PopulationGroup()
12 | {
13 | }
14 |
15 | public PopulationGroup(String name, int amount)
16 | {
17 | Name = name;
18 | Amount = amount;
19 | }
20 | }
21 | }
--------------------------------------------------------------------------------
/SampleWebServerExtension/UserModInfo.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using ICities;
3 |
4 | namespace SampleWebServerExtension
5 | {
6 | public class UserModInfo : IUserMod
7 | {
8 | public String Name
9 | {
10 | get { return "Sample Web Server Extension"; }
11 | }
12 |
13 | public String Description
14 | {
15 | get { return "Adds a sample page to the integrated web server. Doesn't do anything without it!"; }
16 | }
17 | }
18 | }
--------------------------------------------------------------------------------
/SampleWebServerExtension/SampleRequestHandler.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Net;
3 | using CityWebServer.Extensibility;
4 |
5 | namespace SampleWebServerExtension
6 | {
7 | public class SampleRequestHandler : RequestHandlerBase
8 | {
9 | public SampleRequestHandler(IWebServer server)
10 | : base(server, new Guid("1a255904-bf72-406e-b5e2-c5a43fdd9bba"), "Sample", "Rychard", 100, "/Sample")
11 | {
12 | }
13 |
14 | public override IResponseFormatter Handle(HttpListenerRequest request)
15 | {
16 | const String content = "This is a sample page!";
17 |
18 | return HtmlResponse(content);
19 | }
20 | }
21 | }
--------------------------------------------------------------------------------
/CityWebServer.Extensibility/ResponseFormatters/PlainTextResponseFormatter.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Net;
3 | using System.Text;
4 |
5 | namespace CityWebServer.Extensibility.Responses
6 | {
7 | internal class PlainTextResponseFormatter : IResponseFormatter
8 | {
9 | private readonly String _content;
10 | private readonly HttpStatusCode _statusCode;
11 |
12 | public PlainTextResponseFormatter(String content, HttpStatusCode statusCode)
13 | {
14 | _content = content;
15 | _statusCode = statusCode;
16 | }
17 |
18 | public override void WriteContent(HttpListenerResponse response)
19 | {
20 | byte[] buf = Encoding.UTF8.GetBytes(_content);
21 |
22 | response.StatusCode = (int)_statusCode;
23 | response.ContentType = "text/plain";
24 | response.ContentLength64 = buf.Length;
25 | response.OutputStream.Write(buf, 0, buf.Length);
26 | }
27 | }
28 | }
--------------------------------------------------------------------------------
/CityWebServer.Extensibility/ResponseFormatters/HtmlResponseFormatter.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Net;
3 | using System.Text;
4 |
5 | namespace CityWebServer.Extensibility.Responses
6 | {
7 | internal class HtmlResponseFormatter : IResponseFormatter
8 | {
9 | private readonly String _content;
10 | private readonly HttpStatusCode _statusCode;
11 |
12 | public HtmlResponseFormatter(String content, HttpStatusCode statusCode = HttpStatusCode.OK)
13 | {
14 | _content = content;
15 | _statusCode = statusCode;
16 | }
17 |
18 | public override void WriteContent(HttpListenerResponse response)
19 | {
20 | byte[] buf = Encoding.UTF8.GetBytes(_content);
21 |
22 | response.StatusCode = (int)_statusCode;
23 | response.ContentType = "text/html";
24 | response.ContentLength64 = buf.Length;
25 | response.OutputStream.Write(buf, 0, buf.Length);
26 | }
27 | }
28 | }
--------------------------------------------------------------------------------
/CityWebServer/RequestHandlers/MessageRequestHandler.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Net;
3 | using CityWebServer.Extensibility;
4 | using CityWebServer.Retrievers;
5 | using JetBrains.Annotations;
6 |
7 | namespace CityWebServer.RequestHandlers
8 | {
9 | [UsedImplicitly]
10 | public class MessageRequestHandler : RequestHandlerBase
11 | {
12 | private readonly ChirpRetriever _chirpRetriever;
13 |
14 | public MessageRequestHandler(IWebServer server)
15 | : base(server, new Guid("b4efeced-1dbb-435a-8999-9f8adaa5036e"), "Chirper Messages", "Rychard", 100, "/Messages")
16 | {
17 | _chirpRetriever = new ChirpRetriever();
18 | _chirpRetriever.LogMessage += (sender, args) => { OnLogMessage(args.LogLine); };
19 | }
20 |
21 | public override IResponseFormatter Handle(HttpListenerRequest request)
22 | {
23 | // TODO: Customize request handling.
24 | var messages = _chirpRetriever.Messages;
25 |
26 | return JsonResponse(messages);
27 | }
28 | }
29 | }
--------------------------------------------------------------------------------
/CityWebServer.Extensibility/ResponseFormatters/JsonResponseFormatter.cs:
--------------------------------------------------------------------------------
1 | using System.Net;
2 | using System.Text;
3 |
4 | namespace CityWebServer.Extensibility.Responses
5 | {
6 | internal class JsonResponseFormatter : IResponseFormatter
7 | {
8 | private readonly T _content;
9 | private readonly HttpStatusCode _statusCode;
10 |
11 | public JsonResponseFormatter(T content, HttpStatusCode statusCode)
12 | {
13 | _content = content;
14 | _statusCode = statusCode;
15 | }
16 |
17 | public override void WriteContent(HttpListenerResponse response)
18 | {
19 | var writer = new JsonFx.Json.JsonWriter();
20 | var serializedData = writer.Write(_content);
21 |
22 | byte[] buf = Encoding.UTF8.GetBytes(serializedData);
23 |
24 | response.StatusCode = (int)_statusCode;
25 | response.ContentType = "text/json";
26 | response.ContentLength64 = buf.Length;
27 | response.OutputStream.Write(buf, 0, buf.Length);
28 | }
29 | }
30 | }
--------------------------------------------------------------------------------
/CityWebServer/Helpers/NameValueCollectionExtensions.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Specialized;
3 | using System.Linq;
4 |
5 | namespace CityWebServer.Helpers
6 | {
7 | public static class NameValueCollectionExtensions
8 | {
9 | ///
10 | /// Determines whether the specified key exists in the current collection.
11 | ///
12 | /// Returns true if the specified key exists, otherwise false
13 | public static Boolean HasKey(this NameValueCollection nvc, String key)
14 | {
15 | return nvc.AllKeys.Any(obj => obj == key);
16 | }
17 |
18 | ///
19 | /// Gets the value of the specified key as an integer.
20 | ///
21 | /// Returns the value of the integer in the specified key, or null if the value is not a valid integer.
22 | public static int? GetInteger(this NameValueCollection nvc, String key)
23 | {
24 | var value = nvc.Get(key);
25 | int result;
26 | if (Int32.TryParse(value, out result))
27 | {
28 | return result;
29 | }
30 | return null;
31 | }
32 | }
33 | }
--------------------------------------------------------------------------------
/CityWebServer/RequestHandlers/BudgetRequestHandler.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Net;
3 | using CityWebServer.Extensibility;
4 | using CityWebServer.Extensibility.Responses;
5 | using ColossalFramework;
6 |
7 | namespace CityWebServer.RequestHandlers
8 | {
9 | public class BudgetRequestHandler : RequestHandlerBase
10 | {
11 | public BudgetRequestHandler(IWebServer server)
12 | : base(server, new Guid("87205a0d-1b53-47bd-91fa-9cddf0a3bd9e"), "Budget", "Rychard", 100, "/Budget")
13 | {
14 | }
15 |
16 | public override IResponseFormatter Handle(HttpListenerRequest request)
17 | {
18 | // TODO: Expand upon this to expose substantially more information.
19 | var economyManager = Singleton.instance;
20 | long income;
21 | long expenses;
22 | economyManager.GetIncomeAndExpenses(new ItemClass(), out income, out expenses);
23 |
24 | Decimal formattedIncome = Math.Round(((Decimal)income / 100), 2);
25 | Decimal formattedExpenses = Math.Round(((Decimal)expenses / 100), 2);
26 |
27 | var content = String.Format("Income: {0:C}{2}Expenses: {1:C}", formattedIncome, formattedExpenses, Environment.NewLine);
28 |
29 | return new PlainTextResponseFormatter(content, HttpStatusCode.OK);
30 | }
31 | }
32 | }
--------------------------------------------------------------------------------
/SampleWebServerExtension/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.InteropServices;
3 |
4 | // General Information about an assembly is controlled through the following
5 | // set of attributes. Change these attribute values to modify the information
6 | // associated with an assembly.
7 | [assembly: AssemblyTitle("SampleWebServerExtension")]
8 | [assembly: AssemblyDescription("")]
9 | [assembly: AssemblyConfiguration("")]
10 | [assembly: AssemblyCompany("")]
11 | [assembly: AssemblyProduct("SampleWebServerExtension")]
12 | [assembly: AssemblyCopyright("Copyright © 2015")]
13 | [assembly: AssemblyTrademark("")]
14 | [assembly: AssemblyCulture("")]
15 |
16 | // Setting ComVisible to false makes the types in this assembly not visible
17 | // to COM components. If you need to access a type in this assembly from
18 | // COM, set the ComVisible attribute to true on that type.
19 | [assembly: ComVisible(false)]
20 |
21 | // The following GUID is for the ID of the typelib if this project is exposed to COM
22 | [assembly: Guid("4f860aa2-80ae-427e-b9ea-b31b40d67d8a")]
23 |
24 | // Version information for an assembly consists of the following four values:
25 | //
26 | // Major Version
27 | // Minor Version
28 | // Build Number
29 | // Revision
30 | //
31 | // You can specify all the values or you can default the Build and Revision Numbers
32 | // by using the '*' as shown below:
33 | // [assembly: AssemblyVersion("1.0.*")]
34 | [assembly: AssemblyVersion("1.0.*")]
--------------------------------------------------------------------------------
/CityWebServer/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.InteropServices;
3 |
4 | // General Information about an assembly is controlled through the following
5 | // set of attributes. Change these attribute values to modify the information
6 | // associated with an assembly.
7 | [assembly: AssemblyTitle("CityWebServer")]
8 | [assembly: AssemblyDescription("")]
9 | [assembly: AssemblyConfiguration("")]
10 | [assembly: AssemblyCompany("")]
11 | [assembly: AssemblyProduct("CityWebServer")]
12 | [assembly: AssemblyCopyright("Copyright © 2015")]
13 | [assembly: AssemblyTrademark("")]
14 | [assembly: AssemblyCulture("")]
15 |
16 | // Setting ComVisible to false makes the types in this assembly not visible
17 | // to COM components. If you need to access a type in this assembly from
18 | // COM, set the ComVisible attribute to true on that type.
19 | [assembly: ComVisible(false)]
20 |
21 | // The following GUID is for the ID of the typelib if this project is exposed to COM
22 | [assembly: Guid("51b22de1-b43e-402a-988e-a23c96f8052a")]
23 |
24 | // Version information for an assembly consists of the following four values:
25 | //
26 | // Major Version
27 | // Minor Version
28 | // Build Number
29 | // Revision
30 | //
31 | // You can specify all the values or you can default the Build and Revision Numbers
32 | // by using the '*' as shown below:
33 | // [assembly: AssemblyVersion("1.0.*")]
34 | [assembly: AssemblyVersion("1.0.*")]
35 |
36 | //[assembly: AssemblyFileVersion("1.0.0.0")]
--------------------------------------------------------------------------------
/CityWebServer.Extensibility/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.CompilerServices;
3 | using System.Runtime.InteropServices;
4 |
5 | // General Information about an assembly is controlled through the following
6 | // set of attributes. Change these attribute values to modify the information
7 | // associated with an assembly.
8 | [assembly: AssemblyTitle("CityWebServer.Extensibility")]
9 | [assembly: AssemblyDescription("")]
10 | [assembly: AssemblyConfiguration("")]
11 | [assembly: AssemblyCompany("")]
12 | [assembly: AssemblyProduct("CityWebServer.Extensibility")]
13 | [assembly: AssemblyCopyright("Copyright © 2015")]
14 | [assembly: AssemblyTrademark("")]
15 | [assembly: AssemblyCulture("")]
16 |
17 | // Setting ComVisible to false makes the types in this assembly not visible
18 | // to COM components. If you need to access a type in this assembly from
19 | // COM, set the ComVisible attribute to true on that type.
20 | [assembly: ComVisible(false)]
21 |
22 | // The following GUID is for the ID of the typelib if this project is exposed to COM
23 | [assembly: Guid("8161110e-476e-493b-bdcf-fce98ee77f8a")]
24 |
25 | // Version information for an assembly consists of the following four values:
26 | //
27 | // Major Version
28 | // Minor Version
29 | // Build Number
30 | // Revision
31 | //
32 | // You can specify all the values or you can default the Build and Revision Numbers
33 | // by using the '*' as shown below:
34 | // [assembly: AssemblyVersion("1.0.*")]
35 | [assembly: AssemblyVersion("1.0.*")]
36 | [assembly: InternalsVisibleTo("CityWebServer")]
--------------------------------------------------------------------------------
/CityWebServer/RequestHandlers/BuildingRequestHandler.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Net;
4 | using CityWebServer.Extensibility;
5 | using ColossalFramework;
6 |
7 | namespace CityWebServer.RequestHandlers
8 | {
9 | public class BuildingRequestHandler : RequestHandlerBase
10 | {
11 | public BuildingRequestHandler(IWebServer server)
12 | : base(server, new Guid("03897cb0-d53f-4189-a613-e7d22705dc2f"), "Building", "Rychard", 100, "/Building")
13 | {
14 | }
15 |
16 | public override IResponseFormatter Handle(HttpListenerRequest request)
17 | {
18 | var buildingManager = Singleton.instance;
19 |
20 | if (request.Url.AbsolutePath.StartsWith("/Building/List"))
21 | {
22 | List buildingIDs = new List();
23 |
24 | var len = buildingManager.m_buildings.m_buffer.Length;
25 | for (ushort i = 0; i < len; i++)
26 | {
27 | if (buildingManager.m_buildings.m_buffer[i].m_flags == Building.Flags.None) { continue; }
28 |
29 | buildingIDs.Add(i);
30 | }
31 |
32 | return JsonResponse(buildingIDs);
33 | }
34 |
35 | foreach (var building in buildingManager.m_buildings.m_buffer)
36 | {
37 | if (building.m_flags == Building.Flags.None) { continue; }
38 |
39 | // TODO: Something with Buildings.
40 | }
41 |
42 | return JsonResponse("");
43 | }
44 | }
45 | }
--------------------------------------------------------------------------------
/CityWebServer/Models/Economy.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace CityWebServer.Models
4 | {
5 | public class Economy
6 | {
7 | public IncomeExpense[] IncomesAndExpenses { get; set; }
8 |
9 | public TaxRate[] TaxRates { get; set; }
10 | }
11 |
12 | public class TaxRate
13 | {
14 | public String GroupName { get; set; }
15 |
16 | public int Rate { get; set; }
17 |
18 | // Tax Rate: Low-Density Residential
19 | // Tax Rate: High-Density Residential
20 | // Tax Rate: Low-Density Commercial
21 | // Tax Rate: High-Density Commercial
22 | // Tax Rate: Industry
23 | // Tax Rate: Offices
24 | }
25 |
26 | public class IncomeExpense
27 | {
28 | public String Group { get; set; }
29 |
30 | public String SubGroup { get; set; }
31 |
32 | public Double Amount { get; set; }
33 |
34 | // Tax Income: Low-Density Residential
35 | // Tax Income: High-Density Residential
36 | // Tax Income: Low-Density Commercial
37 | // Tax Income: High-Density Commercial
38 | // Tax Income: Industry
39 | // Tax Income: Offices
40 |
41 | // Income: Citizens
42 | // Income: Tourists
43 |
44 | // Income: Bus/Train/Metro?
45 |
46 | // Upkeep Expense: Roads
47 | // Upkeep Expense: Electricity
48 | // Upkeep Expense: Water
49 | // Upkeep Expense: Garbage
50 | // Upkeep Expense: Unique Buildings
51 | // Upkeep Expense: Healthcare
52 | // Upkeep Expense: Education
53 | // Upkeep Expense: Police
54 | // Upkeep Expense: Firefighters
55 | // Upkeep Expense: Parks
56 | // Upkeep Expense: Bus/Train/Metro?
57 | // Upkeep Expense: Taxes???
58 | // Upkeep Expense: Policy
59 | }
60 | }
--------------------------------------------------------------------------------
/CityWebServer.Extensibility/IRequestHandler.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Net;
3 |
4 | namespace CityWebServer.Extensibility
5 | {
6 | ///
7 | /// Represents a handler for servicing requests received by the web server.
8 | ///
9 | public interface IRequestHandler
10 | {
11 | IWebServer Server { get; }
12 |
13 | ///
14 | /// Gets a unique identifier for this handler. Only one handler can be loaded with a given identifier.
15 | ///
16 | Guid HandlerID { get; }
17 |
18 | ///
19 | /// Gets the priority of this request handler. A request will be handled by the request handler with the lowest priority.
20 | ///
21 | int Priority { get; }
22 |
23 | ///
24 | /// Gets the display name of this request handler.
25 | ///
26 | String Name { get; }
27 |
28 | ///
29 | /// Gets the author of this request handler.
30 | ///
31 | String Author { get; }
32 |
33 | ///
34 | /// Gets the absolute path to the main page for this request handler. Your class is responsible for handling requests at this path.
35 | ///
36 | ///
37 | /// When set to a value other than null, the Web Server will show this url as a link on the home page.
38 | ///
39 | String MainPath { get; }
40 |
41 | ///
42 | /// Returns a value that indicates whether this handler is capable of servicing the given request.
43 | ///
44 | Boolean ShouldHandle(HttpListenerRequest request);
45 |
46 | ///
47 | /// Handles the specified request. The method should not close the stream.
48 | ///
49 | IResponseFormatter Handle(HttpListenerRequest request);
50 | }
51 | }
--------------------------------------------------------------------------------
/CityWebServer.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio 2013
4 | VisualStudioVersion = 12.0.31101.0
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CityWebServer", "CityWebServer\CityWebServer.csproj", "{61A80D3C-8A1E-44DA-A044-A16F1ABBA149}"
7 | EndProject
8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CityWebServer.Extensibility", "CityWebServer.Extensibility\CityWebServer.Extensibility.csproj", "{DB96EFB4-FA45-4ACC-8D51-7ED37065CC79}"
9 | EndProject
10 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SampleWebServerExtension", "SampleWebServerExtension\SampleWebServerExtension.csproj", "{7DF15DF6-C475-4866-9111-F5150C1336E1}"
11 | EndProject
12 | Global
13 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
14 | Debug|Any CPU = Debug|Any CPU
15 | Release|Any CPU = Release|Any CPU
16 | EndGlobalSection
17 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
18 | {61A80D3C-8A1E-44DA-A044-A16F1ABBA149}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
19 | {61A80D3C-8A1E-44DA-A044-A16F1ABBA149}.Debug|Any CPU.Build.0 = Debug|Any CPU
20 | {61A80D3C-8A1E-44DA-A044-A16F1ABBA149}.Release|Any CPU.ActiveCfg = Release|Any CPU
21 | {61A80D3C-8A1E-44DA-A044-A16F1ABBA149}.Release|Any CPU.Build.0 = Release|Any CPU
22 | {DB96EFB4-FA45-4ACC-8D51-7ED37065CC79}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
23 | {DB96EFB4-FA45-4ACC-8D51-7ED37065CC79}.Debug|Any CPU.Build.0 = Debug|Any CPU
24 | {DB96EFB4-FA45-4ACC-8D51-7ED37065CC79}.Release|Any CPU.ActiveCfg = Release|Any CPU
25 | {DB96EFB4-FA45-4ACC-8D51-7ED37065CC79}.Release|Any CPU.Build.0 = Release|Any CPU
26 | {7DF15DF6-C475-4866-9111-F5150C1336E1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
27 | {7DF15DF6-C475-4866-9111-F5150C1336E1}.Debug|Any CPU.Build.0 = Debug|Any CPU
28 | {7DF15DF6-C475-4866-9111-F5150C1336E1}.Release|Any CPU.ActiveCfg = Release|Any CPU
29 | {7DF15DF6-C475-4866-9111-F5150C1336E1}.Release|Any CPU.Build.0 = Release|Any CPU
30 | EndGlobalSection
31 | GlobalSection(SolutionProperties) = preSolution
32 | HideSolutionNode = FALSE
33 | EndGlobalSection
34 | EndGlobal
35 |
--------------------------------------------------------------------------------
/CityWebServer/Helpers/DistrictExtensions.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using CityWebServer.Models;
3 |
4 | namespace CityWebServer.Helpers
5 | {
6 | public static class DistrictExtensions
7 | {
8 | public static Boolean IsValid(this District district)
9 | {
10 | return (district.m_flags != District.Flags.None);
11 | }
12 |
13 | public static Boolean IsAlive(this District district)
14 | {
15 | // Get the flags on the district, to ensure we don't access garbage memory if it doesn't have a flag for District.Flags.Created
16 | Boolean alive = ((district.m_flags & District.Flags.Created) == District.Flags.Created);
17 | return alive;
18 | }
19 |
20 | public static PopulationGroup[] GetPopulation(this District district)
21 | {
22 | PopulationGroup[] ageGroups =
23 | {
24 | new PopulationGroup("Children", district.GetChildrenCount()),
25 | new PopulationGroup("Teen", district.GetTeenCount()),
26 | new PopulationGroup("YoungAdult", district.GetYoungAdultCount()),
27 | new PopulationGroup("Adult", district.GetAdultCount()),
28 | new PopulationGroup("Senior", district.GetSeniorCount())
29 | };
30 | return ageGroups;
31 | }
32 |
33 | public static int GetChildrenCount(this District district)
34 | {
35 | return (int)district.m_childData.m_finalCount;
36 | }
37 |
38 | public static int GetTeenCount(this District district)
39 | {
40 | return (int)district.m_teenData.m_finalCount;
41 | }
42 |
43 | public static int GetYoungAdultCount(this District district)
44 | {
45 | return (int)district.m_youngData.m_finalCount;
46 | }
47 |
48 | public static int GetAdultCount(this District district)
49 | {
50 | return (int)district.m_adultData.m_finalCount;
51 | }
52 |
53 | public static int GetSeniorCount(this District district)
54 | {
55 | return (int)district.m_seniorData.m_finalCount;
56 | }
57 | }
58 | }
--------------------------------------------------------------------------------
/CityWebServer/RequestHandlers/VehicleRequestHandler.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Net;
5 | using CityWebServer.Extensibility;
6 | using ColossalFramework;
7 | using JetBrains.Annotations;
8 |
9 | namespace CityWebServer.RequestHandlers
10 | {
11 | [UsedImplicitly]
12 | public class VehicleRequestHandler : RequestHandlerBase
13 | {
14 | public VehicleRequestHandler(IWebServer server)
15 | : base(server, new Guid("2be6546a-d416-4939-8e08-1d0b739be835"), "Vehicle", "Rychard", 100, "/Vehicle")
16 | {
17 | }
18 |
19 | public override IResponseFormatter Handle(HttpListenerRequest request)
20 | {
21 | var vehicleManager = Singleton.instance;
22 |
23 | if (request.Url.AbsolutePath.StartsWith("/Vehicle/List"))
24 | {
25 | List vehicleIds = new List();
26 |
27 | var len = vehicleManager.m_vehicles.m_buffer.Length;
28 | for (ushort i = 0; i < len; i++)
29 | {
30 | if (vehicleManager.m_vehicles.m_buffer[i].m_flags == Vehicle.Flags.None) { continue; }
31 |
32 | vehicleIds.Add(i);
33 | }
34 |
35 | return JsonResponse(vehicleIds);
36 | }
37 |
38 | List s = new List();
39 |
40 | foreach (var vehicle in vehicleManager.m_vehicles.m_buffer)
41 | {
42 | if (vehicle.m_flags == Vehicle.Flags.None) { continue; }
43 |
44 | if ((vehicle.m_flags & Vehicle.Flags.Spawned) == Vehicle.Flags.Spawned && (vehicle.m_flags & Vehicle.Flags.Created) == Vehicle.Flags.Created)
45 | {
46 | var origin = (vehicle.m_sourceBuilding);
47 | var target = (vehicle.m_targetBuilding);
48 |
49 | if (origin > 0) { s.Add(origin); }
50 | if (target > 0) { s.Add(target); }
51 | }
52 | }
53 |
54 | var grouped = s.GroupBy(obj => obj).Select(group => new { BuildingID = group.Key, Count = group.Count() }).OrderByDescending(obj => obj.Count).Select(obj => new { Building = BuildingManager.instance.GetBuildingName(obj.BuildingID, new InstanceID()), obj.Count }).ToList();
55 |
56 | return JsonResponse(grouped);
57 | }
58 | }
59 | }
--------------------------------------------------------------------------------
/CityWebServer/Retrievers/ChirpRetriever.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using CityWebServer.Extensibility;
5 | using CityWebServer.Models;
6 | using ColossalFramework;
7 | using ICities;
8 |
9 | namespace CityWebServer.Retrievers
10 | {
11 | public class ChirpRetriever : ILogAppender
12 | {
13 | public event EventHandler LogMessage;
14 |
15 | private void OnLogMessage(String message)
16 | {
17 | var handler = LogMessage;
18 | if (handler != null)
19 | {
20 | handler(this, new LogAppenderEventArgs(message));
21 | }
22 | }
23 |
24 | private readonly MessageManager _manager;
25 | private List _messages;
26 |
27 | public ChirperMessage[] Messages
28 | {
29 | get { return _messages.ToArray(); }
30 | set { _messages = value.ToList(); }
31 | }
32 |
33 | public ChirpRetriever()
34 | {
35 | _manager = Singleton.instance;
36 | _manager.m_messagesUpdated += ManagerOnMMessagesUpdated;
37 | _manager.m_newMessages += ManagerOnMNewMessages;
38 | _messages = new List();
39 | }
40 |
41 | private void ManagerOnMNewMessages(IChirperMessage message)
42 | {
43 | try
44 | {
45 | var msg = new ChirperMessage
46 | {
47 | SenderID = (int)message.senderID,
48 | SenderName = message.senderName,
49 | Text = message.text
50 | };
51 | _messages.Add(msg);
52 | }
53 | catch (Exception ex)
54 | {
55 | OnLogMessage(ex.ToString());
56 | }
57 | }
58 |
59 | private void ManagerOnMMessagesUpdated()
60 | {
61 | try
62 | {
63 | var messages = _manager.GetRecentMessages();
64 | _messages = messages.Select(obj => new ChirperMessage
65 | {
66 | SenderID = (int)obj.GetSenderID(),
67 | SenderName = obj.GetSenderName(),
68 | Text = obj.GetText(),
69 | }).ToList();
70 | }
71 | catch (Exception ex)
72 | {
73 | OnLogMessage(ex.ToString());
74 | }
75 | }
76 | }
77 | }
--------------------------------------------------------------------------------
/CityWebServer.Extensibility/RestfulRequestHandlerBase.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Net;
3 |
4 | namespace CityWebServer.Extensibility
5 | {
6 | public abstract class RestfulRequestHandlerBase : RequestHandlerBase
7 | {
8 | public RestfulRequestHandlerBase(IWebServer server, Guid handlerID, String name, String author, int priority, String mainPath)
9 | : base(server, handlerID, name, author, priority, mainPath)
10 | {
11 | }
12 |
13 | public override Guid HandlerID { get { return _handlerID; } }
14 |
15 | public override int Priority { get { return _priority; } }
16 |
17 | public override string Name { get { return _name; } }
18 |
19 | public override string Author { get { return _author; } }
20 |
21 | public override string MainPath { get { return _mainPath; } }
22 |
23 | public override bool ShouldHandle(HttpListenerRequest request)
24 | {
25 | return (request.Url.AbsolutePath.StartsWith(_mainPath, StringComparison.OrdinalIgnoreCase));
26 | }
27 |
28 | public override IResponseFormatter Handle(HttpListenerRequest request)
29 | {
30 | switch (request.HttpMethod)
31 | {
32 | case "GET":
33 | return HandleGetRequest(request);
34 |
35 | case "POST":
36 | return HandlePostRequest(request);
37 |
38 | case "PUT":
39 | return HandlePutRequest(request);
40 |
41 | case "DELETE":
42 | return HandleDeleteRequest(request);
43 |
44 | default:
45 | return JsonResponse("400 Bad Request", HttpStatusCode.BadRequest);
46 | }
47 | }
48 |
49 | protected virtual IResponseFormatter HandleGetRequest(HttpListenerRequest request)
50 | {
51 | return JsonResponse("405 Method Not Allowed", HttpStatusCode.MethodNotAllowed);
52 | }
53 |
54 | protected virtual IResponseFormatter HandlePostRequest(HttpListenerRequest request)
55 | {
56 | return JsonResponse("405 Method Not Allowed", HttpStatusCode.MethodNotAllowed);
57 | }
58 |
59 | protected virtual IResponseFormatter HandlePutRequest(HttpListenerRequest request)
60 | {
61 | return JsonResponse("405 Method Not Allowed", HttpStatusCode.MethodNotAllowed);
62 | }
63 |
64 | protected virtual IResponseFormatter HandleDeleteRequest(HttpListenerRequest request)
65 | {
66 | return JsonResponse("405 Method Not Allowed", HttpStatusCode.MethodNotAllowed);
67 | }
68 | }
69 | }
--------------------------------------------------------------------------------
/CityWebServer/RequestHandlers/TransportRequestHandler.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Net;
5 | using CityWebServer.Extensibility;
6 | using CityWebServer.Models;
7 | using ColossalFramework;
8 | using JetBrains.Annotations;
9 |
10 | namespace CityWebServer.RequestHandlers
11 | {
12 | [UsedImplicitly]
13 | public class TransportRequestHandler : RequestHandlerBase
14 | {
15 | public TransportRequestHandler(IWebServer server)
16 | : base(server, new Guid("89c8ef27-fc8c-4fe8-9793-1f6432feb179"), "Transport", "Rychard", 100, "/Transport")
17 | {
18 | }
19 |
20 | public override IResponseFormatter Handle(HttpListenerRequest request)
21 | {
22 | var transportManager = Singleton.instance;
23 |
24 | var lines = transportManager.m_lines.m_buffer;
25 | List lineModels = new List();
26 |
27 | foreach (var line in lines)
28 | {
29 | if (line.m_flags == TransportLine.Flags.None) { continue; }
30 |
31 | var passengers = line.m_passengers;
32 | List passengerGroups = new List
33 | {
34 | new PopulationGroup("Child", (int) passengers.m_childPassengers.m_finalCount),
35 | new PopulationGroup("Teen", (int) passengers.m_teenPassengers.m_finalCount),
36 | new PopulationGroup("Young Adult", (int) passengers.m_youngPassengers.m_finalCount),
37 | new PopulationGroup("Adult", (int) passengers.m_adultPassengers.m_finalCount),
38 | new PopulationGroup("Senior", (int) passengers.m_seniorPassengers.m_finalCount),
39 | new PopulationGroup("Tourist", (int) passengers.m_touristPassengers.m_finalCount),
40 | new PopulationGroup("Resident", (int) passengers.m_residentPassengers.m_finalCount),
41 | new PopulationGroup("Car-Owning", (int) passengers.m_carOwningPassengers.m_finalCount)
42 | };
43 |
44 | var stops = line.CountStops(0); // The parameter is never used.
45 | var vehicles = line.CountVehicles(0); // The parameter is never used.
46 |
47 | var lineModel = new PublicTransportLine
48 | {
49 | Name = String.Format("{0} {1}", line.Info.name, (int)line.m_lineNumber),
50 | StopCount = stops,
51 | VehicleCount = vehicles,
52 | Passengers = passengerGroups.ToArray(),
53 | };
54 | lineModels.Add(lineModel);
55 | }
56 |
57 | lineModels = lineModels.OrderBy(obj => obj.Name).ToList();
58 |
59 | return JsonResponse(lineModels);
60 | }
61 | }
62 | }
--------------------------------------------------------------------------------
/CityWebServer/WebServer.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Net;
3 | using System.Threading;
4 |
5 | namespace CityWebServer
6 | {
7 | public class WebServer
8 | {
9 | private readonly HttpListener _listener = new HttpListener();
10 | private readonly Action _responderMethod;
11 |
12 | public WebServer(String[] prefixes, Action method)
13 | {
14 | if (!HttpListener.IsSupported) { throw new NotSupportedException("This wouldn't happen if you upgraded your operating system more than once a decade."); }
15 |
16 | // URI prefixes are required, for example:
17 | // "http://localhost:8080/index/".
18 | if (prefixes == null || prefixes.Length == 0) { throw new ArgumentException("prefixes"); }
19 |
20 | // A responder method is required
21 | if (method == null) { throw new ArgumentException("method"); }
22 |
23 | foreach (String s in prefixes)
24 | {
25 | _listener.Prefixes.Add(s);
26 | }
27 |
28 | _responderMethod = method;
29 | _listener.Start();
30 | }
31 |
32 | public WebServer(Action method, params String[] prefixes)
33 | : this(prefixes, method)
34 | {
35 | }
36 |
37 | public void Run()
38 | {
39 | ThreadPool.QueueUserWorkItem(o =>
40 | {
41 | try
42 | {
43 | while (_listener.IsListening)
44 | {
45 | ThreadPool.QueueUserWorkItem(RequestHandlerCallback, _listener.GetContext());
46 | }
47 | }
48 | catch { } // Suppress exceptions.
49 | });
50 | }
51 |
52 | private void RequestHandlerCallback(Object context)
53 | {
54 | var ctx = context as HttpListenerContext;
55 | try
56 | {
57 | if (ctx != null)
58 | {
59 | var request = ctx.Request;
60 | var response = ctx.Response;
61 |
62 | // Allow accessing pages from pages hosted from another local web-server, such as IIS, for instance.
63 | response.AddHeader("Access-Control-Allow-Origin", "http://localhost");
64 |
65 | _responderMethod(request, response);
66 | }
67 | }
68 | catch { } // Suppress any exceptions.
69 | finally
70 | {
71 | if (ctx != null)
72 | {
73 | // Ensure that the stream is never left open.
74 | ctx.Response.OutputStream.Close();
75 | }
76 | }
77 | }
78 |
79 | public void Stop()
80 | {
81 | _listener.Stop();
82 | _listener.Close();
83 | }
84 | }
85 | }
--------------------------------------------------------------------------------
/CityWebServer/wwwroot/script.js:
--------------------------------------------------------------------------------
1 | function initializeChart() {
2 | var c = new Highcharts.Chart({
3 | chart: {
4 | renderTo: 'chart',
5 | defaultSeriesType: 'spline',
6 | events: { }
7 | },
8 | title: {
9 | text: 'Statistics'
10 | },
11 | xAxis: {
12 | type: 'datetime',
13 | tickPixelInterval: 150
14 | },
15 | yAxis: {
16 | minPadding: 0.2,
17 | maxPadding: 0.2,
18 | title: {
19 | text: 'Value',
20 | margin: 80
21 | }
22 | }
23 | });
24 | return c;
25 | }
26 |
27 | function deleteUnusedSeries(chart, seriesArray)
28 | {
29 | // seriesArray is an array that contains only the NAMES of the series that were updated this tick.
30 |
31 | //for (var j = 0; j < chart.series.length; j++) {
32 | // var series = chart.series[j];
33 | // var isUsed = false;
34 | // for (var i = 0; i < seriesArray.length; i++) {
35 | // var usedSeriesName = seriesArray[i];
36 | // if (series.name == usedSeriesName) {
37 | // isUsed = true;
38 | // }
39 | // }
40 |
41 | // if (!isUsed) {
42 | // //console.log("Removing: " + chart.series[j].name);
43 | // chart.series[j].remove();
44 | // j = -1;
45 | // }
46 |
47 | //}
48 | }
49 |
50 | function addOrUpdateSeries(theChart, seriesName, value, valueName)
51 | {
52 | var series;
53 | var matchFound = false;
54 | if(theChart.series.length > 0)
55 | {
56 | for(var s = 0; s < theChart.series.length; s++)
57 | {
58 | if(theChart.series[s].name == seriesName)
59 | {
60 | series = theChart.series[s];
61 | matchFound = true;
62 | s = theChart.series.length; // Stop looping
63 | }
64 | }
65 | }
66 |
67 | if(!matchFound)
68 | {
69 | //console.log("Adding series: " + seriesName);
70 | var seriesOptions = {
71 | id: seriesName,
72 | name: seriesName,
73 | data: [{ name: valueName, y: value}]
74 | };
75 | series = theChart.addSeries(seriesOptions, false);
76 | }
77 | else
78 | {
79 | var shift = series.data.length > 20;
80 | series.addPoint(value, true, shift);
81 | }
82 | }
83 |
84 | function updateChart(vm, chart)
85 | {
86 | var updatedSeries = [];
87 | var districts = vm.Districts();
88 | for(var i = 0; i < districts.length; i++)
89 | {
90 | var district = districts[i];
91 | var districtName = district.DistrictName();
92 |
93 | var seriesName = districtName + " - Population";
94 | var population = district.TotalPopulationCount();
95 | addOrUpdateSeries(chart, seriesName, population, vm.Time());
96 | updatedSeries.push(seriesName);
97 | deleteUnusedSeries(chart, updatedSeries);
98 | }
99 | chart.redraw();
100 | }
--------------------------------------------------------------------------------
/CityWebServer.Extensibility/CityWebServer.Extensibility.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | AnyCPU
7 | {DB96EFB4-FA45-4ACC-8D51-7ED37065CC79}
8 | Library
9 | Properties
10 | CityWebServer.Extensibility
11 | CityWebServer.Extensibility
12 | v3.5
13 | 512
14 |
15 |
16 | true
17 | full
18 | false
19 | bin\Debug\
20 | DEBUG;TRACE
21 | prompt
22 | 4
23 |
24 |
25 | pdbonly
26 | true
27 | bin\Release\
28 | TRACE
29 | prompt
30 | 4
31 |
32 |
33 |
34 | ..\packages\JsonFx.2.0.1209.2802\lib\net35\JsonFx.dll
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
67 |
--------------------------------------------------------------------------------
/SampleWebServerExtension/SampleWebServerExtension.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | AnyCPU
7 | {7DF15DF6-C475-4866-9111-F5150C1336E1}
8 | Library
9 | Properties
10 | SampleWebServerExtension
11 | SampleWebServerExtension
12 | v3.5
13 | 512
14 |
15 |
16 | true
17 | full
18 | false
19 | bin\Debug\
20 | DEBUG;TRACE
21 | prompt
22 | 4
23 |
24 |
25 | pdbonly
26 | true
27 | bin\Release\
28 | TRACE
29 | prompt
30 | 4
31 |
32 |
33 |
34 | ..\Assemblies\ICities.dll
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 | {db96efb4-fa45-4acc-8d51-7ed37065cc79}
51 | CityWebServer.Extensibility
52 |
53 |
54 |
55 |
56 | mkdir "%25LOCALAPPDATA%25\Colossal Order\Cities_Skylines\Addons\Mods\$(SolutionName)_$(ProjectName)"
57 | del "%25LOCALAPPDATA%25\Colossal Order\Cities_Skylines\Addons\Mods\$(SolutionName)_$(ProjectName)\$(TargetFileName)"
58 | xcopy /Y "$(TargetPath)" "%25LOCALAPPDATA%25\Colossal Order\Cities_Skylines\Addons\Mods\$(SolutionName)_$(ProjectName)"
59 | xcopy /Y "$(TargetDir)CityWebServer.Extensibility.dll" "%25LOCALAPPDATA%25\Colossal Order\Cities_Skylines\Addons\Mods\$(SolutionName)_$(ProjectName)\*.*"
60 |
61 |
68 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | ## Ignore Visual Studio temporary files, build results, and
2 | ## files generated by popular Visual Studio add-ons.
3 |
4 | # User-specific files
5 | *.suo
6 | *.user
7 | *.sln.docstates
8 |
9 | # Build results
10 | [Dd]ebug/
11 | [Dd]ebugPublic/
12 | [Rr]elease/
13 | [Rr]eleases/
14 | x64/
15 | x86/
16 | build/
17 | bld/
18 | [Bb]in/
19 | [Oo]bj/
20 |
21 | # Roslyn cache directories
22 | *.ide/
23 |
24 | # MSTest test Results
25 | [Tt]est[Rr]esult*/
26 | [Bb]uild[Ll]og.*
27 |
28 | #NUNIT
29 | *.VisualState.xml
30 | TestResult.xml
31 |
32 | # Build Results of an ATL Project
33 | [Dd]ebugPS/
34 | [Rr]eleasePS/
35 | dlldata.c
36 |
37 | *_i.c
38 | *_p.c
39 | *_i.h
40 | *.ilk
41 | *.meta
42 | *.obj
43 | *.pch
44 | *.pdb
45 | *.pgc
46 | *.pgd
47 | *.rsp
48 | *.sbr
49 | *.tlb
50 | *.tli
51 | *.tlh
52 | *.tmp
53 | *.tmp_proj
54 | *.log
55 | *.vspscc
56 | *.vssscc
57 | .builds
58 | *.pidb
59 | *.svclog
60 | *.scc
61 |
62 | # Chutzpah Test files
63 | _Chutzpah*
64 |
65 | # Visual C++ cache files
66 | ipch/
67 | *.aps
68 | *.ncb
69 | *.opensdf
70 | *.sdf
71 | *.cachefile
72 |
73 | # Visual Studio profiler
74 | *.psess
75 | *.vsp
76 | *.vspx
77 |
78 | # TFS 2012 Local Workspace
79 | $tf/
80 |
81 | # Guidance Automation Toolkit
82 | *.gpState
83 |
84 | # ReSharper is a .NET coding add-in
85 | _ReSharper*/
86 | *.[Rr]e[Ss]harper
87 | *.DotSettings.user
88 |
89 | # JustCode is a .NET coding addin-in
90 | .JustCode
91 |
92 | # TeamCity is a build add-in
93 | _TeamCity*
94 |
95 | # DotCover is a Code Coverage Tool
96 | *.dotCover
97 |
98 | # NCrunch
99 | _NCrunch_*
100 | .*crunch*.local.xml
101 |
102 | # MightyMoose
103 | *.mm.*
104 | AutoTest.Net/
105 |
106 | # Web workbench (sass)
107 | .sass-cache/
108 |
109 | # Installshield output folder
110 | [Ee]xpress/
111 |
112 | # DocProject is a documentation generator add-in
113 | DocProject/buildhelp/
114 | DocProject/Help/*.HxT
115 | DocProject/Help/*.HxC
116 | DocProject/Help/*.hhc
117 | DocProject/Help/*.hhk
118 | DocProject/Help/*.hhp
119 | DocProject/Help/Html2
120 | DocProject/Help/html
121 |
122 | # Click-Once directory
123 | publish/
124 |
125 | # Publish Web Output
126 | *.[Pp]ublish.xml
127 | *.azurePubxml
128 | # TODO: Comment the next line if you want to checkin your web deploy settings
129 | # but database connection strings (with potential passwords) will be unencrypted
130 | *.pubxml
131 | *.publishproj
132 |
133 | # NuGet Packages
134 | *.nupkg
135 | # The packages folder can be ignored because of Package Restore
136 | **/packages/*
137 | # except build/, which is used as an MSBuild target.
138 | !**/packages/build/
139 | # If using the old MSBuild-Integrated Package Restore, uncomment this:
140 | #!**/packages/repositories.config
141 |
142 | # Windows Azure Build Output
143 | csx/
144 | *.build.csdef
145 |
146 | # Windows Store app package directory
147 | AppPackages/
148 |
149 | # Others
150 | sql/
151 | *.Cache
152 | ClientBin/
153 | [Ss]tyle[Cc]op.*
154 | ~$*
155 | *~
156 | *.dbmdl
157 | *.dbproj.schemaview
158 | *.pfx
159 | *.publishsettings
160 | node_modules/
161 |
162 | # RIA/Silverlight projects
163 | Generated_Code/
164 |
165 | # Backup & report files from converting an old project file
166 | # to a newer Visual Studio version. Backup files are not needed,
167 | # because we have git ;-)
168 | _UpgradeReport_Files/
169 | Backup*/
170 | UpgradeLog*.XML
171 | UpgradeLog*.htm
172 |
173 | # SQL Server files
174 | *.mdf
175 | *.ldf
176 |
177 | # Business Intelligence projects
178 | *.rdl.data
179 | *.bim.layout
180 | *.bim_*.settings
181 |
182 | # Microsoft Fakes
183 | FakesAssemblies/
184 |
185 | # Cities: Skylines Assemblies
186 | Assembly-CSharp.dll
187 | ColossalManaged.dll
188 | ICities.dll
189 | UnityEngine.dll
190 | UnityEngine.UI.dll
191 |
--------------------------------------------------------------------------------
/CityWebServer/Helpers/TemplateHelper.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.IO;
4 | using System.Linq;
5 | using CityWebServer.Extensibility;
6 | using ColossalFramework.Plugins;
7 |
8 | namespace CityWebServer.Helpers
9 | {
10 | public static class TemplateHelper
11 | {
12 | ///
13 | /// Gets the full path of the directory that contains this assembly.
14 | ///
15 | public static String GetModPath()
16 | {
17 | var modPaths = PluginManager.instance.GetPluginsInfo().Select(obj => obj.modPath);
18 |
19 | foreach (var path in modPaths)
20 | {
21 | var indexPath = Path.Combine(path, "index.html");
22 | if (File.Exists(indexPath))
23 | {
24 | return indexPath;
25 | }
26 | }
27 | return null;
28 | }
29 |
30 | ///
31 | /// Gets the full content of a template.
32 | ///
33 | public static String GetTemplate(String template)
34 | {
35 | // Templates seem like something we shouldn't handle internally.
36 | // Perhaps we should force request handlers to implement their own templating if they so desire, and maintain a more "API" approach within the core.
37 | String modPath = GetModPath();
38 | String templatePath = Path.Combine(modPath, "wwwroot");
39 | String specifiedTemplatePath = String.Format("{0}{1}{2}.html", templatePath, Path.DirectorySeparatorChar, template);
40 |
41 | if (File.Exists(specifiedTemplatePath))
42 | {
43 | String templateContents = File.ReadAllText(specifiedTemplatePath);
44 | return templateContents;
45 | }
46 |
47 | // All templates must at least have a #PAGEBODY# token.
48 | // If we can't find the specified template, just return a string that contains only that.
49 | return "#PAGEBODY#";
50 | }
51 |
52 | ///
53 | /// Retrieves the template with the specified name, and returns the contents of the template after replacing instances of the dictionary keys from with their coorresponding values.
54 | ///
55 | /// The name of the template to populate.
56 | /// A dictionary containing key/value pairs for replacement.
57 | ///
58 | /// The value of should not include the file extension.
59 | ///
60 | public static String PopulateTemplate(String template, Dictionary tokenReplacements)
61 | {
62 | try
63 | {
64 | String templateContents = GetTemplate(template);
65 | foreach (var tokenReplacement in tokenReplacements)
66 | {
67 | templateContents = templateContents.Replace(tokenReplacement.Key, tokenReplacement.Value);
68 | }
69 | return templateContents;
70 | }
71 | catch (Exception ex)
72 | {
73 | IntegratedWebServer.LogMessage(ex.ToString());
74 | return tokenReplacements["#PAGEBODY#"];
75 | }
76 | }
77 |
78 | ///
79 | /// Gets a dictionary that contains standard replacement tokens using the specified values.
80 | ///
81 | public static Dictionary GetTokenReplacements(String cityName, String title, List handlers, String body)
82 | {
83 | var orderedHandlers = handlers.OrderBy(obj => obj.Priority).ThenBy(obj => obj.Name);
84 | var handlerLinks = orderedHandlers.Select(obj => String.Format("{1}", obj.MainPath, obj.Name)).ToArray();
85 | String nav = String.Join(Environment.NewLine, handlerLinks);
86 |
87 | return new Dictionary
88 | {
89 | { "#PAGETITLE#", title },
90 | { "#NAV#", nav},
91 | { "#CSS#", ""}, // Moved directly into the template.
92 | { "#PAGEBODY#", body},
93 | { "#CITYNAME#", cityName},
94 | { "#JS#", ""}, // Moved directly into the template.
95 | };
96 | }
97 | }
98 | }
--------------------------------------------------------------------------------
/CityWebServer.Extensibility/RequestHandlerBase.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Net;
3 | using CityWebServer.Extensibility.Responses;
4 |
5 | namespace CityWebServer.Extensibility
6 | {
7 | public abstract class RequestHandlerBase : IRequestHandler, ILogAppender
8 | {
9 | #region ILogAppender Implementation
10 |
11 | public event EventHandler LogMessage;
12 |
13 | protected void OnLogMessage(String message)
14 | {
15 | var handler = LogMessage;
16 | if (handler != null)
17 | {
18 | handler(this, new LogAppenderEventArgs(message));
19 | }
20 | }
21 |
22 | #endregion ILogAppender Implementation
23 |
24 | protected readonly IWebServer _server;
25 | protected Guid _handlerID;
26 | protected int _priority;
27 | protected String _name;
28 | protected String _author;
29 | protected String _mainPath;
30 |
31 | private RequestHandlerBase()
32 | {
33 | }
34 |
35 | protected RequestHandlerBase(IWebServer server, Guid handlerID, String name, String author, int priority, String mainPath)
36 | {
37 | _server = server;
38 | _handlerID = handlerID;
39 | _name = name;
40 | _author = author;
41 | _priority = priority;
42 | _mainPath = mainPath;
43 | }
44 |
45 | ///
46 | /// Gets the server that is currently servicing this instance.
47 | ///
48 | public virtual IWebServer Server { get { return _server; } }
49 |
50 | ///
51 | /// Gets a unique identifier for this handler. Only one handler can be loaded with a given identifier.
52 | ///
53 | public virtual Guid HandlerID { get { return _handlerID; } }
54 |
55 | ///
56 | /// Gets the priority of this request handler. A request will be handled by the request handler with the lowest priority.
57 | ///
58 | public virtual int Priority { get { return _priority; } }
59 |
60 | ///
61 | /// Gets the display name of this request handler.
62 | ///
63 | public virtual String Name { get { return _name; } }
64 |
65 | ///
66 | /// Gets the author of this request handler.
67 | ///
68 | public virtual String Author { get { return _author; } }
69 |
70 | ///
71 | /// Gets the absolute path to the main page for this request handler. Your class is responsible for handling requests at this path.
72 | ///
73 | ///
74 | /// When set to a value other than null, the Web Server will show this url as a link on the home page.
75 | ///
76 | public virtual String MainPath { get { return _mainPath; } }
77 |
78 | ///
79 | /// Returns a value that indicates whether this handler is capable of servicing the given request.
80 | ///
81 | public virtual Boolean ShouldHandle(HttpListenerRequest request)
82 | {
83 | return (request.Url.AbsolutePath.Equals(_mainPath, StringComparison.OrdinalIgnoreCase));
84 | }
85 |
86 | ///
87 | /// Handles the specified request. The method should not close the stream.
88 | ///
89 | public abstract IResponseFormatter Handle(HttpListenerRequest request);
90 |
91 | ///
92 | /// Returns a response in JSON format.
93 | ///
94 | protected IResponseFormatter JsonResponse(T content, HttpStatusCode statusCode = HttpStatusCode.OK)
95 | {
96 | return new JsonResponseFormatter(content, statusCode);
97 | }
98 |
99 | ///
100 | /// Returns a response in HTML format.
101 | ///
102 | protected IResponseFormatter HtmlResponse(String content, HttpStatusCode statusCode = HttpStatusCode.OK)
103 | {
104 | return new HtmlResponseFormatter(content, statusCode);
105 | }
106 |
107 | ///
108 | /// Returns a response in plain text format.
109 | ///
110 | protected IResponseFormatter PlainTextResponse(String content, HttpStatusCode statusCode = HttpStatusCode.OK)
111 | {
112 | return new PlainTextResponseFormatter(content, statusCode);
113 | }
114 | }
115 | }
--------------------------------------------------------------------------------
/CityWebServer/Models/DistrictInfo.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using CityWebServer.Helpers;
5 | using ColossalFramework;
6 |
7 | namespace CityWebServer.Models
8 | {
9 | public class DistrictInfo
10 | {
11 | public int DistrictID { get; set; }
12 |
13 | public String DistrictName { get; set; }
14 |
15 | public PopulationGroup[] PopulationData { get; set; }
16 |
17 | public int TotalPopulationCount { get; set; }
18 |
19 | public int TotalBuildingCount { get; set; }
20 |
21 | public int TotalVehicleCount { get; set; }
22 |
23 | public int CurrentHouseholds { get; set; }
24 |
25 | public int AvailableHouseholds { get; set; }
26 |
27 | public int CurrentJobs { get; set; }
28 |
29 | public int AvailableJobs { get; set; }
30 |
31 | public int WeeklyTouristVisits { get; set; }
32 |
33 | public int AverageLandValue { get; set; }
34 |
35 | public Double Pollution { get; set; }
36 |
37 | public PolicyInfo[] Policies { get; set; }
38 |
39 | public static IEnumerable GetDistricts()
40 | {
41 | var districtManager = Singleton.instance;
42 |
43 | // This is the value used in Assembly-CSharp, so I presume that's the maximum number of districts allowed.
44 | const int count = 128;
45 |
46 | var districts = districtManager.m_districts.m_buffer;
47 |
48 | for (int i = 0; i < count; i++)
49 | {
50 | if (!districts[i].IsAlive()) { continue; }
51 | yield return i;
52 | }
53 | }
54 |
55 | public static DistrictInfo GetDistrictInfo(int districtID)
56 | {
57 | var districtManager = Singleton.instance;
58 | var district = GetDistrict(districtID);
59 |
60 | if (!district.IsValid()) { return null; }
61 |
62 | String districtName = String.Empty;
63 |
64 | if (districtID == 0)
65 | {
66 | // The district with ID 0 is always the global district.
67 | // It receives an auto-generated name by default, but the game always displays the city name instead.
68 | districtName = "City";
69 | }
70 | else
71 | {
72 | districtName = districtManager.GetDistrictName(districtID);
73 | }
74 |
75 | var pollution = Math.Round((district.m_groundData.m_finalPollution / (Double) byte.MaxValue), 2);
76 |
77 | var model = new DistrictInfo
78 | {
79 | DistrictID = districtID,
80 | DistrictName = districtName,
81 | TotalPopulationCount = (int)district.m_populationData.m_finalCount,
82 | PopulationData = GetPopulationGroups(districtID),
83 | CurrentHouseholds = (int)district.m_residentialData.m_finalAliveCount,
84 | AvailableHouseholds = (int)district.m_residentialData.m_finalHomeOrWorkCount,
85 | CurrentJobs = (int)district.m_commercialData.m_finalAliveCount + (int)district.m_industrialData.m_finalAliveCount + (int)district.m_officeData.m_finalAliveCount + (int)district.m_playerData.m_finalAliveCount,
86 | AvailableJobs = (int)district.m_commercialData.m_finalHomeOrWorkCount + (int)district.m_industrialData.m_finalHomeOrWorkCount + (int)district.m_officeData.m_finalHomeOrWorkCount + (int)district.m_playerData.m_finalHomeOrWorkCount,
87 | AverageLandValue = district.GetLandValue(),
88 | Pollution = pollution,
89 | WeeklyTouristVisits = (int)district.m_tourist1Data.m_averageCount + (int)district.m_tourist2Data.m_averageCount + (int)district.m_tourist3Data.m_averageCount,
90 | Policies = GetPolicies().ToArray(),
91 | };
92 | return model;
93 | }
94 |
95 | private static District GetDistrict(int? districtID = null)
96 | {
97 | if (districtID == null) { districtID = 0; }
98 | var districtManager = Singleton.instance;
99 | var district = districtManager.m_districts.m_buffer[districtID.Value];
100 | return district;
101 | }
102 |
103 | private static PopulationGroup[] GetPopulationGroups(int? districtID = null)
104 | {
105 | var district = GetDistrict(districtID);
106 | return district.GetPopulation();
107 | }
108 |
109 | private static IEnumerable GetPolicies()
110 | {
111 | var policies = EnumHelper.GetValues();
112 | var districtManager = Singleton.instance;
113 |
114 | foreach (var policy in policies)
115 | {
116 | String policyName = Enum.GetName(typeof(DistrictPolicies.Policies), policy);
117 | Boolean isEnabled = districtManager.IsCityPolicySet(policy);
118 | yield return new PolicyInfo
119 | {
120 | Name = policyName,
121 | Enabled = isEnabled
122 | };
123 | }
124 | }
125 | }
126 | }
--------------------------------------------------------------------------------
/CityWebServer/wwwroot/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | Cities: Skylines - Test
9 |
10 |
13 |
14 |
15 |
16 |
30 |
31 |
Cities: Skylines - Integrated Web Server
32 |
33 |
34 |
35 |
36 | | District ID |
37 | Name |
38 | Population |
39 | Buildings |
40 | Vehicles |
41 | Households (Max) |
42 | Jobs (Max) |
43 | Weekly Tourists |
44 | Land Value |
45 | Pollution |
46 |
47 |
48 |
49 |
50 |
51 |
52 | |
53 | |
54 | |
55 | |
56 | |
57 | () |
58 | () |
59 | |
60 | |
61 | |
62 |
63 |
64 |
65 |
66 |
67 | |
68 | Total |
69 | |
70 | |
71 | |
72 | () |
73 | () |
74 | |
75 | |
76 | |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
122 |
123 |
--------------------------------------------------------------------------------
/CityWebServer/RequestHandlers/CityInfoRequestHandler.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Net;
5 | using CityWebServer.Extensibility;
6 | using CityWebServer.Helpers;
7 | using CityWebServer.Models;
8 | using ColossalFramework;
9 | using JetBrains.Annotations;
10 |
11 | namespace CityWebServer.RequestHandlers
12 | {
13 | [UsedImplicitly]
14 | public class CityInfoRequestHandler : RequestHandlerBase
15 | {
16 | public CityInfoRequestHandler(IWebServer server)
17 | : base(server, new Guid("eeada0d0-f1d2-43b0-9595-2a6a4d917631"), "City Info", "Rychard", 100, "/CityInfo")
18 | {
19 | }
20 |
21 | public override IResponseFormatter Handle(HttpListenerRequest request)
22 | {
23 | if (request.QueryString.HasKey("showList"))
24 | {
25 | return HandleDistrictList();
26 | }
27 |
28 | return HandleDistrict(request);
29 | }
30 |
31 | private IResponseFormatter HandleDistrictList()
32 | {
33 | var districtIDs = DistrictInfo.GetDistricts().ToArray();
34 |
35 | return JsonResponse(districtIDs);
36 | }
37 |
38 | private IResponseFormatter HandleDistrict(HttpListenerRequest request)
39 | {
40 | var districtIDs = GetDistrictsFromRequest(request);
41 |
42 | DistrictInfo globalDistrictInfo = null;
43 | List districtInfoList = new List();
44 |
45 | var buildings = GetBuildingBreakdownByDistrict();
46 | var vehicles = GetVehicleBreakdownByDistrict();
47 |
48 | foreach (var districtID in districtIDs)
49 | {
50 | var districtInfo = DistrictInfo.GetDistrictInfo(districtID);
51 | if (districtID == 0)
52 | {
53 | districtInfo.TotalBuildingCount = buildings.Sum(obj => obj.Value);
54 | districtInfo.TotalVehicleCount = vehicles.Sum(obj => obj.Value);
55 | globalDistrictInfo = districtInfo;
56 | }
57 | else
58 | {
59 | districtInfo.TotalBuildingCount = buildings.Where(obj => obj.Key == districtID).Sum(obj => obj.Value);
60 | districtInfo.TotalVehicleCount = vehicles.Where(obj => obj.Key == districtID).Sum(obj => obj.Value);
61 | districtInfoList.Add(districtInfo);
62 | }
63 | }
64 |
65 | var simulationManager = Singleton.instance;
66 |
67 | var cityInfo = new CityInfo
68 | {
69 | Name = simulationManager.m_metaData.m_CityName,
70 | Time = simulationManager.m_currentGameTime.Date,
71 | GlobalDistrict = globalDistrictInfo,
72 | Districts = districtInfoList.ToArray(),
73 | };
74 |
75 | return JsonResponse(cityInfo);
76 | }
77 |
78 | private Dictionary GetBuildingBreakdownByDistrict()
79 | {
80 | var districtManager = Singleton.instance;
81 |
82 | Dictionary districtBuildings = new Dictionary();
83 | BuildingManager instance = Singleton.instance;
84 | foreach (Building building in instance.m_buildings.m_buffer)
85 | {
86 | if (building.m_flags == Building.Flags.None) { continue; }
87 | var districtID = (int)districtManager.GetDistrict(building.m_position);
88 | if (districtBuildings.ContainsKey(districtID))
89 | {
90 | districtBuildings[districtID]++;
91 | }
92 | else
93 | {
94 | districtBuildings.Add(districtID, 1);
95 | }
96 | }
97 | return districtBuildings;
98 | }
99 |
100 | private Dictionary GetVehicleBreakdownByDistrict()
101 | {
102 | var districtManager = Singleton.instance;
103 |
104 | Dictionary districtVehicles = new Dictionary();
105 | VehicleManager vehicleManager = Singleton.instance;
106 | foreach (Vehicle vehicle in vehicleManager.m_vehicles.m_buffer)
107 | {
108 | if (vehicle.m_flags != Vehicle.Flags.None)
109 | {
110 | var districtID = (int)districtManager.GetDistrict(vehicle.GetLastFramePosition());
111 | if (districtVehicles.ContainsKey(districtID))
112 | {
113 | districtVehicles[districtID]++;
114 | }
115 | else
116 | {
117 | districtVehicles.Add(districtID, 1);
118 | }
119 | }
120 | }
121 | return districtVehicles;
122 | }
123 |
124 | private IEnumerable GetDistrictsFromRequest(HttpListenerRequest request)
125 | {
126 | IEnumerable districtIDs;
127 | if (request.QueryString.HasKey("districtID"))
128 | {
129 | List districtIDList = new List();
130 | var districtID = request.QueryString.GetInteger("districtID");
131 | if (districtID.HasValue)
132 | {
133 | districtIDList.Add(districtID.Value);
134 | }
135 | districtIDs = districtIDList;
136 | }
137 | else
138 | {
139 | districtIDs = DistrictInfo.GetDistricts();
140 | }
141 | return districtIDs;
142 | }
143 | }
144 | }
--------------------------------------------------------------------------------
/CityWebServer/WebsiteButton.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Diagnostics;
3 | using CityWebServer.Helpers;
4 | using ColossalFramework.UI;
5 | using ICities;
6 | using UnityEngine;
7 |
8 | namespace CityWebServer
9 | {
10 | ///
11 | /// Adds a button to the UI for quick access to the website.
12 | ///
13 | ///
14 | /// Most/All of this code was sourced from here:
15 | /// https://github.com/AlexanderDzhoganov/Skylines-FPSCamera/blob/master/FPSCamera/Mod.cs
16 | ///
17 | public class WebsiteButton : LoadingExtensionBase
18 | {
19 | private const String keyButtonPositionX = "browserButtonPositionX";
20 | private const String keyButtonPositionY = "browserButtonPositionY";
21 |
22 | private UIDragHandle _browserButtonDragHandle;
23 | private UIButton _browserButton;
24 | private UILabel _browserButtonLabel;
25 | private Vector2 _buttonPosition;
26 | private Boolean _useSavedPosition;
27 |
28 | public override void OnLevelLoaded(LoadMode mode)
29 | {
30 | if (mode != LoadMode.LoadGame && mode != LoadMode.NewGame)
31 | {
32 | base.OnLevelLoaded(mode);
33 | return;
34 | }
35 |
36 | // Get a reference to the game's UI.
37 | var uiView = UnityEngine.Object.FindObjectOfType();
38 |
39 | var button = uiView.AddUIComponent(typeof(UIButton));
40 | _browserButton = button as UIButton;
41 |
42 | // The object should *never* be null.
43 | // We call this a "sanity check".
44 | if (_browserButton == null) { return; }
45 |
46 | // Create a drag handler and attach it to our button.
47 | _browserButtonDragHandle = button.AddUIComponent();
48 | _browserButtonDragHandle.target = _browserButton;
49 |
50 | _browserButton.width = 36;
51 | _browserButton.height = 36;
52 | _browserButton.pressedBgSprite = "OptionBasePressed";
53 | _browserButton.normalBgSprite = "OptionBase";
54 | _browserButton.hoveredBgSprite = "OptionBaseHovered";
55 | _browserButton.disabledBgSprite = "OptionBaseDisabled";
56 | _browserButton.normalFgSprite = "ToolbarIconZoomOutGlobe";
57 | _browserButton.foregroundSpriteMode = UIForegroundSpriteMode.Scale;
58 | _browserButton.scaleFactor = 1.0f;
59 | _browserButton.tooltip = "Open Browser (Hold Shift to drag)";
60 | _browserButton.tooltipBox = uiView.defaultTooltipBox;
61 |
62 | // If the user has moved the button, load their saved position data.
63 | if (Configuration.HasSetting(keyButtonPositionX) && Configuration.HasSetting(keyButtonPositionY))
64 | {
65 | var buttonPositionX = Configuration.GetFloat(keyButtonPositionX);
66 | var buttonPositionY = Configuration.GetFloat(keyButtonPositionY);
67 | _buttonPosition = new Vector2(buttonPositionX, buttonPositionY);
68 | _useSavedPosition = true;
69 | }
70 | else
71 | {
72 | _useSavedPosition = false;
73 | }
74 |
75 | // Since we're on another thread, we can pretty safely spin until our object has been created.
76 | //while (_browserButton == null) { System.Threading.Thread.Sleep(100); }
77 |
78 | if (!_useSavedPosition)
79 | {
80 | // Get a reference to the game's UI.
81 | //var uiView = UnityEngine.Object.FindObjectOfType();
82 |
83 | // The default position of the button is the middle of the screen.
84 | var buttonPositionX = (uiView.fixedWidth / 2f) + (_browserButton.width / 2f);
85 | var buttonPositionY = (uiView.fixedHeight / 2f) + (_browserButton.height / 2f);
86 | _buttonPosition = new Vector2(buttonPositionX, buttonPositionY);
87 | }
88 | _browserButton.absolutePosition = _buttonPosition;
89 |
90 | var labelObject = new GameObject();
91 | labelObject.transform.parent = uiView.transform;
92 |
93 | _browserButtonLabel = labelObject.AddComponent();
94 | _browserButtonLabel.textColor = new Color32(255, 255, 255, 255);
95 | _browserButtonLabel.transformPosition = new Vector3(1.15f, 0.90f);
96 | _browserButtonLabel.Hide();
97 |
98 | RegisterEvents();
99 |
100 | base.OnLevelLoaded(mode);
101 | }
102 |
103 | private void RegisterEvents()
104 | {
105 | // Accept button clicks.
106 | _browserButton.eventClick += OnBrowserButtonClick;
107 | _browserButton.eventMouseLeave += OnBrowserButtonMouseLeave;
108 | }
109 |
110 | private void OnBrowserButtonMouseLeave(UIComponent component, UIMouseEventParameter eventParam)
111 | {
112 | _buttonPosition = component.absolutePosition;
113 |
114 | Configuration.SetFloat(keyButtonPositionX, _buttonPosition.x);
115 | Configuration.SetFloat(keyButtonPositionY, _buttonPosition.y);
116 | Configuration.SaveSettings();
117 | }
118 |
119 | private void OnBrowserButtonClick(UIComponent component, UIMouseEventParameter args)
120 | {
121 | if (Input.GetKey(KeyCode.LeftControl))
122 | {
123 | // If the left control key is pressed, open the folder where the configuration file is located.
124 | var filePath = Configuration.GetSettingsFilePath();
125 | var directory = System.IO.Path.GetDirectoryName(filePath);
126 | if (!String.IsNullOrEmpty(directory) && System.IO.Directory.Exists(directory))
127 | {
128 | Process.Start(directory);
129 | }
130 | }
131 | else if (!Input.GetKey(KeyCode.LeftShift))
132 | {
133 | // Accept clicks only when shift isn't pressed.
134 | var endpoint = String.Format("{0}index.html", IntegratedWebServer.Endpoint);
135 | Process.Start(endpoint);
136 | }
137 | }
138 | }
139 | }
--------------------------------------------------------------------------------
/CityWebServer/CityWebServer.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | AnyCPU
7 | {61A80D3C-8A1E-44DA-A044-A16F1ABBA149}
8 | Library
9 | Properties
10 | CityWebServer
11 | CityWebServer
12 | v3.5
13 | 512
14 |
15 |
16 | true
17 | full
18 | false
19 | bin\Debug\
20 | DEBUG;TRACE
21 | prompt
22 | 4
23 |
24 |
25 | pdbonly
26 | true
27 | bin\Release\
28 | TRACE
29 | prompt
30 | 4
31 |
32 |
33 |
34 | ..\Assemblies\Assembly-CSharp.dll
35 |
36 |
37 | ..\Assemblies\ColossalManaged.dll
38 |
39 |
40 | ..\Assemblies\ICities.dll
41 |
42 |
43 | ..\packages\JsonFx.2.0.1209.2802\lib\net35\JsonFx.dll
44 |
45 |
46 |
47 |
48 | True
49 |
50 |
51 |
52 |
53 | False
54 |
55 |
56 |
57 | ..\Assemblies\UnityEngine.dll
58 |
59 |
60 | ..\Assemblies\UnityEngine.UI.dll
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 | PreserveNewest
94 |
95 |
96 | PreserveNewest
97 |
98 |
99 | PreserveNewest
100 |
101 |
102 | PreserveNewest
103 |
104 |
105 | PreserveNewest
106 |
107 |
108 | PreserveNewest
109 |
110 |
111 | PreserveNewest
112 |
113 |
114 |
115 |
116 | {db96efb4-fa45-4acc-8d51-7ed37065cc79}
117 | CityWebServer.Extensibility
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 | mkdir "%25LOCALAPPDATA%25\Colossal Order\Cities_Skylines\Addons\Mods\$(SolutionName)_$(ProjectName)"
126 | del "%25LOCALAPPDATA%25\Colossal Order\Cities_Skylines\Addons\Mods\$(SolutionName)_$(ProjectName)\$(TargetFileName)"
127 | xcopy /Y "$(TargetPath)" "%25LOCALAPPDATA%25\Colossal Order\Cities_Skylines\Addons\Mods\$(SolutionName)_$(ProjectName)"
128 | xcopy /Y "$(TargetDir)CityWebServer.Extensibility.dll" "%25LOCALAPPDATA%25\Colossal Order\Cities_Skylines\Addons\Mods\$(SolutionName)_$(ProjectName)\*.*"
129 | xcopy /Y "$(TargetDir)Jsonfx.dll" "%25LOCALAPPDATA%25\Colossal Order\Cities_Skylines\Addons\Mods\$(SolutionName)_$(ProjectName)\*.*"
130 | xcopy /Y /E "$(TargetDir)wwwroot\*.*" "%25LOCALAPPDATA%25\Colossal Order\Cities_Skylines\Addons\Mods\$(SolutionName)_$(ProjectName)\wwwroot\*.*"
131 |
132 |
139 |
--------------------------------------------------------------------------------
/CityWebServer/Helpers/ConfigurationHelper.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Globalization;
4 | using System.IO;
5 | using System.Linq;
6 | using ColossalFramework.IO;
7 | using JsonFx.Serialization;
8 |
9 | namespace CityWebServer.Helpers
10 | {
11 | public static class Configuration
12 | {
13 | private static readonly Object LockerObject = new Object();
14 | private static readonly String _filePath;
15 | private static List _settings;
16 |
17 | static Configuration()
18 | {
19 | _filePath = GetSettingsFilePath();
20 | LoadSettings();
21 | }
22 |
23 | public static String GetSettingsFilePath()
24 | {
25 | var localApplicationDataRoot = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
26 | var vendorRoot = System.IO.Path.Combine(localApplicationDataRoot, "Colossal Order");
27 | var appRoot = System.IO.Path.Combine(vendorRoot, "Cities_Skylines");
28 | var filePath = System.IO.Path.Combine(appRoot, "ModSettings.json");
29 |
30 | // This works for Windows, but not for OSX.
31 | if (CanAccess(filePath))
32 | {
33 | return filePath;
34 | }
35 |
36 | // If we just use a filename, it will exist in the root directory of the game's files. Not ideal, but it'll work.
37 | return "ModSettings.json";
38 | }
39 |
40 | private static Boolean CanAccess(String filePath)
41 | {
42 | try
43 | {
44 | using (var fileStream = System.IO.File.Open(filePath, FileMode.OpenOrCreate, FileAccess.Read))
45 | {
46 | return true;
47 | }
48 | }
49 | catch (Exception)
50 | {
51 | return false;
52 | }
53 | }
54 |
55 | private static void LoadSettings()
56 | {
57 | lock (LockerObject)
58 | {
59 | using (var fileStream = System.IO.File.Open(_filePath, FileMode.OpenOrCreate, FileAccess.Read))
60 | {
61 | using (TextReader tr = new StreamReader(fileStream))
62 | {
63 | var jsonReader = new JsonFx.Json.JsonReader();
64 | var deserialized = jsonReader.Read(tr, typeof(List));
65 | var settings = deserialized as List;
66 | _settings = settings ?? new List();
67 | }
68 | }
69 | }
70 | }
71 |
72 | public static void SaveSettings()
73 | {
74 | lock (LockerObject)
75 | {
76 | // No settings to save? Don't save anything.
77 | if (_settings == null) { return; }
78 |
79 | var dataWriterSettings = new DataWriterSettings
80 | {
81 | PrettyPrint = true,
82 | };
83 |
84 | if (System.IO.File.Exists(_filePath))
85 | {
86 | System.IO.File.Delete(_filePath);
87 | }
88 | using (var fileStream = System.IO.File.Open(_filePath, FileMode.CreateNew, FileAccess.Write))
89 | {
90 | using (TextWriter tw = new StreamWriter(fileStream))
91 | {
92 | var jsonWriter = new JsonFx.Json.JsonWriter(dataWriterSettings);
93 | jsonWriter.Write(_settings, tw);
94 | }
95 | }
96 | }
97 | }
98 |
99 | private static String GetSettingRaw(String key)
100 | {
101 | lock (LockerObject)
102 | {
103 | String raw;
104 | var matches = _settings.Where(obj => obj.Key.Equals(key, StringComparison.InvariantCultureIgnoreCase)).ToList();
105 | if (matches.Any())
106 | {
107 | raw = matches.First().Value;
108 | }
109 | else
110 | {
111 | raw = null;
112 | }
113 | return raw;
114 | }
115 | }
116 |
117 | private static void SetSettingRaw(String key, String value, String type)
118 | {
119 | lock (LockerObject)
120 | {
121 | var matches = _settings.Where(obj => obj.Key.Equals(key, StringComparison.InvariantCultureIgnoreCase)).ToList();
122 | if (matches.Any())
123 | {
124 | matches.First().Value = value;
125 | }
126 | else
127 | {
128 | _settings.Add(new Setting
129 | {
130 | Key = key,
131 | Value = value,
132 | Type = type
133 | });
134 | }
135 | }
136 | }
137 |
138 | public static Boolean HasSetting(String key)
139 | {
140 | lock (LockerObject)
141 | {
142 | if (_settings == null) { throw new Exception("Settings aren't loaded!"); }
143 | var matches = _settings.Where(obj => obj.Key.Equals(key, StringComparison.InvariantCultureIgnoreCase)).ToList();
144 | return (matches.Any());
145 | }
146 | }
147 |
148 | public static Type GetSettingType(String key)
149 | {
150 | lock (LockerObject)
151 | {
152 | var matches = _settings.Where(obj => obj.Key.Equals(key, StringComparison.InvariantCultureIgnoreCase)).ToList();
153 | if (matches.Any())
154 | {
155 | var t = matches.First().Type;
156 | switch (t)
157 | {
158 | case "string":
159 | return typeof(string);
160 |
161 | case "int":
162 | return typeof(int);
163 |
164 | case "float":
165 | return typeof(float);
166 |
167 | case "double":
168 | return typeof(double);
169 |
170 | default:
171 | return typeof(object);
172 | }
173 | }
174 | return null;
175 | }
176 | }
177 |
178 | #region String
179 |
180 | public static String GetString(String key)
181 | {
182 | var raw = GetSettingRaw(key);
183 | return raw;
184 | }
185 |
186 | public static void SetString(String key, String value)
187 | {
188 | SetSettingRaw(key, value, "string");
189 | }
190 |
191 | #endregion String
192 |
193 | #region Integer
194 |
195 | public static int GetInt(String key)
196 | {
197 | var raw = GetSettingRaw(key);
198 | int i;
199 | if (int.TryParse(raw, out i))
200 | {
201 | return i;
202 | }
203 | return default(int);
204 | }
205 |
206 | public static void SetInt(String key, int value)
207 | {
208 | SetSettingRaw(key, value.ToString(CultureInfo.InvariantCulture), "int");
209 | }
210 |
211 | #endregion Integer
212 |
213 | #region Float
214 |
215 | public static float GetFloat(String key)
216 | {
217 | var raw = GetSettingRaw(key);
218 | float f;
219 | if (float.TryParse(raw, out f))
220 | {
221 | return f;
222 | }
223 | return default(float);
224 | }
225 |
226 | public static void SetFloat(String key, float value)
227 | {
228 | SetSettingRaw(key, value.ToString(CultureInfo.InvariantCulture), "float");
229 | }
230 |
231 | #endregion Float
232 |
233 | #region Double
234 |
235 | public static double GetDouble(String key)
236 | {
237 | var raw = GetSettingRaw(key);
238 | double d;
239 | if (double.TryParse(raw, out d))
240 | {
241 | return d;
242 | }
243 | return default(double);
244 | }
245 |
246 | public static void SetDouble(String key, double value)
247 | {
248 | SetSettingRaw(key, value.ToString(CultureInfo.InvariantCulture), "double");
249 | }
250 |
251 | #endregion Double
252 | }
253 |
254 | public class Setting
255 | {
256 | public String Key { get; set; }
257 |
258 | public String Value { get; set; }
259 |
260 | public String Type { get; set; }
261 | }
262 | }
--------------------------------------------------------------------------------
/CityWebServer/wwwroot/knockout.mapping-latest.js:
--------------------------------------------------------------------------------
1 | /// Knockout Mapping plugin v2.4.1
2 | /// (c) 2013 Steven Sanderson, Roy Jacobs - http://knockoutjs.com/
3 | /// License: MIT (http://www.opensource.org/licenses/mit-license.php)
4 | (function(e){"function"===typeof require&&"object"===typeof exports&&"object"===typeof module?e(require("knockout"),exports):"function"===typeof define&&define.amd?define(["knockout","exports"],e):e(ko,ko.mapping={})})(function(e,f){function y(b,c){var a,d;for(d in c)if(c.hasOwnProperty(d)&&c[d])if(a=f.getType(b[d]),d&&b[d]&&"array"!==a&&"string"!==a)y(b[d],c[d]);else if("array"===f.getType(b[d])&&"array"===f.getType(c[d])){a=b;for(var e=d,l=b[d],n=c[d],t={},g=l.length-1;0<=g;--g)t[l[g]]=l[g];for(g=
5 | n.length-1;0<=g;--g)t[n[g]]=n[g];l=[];n=void 0;for(n in t)l.push(t[n]);a[e]=l}else b[d]=c[d]}function E(b,c){var a={};y(a,b);y(a,c);return a}function z(b,c){for(var a=E({},b),e=L.length-1;0<=e;e--){var f=L[e];a[f]&&(a[""]instanceof Object||(a[""]={}),a[""][f]=a[f],delete a[f])}c&&(a.ignore=h(c.ignore,a.ignore),a.include=h(c.include,a.include),a.copy=h(c.copy,a.copy),a.observe=h(c.observe,a.observe));a.ignore=h(a.ignore,j.ignore);a.include=h(a.include,j.include);a.copy=h(a.copy,j.copy);a.observe=h(a.observe,
6 | j.observe);a.mappedProperties=a.mappedProperties||{};a.copiedProperties=a.copiedProperties||{};return a}function h(b,c){"array"!==f.getType(b)&&(b="undefined"===f.getType(b)?[]:[b]);"array"!==f.getType(c)&&(c="undefined"===f.getType(c)?[]:[c]);return e.utils.arrayGetDistinctValues(b.concat(c))}function F(b,c,a,d,k,l,n){var t="array"===f.getType(e.utils.unwrapObservable(c));l=l||"";if(f.isMapped(b)){var g=e.utils.unwrapObservable(b)[p];a=E(g,a)}var j=n||k,h=function(){return a[d]&&a[d].create instanceof
7 | Function},x=function(b){var f=G,g=e.dependentObservable;e.dependentObservable=function(a,b,c){c=c||{};a&&"object"==typeof a&&(c=a);var d=c.deferEvaluation,M=!1;c.deferEvaluation=!0;a=new H(a,b,c);if(!d){var g=a,d=e.dependentObservable;e.dependentObservable=H;a=e.isWriteableObservable(g);e.dependentObservable=d;d=H({read:function(){M||(e.utils.arrayRemoveItem(f,g),M=!0);return g.apply(g,arguments)},write:a&&function(a){return g(a)},deferEvaluation:!0});d.__DO=g;a=d;f.push(a)}return a};e.dependentObservable.fn=
8 | H.fn;e.computed=e.dependentObservable;b=e.utils.unwrapObservable(k)instanceof Array?a[d].create({data:b||c,parent:j,skip:N}):a[d].create({data:b||c,parent:j});e.dependentObservable=g;e.computed=e.dependentObservable;return b},u=function(){return a[d]&&a[d].update instanceof Function},v=function(b,f){var g={data:f||c,parent:j,target:e.utils.unwrapObservable(b)};e.isWriteableObservable(b)&&(g.observable=b);return a[d].update(g)};if(n=I.get(c))return n;d=d||"";if(t){var t=[],s=!1,m=function(a){return a};
9 | a[d]&&a[d].key&&(m=a[d].key,s=!0);e.isObservable(b)||(b=e.observableArray([]),b.mappedRemove=function(a){var c="function"==typeof a?a:function(b){return b===m(a)};return b.remove(function(a){return c(m(a))})},b.mappedRemoveAll=function(a){var c=C(a,m);return b.remove(function(a){return-1!=e.utils.arrayIndexOf(c,m(a))})},b.mappedDestroy=function(a){var c="function"==typeof a?a:function(b){return b===m(a)};return b.destroy(function(a){return c(m(a))})},b.mappedDestroyAll=function(a){var c=C(a,m);return b.destroy(function(a){return-1!=
10 | e.utils.arrayIndexOf(c,m(a))})},b.mappedIndexOf=function(a){var c=C(b(),m);a=m(a);return e.utils.arrayIndexOf(c,a)},b.mappedGet=function(a){return b()[b.mappedIndexOf(a)]},b.mappedCreate=function(a){if(-1!==b.mappedIndexOf(a))throw Error("There already is an object with the key that you specified.");var c=h()?x(a):a;u()&&(a=v(c,a),e.isWriteableObservable(c)?c(a):c=a);b.push(c);return c});n=C(e.utils.unwrapObservable(b),m).sort();g=C(c,m);s&&g.sort();s=e.utils.compareArrays(n,g);n={};var J,A=e.utils.unwrapObservable(c),
11 | y={},z=!0,g=0;for(J=A.length;g _logLines;
25 | private static string _endpoint;
26 |
27 | private WebServer _server;
28 | private List _requestHandlers;
29 | private String _cityName = "CityName";
30 |
31 | // Not required, but prevents a number of spurious entries from making it to the log file.
32 | private static readonly List IgnoredAssemblies = new List
33 | {
34 | "Anonymously Hosted DynamicMethods Assembly",
35 | "Assembly-CSharp",
36 | "Assembly-CSharp-firstpass",
37 | "Assembly-UnityScript-firstpass",
38 | "Boo.Lang",
39 | "ColossalManaged",
40 | "ICSharpCode.SharpZipLib",
41 | "ICities",
42 | "Mono.Security",
43 | "mscorlib",
44 | "System",
45 | "System.Configuration",
46 | "System.Core",
47 | "System.Xml",
48 | "UnityEngine",
49 | "UnityEngine.UI",
50 | };
51 |
52 | ///
53 | /// Gets the root endpoint for which the server is configured to service HTTP requests.
54 | ///
55 | public static String Endpoint
56 | {
57 | get { return _endpoint; }
58 | }
59 |
60 | ///
61 | /// Gets the full path to the directory where static pages are served from.
62 | ///
63 | public static String GetWebRoot()
64 | {
65 | var modPaths = PluginManager.instance.GetPluginsInfo().Select(obj => obj.modPath);
66 | foreach (var path in modPaths)
67 | {
68 | var testPath = Path.Combine(path, "wwwroot");
69 |
70 | if (Directory.Exists(testPath))
71 | {
72 | return testPath;
73 | }
74 | }
75 | return null;
76 | }
77 |
78 | ///
79 | /// Gets an array containing all currently registered request handlers.
80 | ///
81 | public IRequestHandler[] RequestHandlers
82 | {
83 | get { return _requestHandlers.ToArray(); }
84 | }
85 |
86 | ///
87 | /// Initializes a new instance of the class.
88 | ///
89 | public IntegratedWebServer()
90 | {
91 | // For the entire lifetime of this instance, we'll preseve log messages.
92 | // After a certain point, it might be worth truncating them, but we'll cross that bridge when we get to it.
93 | _logLines = new List();
94 |
95 | // We need a place to store all the request handlers that have been registered.
96 | _requestHandlers = new List();
97 | }
98 |
99 | #region Create
100 |
101 | ///
102 | /// Called by the game after this instance is created.
103 | ///
104 | /// The threading.
105 | public override void OnCreated(IThreading threading)
106 | {
107 | InitializeServer();
108 |
109 | base.OnCreated(threading);
110 | }
111 |
112 | private void InitializeServer()
113 | {
114 | if (_server != null)
115 | {
116 | _server.Stop();
117 | _server = null;
118 | }
119 |
120 | LogMessage("Initializing Server...");
121 |
122 | List bindings = new List();
123 |
124 | int currentBinding = 1;
125 | String currentBindingKey = String.Format(WebServerHostKey, currentBinding);
126 | while (Configuration.HasSetting(currentBindingKey))
127 | {
128 | bindings.Add(Configuration.GetString(currentBindingKey));
129 | currentBinding++;
130 | currentBindingKey = String.Format(WebServerHostKey, currentBinding);
131 | }
132 |
133 | // If there are no bindings in the configuration file, we'll need to initialize those values.
134 | if (bindings.Count == 0)
135 | {
136 | const String defaultBinding = "http://localhost:8080/";
137 | bindings.Add(defaultBinding);
138 |
139 | // If there aren't any bindings, the value of currentBindingKey will never have made it past 1.
140 | // As a result, we can just use that.
141 | Configuration.SetString(currentBindingKey, defaultBinding);
142 | Configuration.SaveSettings();
143 | }
144 |
145 | // The endpoint used internally should always be the first binding in the configuration.
146 | // There's no need to use multiple bindings for internal references, we only need a single one.
147 | _endpoint = bindings.First();
148 |
149 | WebServer ws = new WebServer(HandleRequest, bindings.ToArray());
150 | _server = ws;
151 | _server.Run();
152 | LogMessage("Server Initialized.");
153 |
154 | _requestHandlers = new List();
155 |
156 | try
157 | {
158 | RegisterHandlers();
159 | }
160 | catch (Exception ex)
161 | {
162 | UnityEngine.Debug.LogException(ex);
163 | }
164 | }
165 |
166 | #endregion Create
167 |
168 | #region Release
169 |
170 | ///
171 | /// Called by the game before this instance is about to be destroyed.
172 | ///
173 | public override void OnReleased()
174 | {
175 | ReleaseServer();
176 |
177 | // TODO: Unregister from events (i.e. ILogAppender.LogMessage)
178 | _requestHandlers.Clear();
179 |
180 | Configuration.SaveSettings();
181 |
182 | base.OnReleased();
183 | }
184 |
185 | private void ReleaseServer()
186 | {
187 | LogMessage("Checking for existing server...");
188 | if (_server != null)
189 | {
190 | LogMessage("Server found; disposing...");
191 | _server.Stop();
192 | _server = null;
193 | LogMessage("Server Disposed.");
194 | }
195 | }
196 |
197 | #endregion Release
198 |
199 | /// ;
200 | /// Handles the specified request.
201 | ///
202 | ///
203 | /// Defers execution to an appropriate request handler, except for requests to the reserved endpoints: ~/ and ~/Log.
204 | /// Returns a default error message if an appropriate request handler can not be found.
205 | ///
206 | private void HandleRequest(HttpListenerRequest request, HttpListenerResponse response)
207 | {
208 | LogMessage(String.Format("{0} {1}", request.HttpMethod, request.RawUrl));
209 |
210 | var simulationManager = Singleton.instance;
211 | _cityName = simulationManager.m_metaData.m_CityName;
212 |
213 | // There are two reserved endpoints: "/" and "/Log".
214 | // These take precedence over all other request handlers.
215 | if (ServiceRoot(request, response))
216 | {
217 | return;
218 | }
219 |
220 | if (ServiceLog(request, response))
221 | {
222 | return;
223 | }
224 |
225 | // Get the request handler associated with the current request.
226 | var handler = _requestHandlers.FirstOrDefault(obj => obj.ShouldHandle(request));
227 | if (handler != null)
228 | {
229 | try
230 | {
231 | IResponseFormatter responseFormatterWriter = handler.Handle(request);
232 | responseFormatterWriter.WriteContent(response);
233 |
234 | return;
235 | }
236 | catch (Exception ex)
237 | {
238 | String errorBody = String.Format("An error has occurred!
{0}", ex);
239 | var tokens = TemplateHelper.GetTokenReplacements(_cityName, "Error", _requestHandlers, errorBody);
240 | var template = TemplateHelper.PopulateTemplate("index", tokens);
241 |
242 | IResponseFormatter errorResponseFormatter = new HtmlResponseFormatter(template);
243 | errorResponseFormatter.WriteContent(response);
244 |
245 | return;
246 | }
247 | }
248 |
249 | var wwwroot = GetWebRoot();
250 |
251 | // At this point, we can guarantee that we don't need any game data, so we can safely start a new thread to perform the remaining tasks.
252 | ServiceFileRequest(wwwroot, request, response);
253 | }
254 |
255 | private static void ServiceFileRequest(String wwwroot, HttpListenerRequest request, HttpListenerResponse response)
256 | {
257 | var relativePath = request.Url.AbsolutePath.Substring(1);
258 | relativePath = relativePath.Replace("/", Path.DirectorySeparatorChar.ToString());
259 | var absolutePath = Path.Combine(wwwroot, relativePath);
260 |
261 | if (File.Exists(absolutePath))
262 | {
263 | var extension = Path.GetExtension(absolutePath);
264 | response.ContentType = Apache.GetMime(extension);
265 | response.StatusCode = 200; // HTTP 200 - SUCCESS
266 |
267 | // Open file, read bytes into buffer and write them to the output stream.
268 | using (FileStream fileReader = File.OpenRead(absolutePath))
269 | {
270 | byte[] buffer = new byte[4096];
271 | int read;
272 | while ((read = fileReader.Read(buffer, 0, buffer.Length)) > 0)
273 | {
274 | response.OutputStream.Write(buffer, 0, read);
275 | }
276 | }
277 | }
278 | else
279 | {
280 | String body = String.Format("No resource is available at the specified filepath: {0}", absolutePath);
281 |
282 | IResponseFormatter notFoundResponseFormatter = new PlainTextResponseFormatter(body, HttpStatusCode.NotFound);
283 | notFoundResponseFormatter.WriteContent(response);
284 | }
285 | }
286 |
287 | ///
288 | /// Searches all the assemblies in the current AppDomain for class definitions that implement the interface. Those classes are instantiated and registered as request handlers.
289 | ///
290 | private void RegisterHandlers()
291 | {
292 | IEnumerable handlers = FindHandlersInLoadedAssemblies();
293 | RegisterHandlers(handlers);
294 | }
295 |
296 | private void RegisterHandlers(IEnumerable handlers)
297 | {
298 | if (handlers == null) { return; }
299 |
300 | if (_requestHandlers == null)
301 | {
302 | _requestHandlers = new List();
303 | }
304 |
305 | foreach (var handler in handlers)
306 | {
307 | // Only register handlers that we don't already have an instance of.
308 | if (_requestHandlers.Any(h => h.GetType() == handler))
309 | {
310 | continue;
311 | }
312 |
313 | IRequestHandler handlerInstance = null;
314 | Boolean exists = false;
315 |
316 | try
317 | {
318 | if (typeof(RequestHandlerBase).IsAssignableFrom(handler))
319 | {
320 | handlerInstance = (RequestHandlerBase)Activator.CreateInstance(handler, this);
321 | }
322 | else
323 | {
324 | handlerInstance = (IRequestHandler)Activator.CreateInstance(handler);
325 | }
326 |
327 | if (handlerInstance == null)
328 | {
329 | LogMessage(String.Format("Request Handler ({0}) could not be instantiated!", handler.Name));
330 | continue;
331 | }
332 |
333 | // Duplicates handlers seem to pass the check above, so now we filter them based on their identifier values, which should work.
334 | exists = _requestHandlers.Any(obj => obj.HandlerID == handlerInstance.HandlerID);
335 | }
336 | catch (Exception ex)
337 | {
338 | LogMessage(ex.ToString());
339 | }
340 |
341 | if (exists)
342 | {
343 | // TODO: Allow duplicate registrations to occur; previous registration is removed and replaced with a new one?
344 | LogMessage(String.Format("Supressing duplicate handler registration for '{0}'", handler.Name));
345 | }
346 | else
347 | {
348 | _requestHandlers.Add(handlerInstance);
349 | if (handlerInstance is ILogAppender)
350 | {
351 | var logAppender = (handlerInstance as ILogAppender);
352 | logAppender.LogMessage += RequestHandlerLogAppender_OnLogMessage;
353 | }
354 |
355 | LogMessage(String.Format("Added Request Handler: {0}", handler.FullName));
356 | }
357 | }
358 | }
359 |
360 | private void RequestHandlerLogAppender_OnLogMessage(object sender, LogAppenderEventArgs logAppenderEventArgs)
361 | {
362 | var senderTypeName = sender.GetType().Name;
363 | LogMessage(logAppenderEventArgs.LogLine, senderTypeName, false);
364 | }
365 |
366 | ///
367 | /// Searches all the assemblies in the current AppDomain, and returns a collection of those that implement the interface.
368 | ///
369 | private static IEnumerable FindHandlersInLoadedAssemblies()
370 | {
371 | var assemblies = AppDomain.CurrentDomain.GetAssemblies();
372 |
373 | foreach (var assembly in assemblies)
374 | {
375 | var handlers = FetchHandlers(assembly);
376 | foreach (var handler in handlers)
377 | {
378 | yield return handler;
379 | }
380 | }
381 | }
382 |
383 | private static IEnumerable FetchHandlers(Assembly assembly)
384 | {
385 | var assemblyName = assembly.GetName().Name;
386 |
387 | // Skip any assemblies that we don't anticipate finding anything in.
388 | if (IgnoredAssemblies.Contains(assemblyName)) { yield break; }
389 |
390 | Type[] types = new Type[0];
391 | try
392 | {
393 | types = assembly.GetTypes();
394 | }
395 | catch { }
396 |
397 | foreach (var type in types)
398 | {
399 | Boolean isValid = false;
400 | try
401 | {
402 | isValid = typeof(IRequestHandler).IsAssignableFrom(type) && type.IsClass && !type.IsAbstract;
403 | }
404 | catch { }
405 |
406 | if (isValid)
407 | {
408 | yield return type;
409 | }
410 | }
411 | }
412 |
413 | #region Reserved Endpoint Handlers
414 |
415 | ///
416 | /// Services requests to ~/
417 | ///
418 | private Boolean ServiceRoot(HttpListenerRequest request, HttpListenerResponse response)
419 | {
420 | if (request.Url.AbsolutePath.ToLower() == "/")
421 | {
422 | List links = new List();
423 | foreach (var requestHandler in this._requestHandlers.OrderBy(obj => obj.Priority))
424 | {
425 | links.Add(String.Format("{0} by {2} (Priority: {3})", requestHandler.Name, requestHandler.MainPath, requestHandler.Author, requestHandler.Priority));
426 | }
427 |
428 | String body = String.Format("Cities: Skylines - Integrated Web Server
", String.Join("", links.ToArray()));
429 | var tokens = TemplateHelper.GetTokenReplacements(_cityName, "Home", _requestHandlers, body);
430 | var template = TemplateHelper.PopulateTemplate("index", tokens);
431 |
432 | IResponseFormatter htmlResponseFormatter = new HtmlResponseFormatter(template);
433 | htmlResponseFormatter.WriteContent(response);
434 |
435 | return true;
436 | }
437 |
438 | return false;
439 | }
440 |
441 | ///
442 | /// Services requests to ~/Log
443 | ///
444 | private Boolean ServiceLog(HttpListenerRequest request, HttpListenerResponse response)
445 | {
446 | if (request.Url.AbsolutePath.ToLower() == "/log")
447 | {
448 | {
449 | String body = String.Format("Server Log
{0}", String.Join("", _logLines.ToArray()));
450 | var tokens = TemplateHelper.GetTokenReplacements(_cityName, "Log", _requestHandlers, body);
451 | var template = TemplateHelper.PopulateTemplate("index", tokens);
452 |
453 | IResponseFormatter htmlResponseFormatter = new HtmlResponseFormatter(template);
454 | htmlResponseFormatter.WriteContent(response);
455 |
456 | return true;
457 | }
458 | }
459 |
460 | return false;
461 | }
462 |
463 | #endregion Reserved Endpoint Handlers
464 |
465 | #region Logging
466 |
467 | ///
468 | /// Adds a timestamp to the specified message, and appends it to the internal log.
469 | ///
470 | public static void LogMessage(String message, String label = null, Boolean showInDebugPanel = false)
471 | {
472 | var dt = DateTime.Now;
473 | String time = String.Format("{0} {1}", dt.ToShortDateString(), dt.ToShortTimeString());
474 | String messageWithLabel = String.IsNullOrEmpty(label) ? message : String.Format("{0}: {1}", label, message);
475 | String line = String.Format("[{0}] {1}{2}", time, messageWithLabel, Environment.NewLine);
476 | _logLines.Add(line);
477 | if (showInDebugPanel)
478 | {
479 | DebugOutputPanel.AddMessage(PluginManager.MessageType.Message, line);
480 | }
481 | }
482 |
483 | ///
484 | /// Writes the value of . to the internal log.
485 | ///
486 | private void ServerOnLogMessage(object sender, LogAppenderEventArgs args)
487 | {
488 | LogMessage(args.LogLine);
489 | }
490 |
491 | #endregion Logging
492 | }
493 | }
--------------------------------------------------------------------------------
/CityWebServer/wwwroot/bootstrap.min.js:
--------------------------------------------------------------------------------
1 | /*!
2 | * Bootstrap v3.3.2 (http://getbootstrap.com)
3 | * Copyright 2011-2015 Twitter, Inc.
4 | * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
5 | */
6 | if("undefined"==typeof jQuery)throw new Error("Bootstrap's JavaScript requires jQuery");+function(a){"use strict";var b=a.fn.jquery.split(" ")[0].split(".");if(b[0]<2&&b[1]<9||1==b[0]&&9==b[1]&&b[2]<1)throw new Error("Bootstrap's JavaScript requires jQuery version 1.9.1 or higher")}(jQuery),+function(a){"use strict";function b(){var a=document.createElement("bootstrap"),b={WebkitTransition:"webkitTransitionEnd",MozTransition:"transitionend",OTransition:"oTransitionEnd otransitionend",transition:"transitionend"};for(var c in b)if(void 0!==a.style[c])return{end:b[c]};return!1}a.fn.emulateTransitionEnd=function(b){var c=!1,d=this;a(this).one("bsTransitionEnd",function(){c=!0});var e=function(){c||a(d).trigger(a.support.transition.end)};return setTimeout(e,b),this},a(function(){a.support.transition=b(),a.support.transition&&(a.event.special.bsTransitionEnd={bindType:a.support.transition.end,delegateType:a.support.transition.end,handle:function(b){return a(b.target).is(this)?b.handleObj.handler.apply(this,arguments):void 0}})})}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var c=a(this),e=c.data("bs.alert");e||c.data("bs.alert",e=new d(this)),"string"==typeof b&&e[b].call(c)})}var c='[data-dismiss="alert"]',d=function(b){a(b).on("click",c,this.close)};d.VERSION="3.3.2",d.TRANSITION_DURATION=150,d.prototype.close=function(b){function c(){g.detach().trigger("closed.bs.alert").remove()}var e=a(this),f=e.attr("data-target");f||(f=e.attr("href"),f=f&&f.replace(/.*(?=#[^\s]*$)/,""));var g=a(f);b&&b.preventDefault(),g.length||(g=e.closest(".alert")),g.trigger(b=a.Event("close.bs.alert")),b.isDefaultPrevented()||(g.removeClass("in"),a.support.transition&&g.hasClass("fade")?g.one("bsTransitionEnd",c).emulateTransitionEnd(d.TRANSITION_DURATION):c())};var e=a.fn.alert;a.fn.alert=b,a.fn.alert.Constructor=d,a.fn.alert.noConflict=function(){return a.fn.alert=e,this},a(document).on("click.bs.alert.data-api",c,d.prototype.close)}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.button"),f="object"==typeof b&&b;e||d.data("bs.button",e=new c(this,f)),"toggle"==b?e.toggle():b&&e.setState(b)})}var c=function(b,d){this.$element=a(b),this.options=a.extend({},c.DEFAULTS,d),this.isLoading=!1};c.VERSION="3.3.2",c.DEFAULTS={loadingText:"loading..."},c.prototype.setState=function(b){var c="disabled",d=this.$element,e=d.is("input")?"val":"html",f=d.data();b+="Text",null==f.resetText&&d.data("resetText",d[e]()),setTimeout(a.proxy(function(){d[e](null==f[b]?this.options[b]:f[b]),"loadingText"==b?(this.isLoading=!0,d.addClass(c).attr(c,c)):this.isLoading&&(this.isLoading=!1,d.removeClass(c).removeAttr(c))},this),0)},c.prototype.toggle=function(){var a=!0,b=this.$element.closest('[data-toggle="buttons"]');if(b.length){var c=this.$element.find("input");"radio"==c.prop("type")&&(c.prop("checked")&&this.$element.hasClass("active")?a=!1:b.find(".active").removeClass("active")),a&&c.prop("checked",!this.$element.hasClass("active")).trigger("change")}else this.$element.attr("aria-pressed",!this.$element.hasClass("active"));a&&this.$element.toggleClass("active")};var d=a.fn.button;a.fn.button=b,a.fn.button.Constructor=c,a.fn.button.noConflict=function(){return a.fn.button=d,this},a(document).on("click.bs.button.data-api",'[data-toggle^="button"]',function(c){var d=a(c.target);d.hasClass("btn")||(d=d.closest(".btn")),b.call(d,"toggle"),c.preventDefault()}).on("focus.bs.button.data-api blur.bs.button.data-api",'[data-toggle^="button"]',function(b){a(b.target).closest(".btn").toggleClass("focus",/^focus(in)?$/.test(b.type))})}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.carousel"),f=a.extend({},c.DEFAULTS,d.data(),"object"==typeof b&&b),g="string"==typeof b?b:f.slide;e||d.data("bs.carousel",e=new c(this,f)),"number"==typeof b?e.to(b):g?e[g]():f.interval&&e.pause().cycle()})}var c=function(b,c){this.$element=a(b),this.$indicators=this.$element.find(".carousel-indicators"),this.options=c,this.paused=this.sliding=this.interval=this.$active=this.$items=null,this.options.keyboard&&this.$element.on("keydown.bs.carousel",a.proxy(this.keydown,this)),"hover"==this.options.pause&&!("ontouchstart"in document.documentElement)&&this.$element.on("mouseenter.bs.carousel",a.proxy(this.pause,this)).on("mouseleave.bs.carousel",a.proxy(this.cycle,this))};c.VERSION="3.3.2",c.TRANSITION_DURATION=600,c.DEFAULTS={interval:5e3,pause:"hover",wrap:!0,keyboard:!0},c.prototype.keydown=function(a){if(!/input|textarea/i.test(a.target.tagName)){switch(a.which){case 37:this.prev();break;case 39:this.next();break;default:return}a.preventDefault()}},c.prototype.cycle=function(b){return b||(this.paused=!1),this.interval&&clearInterval(this.interval),this.options.interval&&!this.paused&&(this.interval=setInterval(a.proxy(this.next,this),this.options.interval)),this},c.prototype.getItemIndex=function(a){return this.$items=a.parent().children(".item"),this.$items.index(a||this.$active)},c.prototype.getItemForDirection=function(a,b){var c=this.getItemIndex(b),d="prev"==a&&0===c||"next"==a&&c==this.$items.length-1;if(d&&!this.options.wrap)return b;var e="prev"==a?-1:1,f=(c+e)%this.$items.length;return this.$items.eq(f)},c.prototype.to=function(a){var b=this,c=this.getItemIndex(this.$active=this.$element.find(".item.active"));return a>this.$items.length-1||0>a?void 0:this.sliding?this.$element.one("slid.bs.carousel",function(){b.to(a)}):c==a?this.pause().cycle():this.slide(a>c?"next":"prev",this.$items.eq(a))},c.prototype.pause=function(b){return b||(this.paused=!0),this.$element.find(".next, .prev").length&&a.support.transition&&(this.$element.trigger(a.support.transition.end),this.cycle(!0)),this.interval=clearInterval(this.interval),this},c.prototype.next=function(){return this.sliding?void 0:this.slide("next")},c.prototype.prev=function(){return this.sliding?void 0:this.slide("prev")},c.prototype.slide=function(b,d){var e=this.$element.find(".item.active"),f=d||this.getItemForDirection(b,e),g=this.interval,h="next"==b?"left":"right",i=this;if(f.hasClass("active"))return this.sliding=!1;var j=f[0],k=a.Event("slide.bs.carousel",{relatedTarget:j,direction:h});if(this.$element.trigger(k),!k.isDefaultPrevented()){if(this.sliding=!0,g&&this.pause(),this.$indicators.length){this.$indicators.find(".active").removeClass("active");var l=a(this.$indicators.children()[this.getItemIndex(f)]);l&&l.addClass("active")}var m=a.Event("slid.bs.carousel",{relatedTarget:j,direction:h});return a.support.transition&&this.$element.hasClass("slide")?(f.addClass(b),f[0].offsetWidth,e.addClass(h),f.addClass(h),e.one("bsTransitionEnd",function(){f.removeClass([b,h].join(" ")).addClass("active"),e.removeClass(["active",h].join(" ")),i.sliding=!1,setTimeout(function(){i.$element.trigger(m)},0)}).emulateTransitionEnd(c.TRANSITION_DURATION)):(e.removeClass("active"),f.addClass("active"),this.sliding=!1,this.$element.trigger(m)),g&&this.cycle(),this}};var d=a.fn.carousel;a.fn.carousel=b,a.fn.carousel.Constructor=c,a.fn.carousel.noConflict=function(){return a.fn.carousel=d,this};var e=function(c){var d,e=a(this),f=a(e.attr("data-target")||(d=e.attr("href"))&&d.replace(/.*(?=#[^\s]+$)/,""));if(f.hasClass("carousel")){var g=a.extend({},f.data(),e.data()),h=e.attr("data-slide-to");h&&(g.interval=!1),b.call(f,g),h&&f.data("bs.carousel").to(h),c.preventDefault()}};a(document).on("click.bs.carousel.data-api","[data-slide]",e).on("click.bs.carousel.data-api","[data-slide-to]",e),a(window).on("load",function(){a('[data-ride="carousel"]').each(function(){var c=a(this);b.call(c,c.data())})})}(jQuery),+function(a){"use strict";function b(b){var c,d=b.attr("data-target")||(c=b.attr("href"))&&c.replace(/.*(?=#[^\s]+$)/,"");return a(d)}function c(b){return this.each(function(){var c=a(this),e=c.data("bs.collapse"),f=a.extend({},d.DEFAULTS,c.data(),"object"==typeof b&&b);!e&&f.toggle&&"show"==b&&(f.toggle=!1),e||c.data("bs.collapse",e=new d(this,f)),"string"==typeof b&&e[b]()})}var d=function(b,c){this.$element=a(b),this.options=a.extend({},d.DEFAULTS,c),this.$trigger=a(this.options.trigger).filter('[href="#'+b.id+'"], [data-target="#'+b.id+'"]'),this.transitioning=null,this.options.parent?this.$parent=this.getParent():this.addAriaAndCollapsedClass(this.$element,this.$trigger),this.options.toggle&&this.toggle()};d.VERSION="3.3.2",d.TRANSITION_DURATION=350,d.DEFAULTS={toggle:!0,trigger:'[data-toggle="collapse"]'},d.prototype.dimension=function(){var a=this.$element.hasClass("width");return a?"width":"height"},d.prototype.show=function(){if(!this.transitioning&&!this.$element.hasClass("in")){var b,e=this.$parent&&this.$parent.children(".panel").children(".in, .collapsing");if(!(e&&e.length&&(b=e.data("bs.collapse"),b&&b.transitioning))){var f=a.Event("show.bs.collapse");if(this.$element.trigger(f),!f.isDefaultPrevented()){e&&e.length&&(c.call(e,"hide"),b||e.data("bs.collapse",null));var g=this.dimension();this.$element.removeClass("collapse").addClass("collapsing")[g](0).attr("aria-expanded",!0),this.$trigger.removeClass("collapsed").attr("aria-expanded",!0),this.transitioning=1;var h=function(){this.$element.removeClass("collapsing").addClass("collapse in")[g](""),this.transitioning=0,this.$element.trigger("shown.bs.collapse")};if(!a.support.transition)return h.call(this);var i=a.camelCase(["scroll",g].join("-"));this.$element.one("bsTransitionEnd",a.proxy(h,this)).emulateTransitionEnd(d.TRANSITION_DURATION)[g](this.$element[0][i])}}}},d.prototype.hide=function(){if(!this.transitioning&&this.$element.hasClass("in")){var b=a.Event("hide.bs.collapse");if(this.$element.trigger(b),!b.isDefaultPrevented()){var c=this.dimension();this.$element[c](this.$element[c]())[0].offsetHeight,this.$element.addClass("collapsing").removeClass("collapse in").attr("aria-expanded",!1),this.$trigger.addClass("collapsed").attr("aria-expanded",!1),this.transitioning=1;var e=function(){this.transitioning=0,this.$element.removeClass("collapsing").addClass("collapse").trigger("hidden.bs.collapse")};return a.support.transition?void this.$element[c](0).one("bsTransitionEnd",a.proxy(e,this)).emulateTransitionEnd(d.TRANSITION_DURATION):e.call(this)}}},d.prototype.toggle=function(){this[this.$element.hasClass("in")?"hide":"show"]()},d.prototype.getParent=function(){return a(this.options.parent).find('[data-toggle="collapse"][data-parent="'+this.options.parent+'"]').each(a.proxy(function(c,d){var e=a(d);this.addAriaAndCollapsedClass(b(e),e)},this)).end()},d.prototype.addAriaAndCollapsedClass=function(a,b){var c=a.hasClass("in");a.attr("aria-expanded",c),b.toggleClass("collapsed",!c).attr("aria-expanded",c)};var e=a.fn.collapse;a.fn.collapse=c,a.fn.collapse.Constructor=d,a.fn.collapse.noConflict=function(){return a.fn.collapse=e,this},a(document).on("click.bs.collapse.data-api",'[data-toggle="collapse"]',function(d){var e=a(this);e.attr("data-target")||d.preventDefault();var f=b(e),g=f.data("bs.collapse"),h=g?"toggle":a.extend({},e.data(),{trigger:this});c.call(f,h)})}(jQuery),+function(a){"use strict";function b(b){b&&3===b.which||(a(e).remove(),a(f).each(function(){var d=a(this),e=c(d),f={relatedTarget:this};e.hasClass("open")&&(e.trigger(b=a.Event("hide.bs.dropdown",f)),b.isDefaultPrevented()||(d.attr("aria-expanded","false"),e.removeClass("open").trigger("hidden.bs.dropdown",f)))}))}function c(b){var c=b.attr("data-target");c||(c=b.attr("href"),c=c&&/#[A-Za-z]/.test(c)&&c.replace(/.*(?=#[^\s]*$)/,""));var d=c&&a(c);return d&&d.length?d:b.parent()}function d(b){return this.each(function(){var c=a(this),d=c.data("bs.dropdown");d||c.data("bs.dropdown",d=new g(this)),"string"==typeof b&&d[b].call(c)})}var e=".dropdown-backdrop",f='[data-toggle="dropdown"]',g=function(b){a(b).on("click.bs.dropdown",this.toggle)};g.VERSION="3.3.2",g.prototype.toggle=function(d){var e=a(this);if(!e.is(".disabled, :disabled")){var f=c(e),g=f.hasClass("open");if(b(),!g){"ontouchstart"in document.documentElement&&!f.closest(".navbar-nav").length&&a('').insertAfter(a(this)).on("click",b);var h={relatedTarget:this};if(f.trigger(d=a.Event("show.bs.dropdown",h)),d.isDefaultPrevented())return;e.trigger("focus").attr("aria-expanded","true"),f.toggleClass("open").trigger("shown.bs.dropdown",h)}return!1}},g.prototype.keydown=function(b){if(/(38|40|27|32)/.test(b.which)&&!/input|textarea/i.test(b.target.tagName)){var d=a(this);if(b.preventDefault(),b.stopPropagation(),!d.is(".disabled, :disabled")){var e=c(d),g=e.hasClass("open");if(!g&&27!=b.which||g&&27==b.which)return 27==b.which&&e.find(f).trigger("focus"),d.trigger("click");var h=" li:not(.divider):visible a",i=e.find('[role="menu"]'+h+', [role="listbox"]'+h);if(i.length){var j=i.index(b.target);38==b.which&&j>0&&j--,40==b.which&&j').prependTo(this.$element).on("click.dismiss.bs.modal",a.proxy(function(a){a.target===a.currentTarget&&("static"==this.options.backdrop?this.$element[0].focus.call(this.$element[0]):this.hide.call(this))},this)),f&&this.$backdrop[0].offsetWidth,this.$backdrop.addClass("in"),!b)return;f?this.$backdrop.one("bsTransitionEnd",b).emulateTransitionEnd(c.BACKDROP_TRANSITION_DURATION):b()}else if(!this.isShown&&this.$backdrop){this.$backdrop.removeClass("in");var g=function(){d.removeBackdrop(),b&&b()};a.support.transition&&this.$element.hasClass("fade")?this.$backdrop.one("bsTransitionEnd",g).emulateTransitionEnd(c.BACKDROP_TRANSITION_DURATION):g()}else b&&b()},c.prototype.handleUpdate=function(){this.options.backdrop&&this.adjustBackdrop(),this.adjustDialog()},c.prototype.adjustBackdrop=function(){this.$backdrop.css("height",0).css("height",this.$element[0].scrollHeight)},c.prototype.adjustDialog=function(){var a=this.$element[0].scrollHeight>document.documentElement.clientHeight;this.$element.css({paddingLeft:!this.bodyIsOverflowing&&a?this.scrollbarWidth:"",paddingRight:this.bodyIsOverflowing&&!a?this.scrollbarWidth:""})},c.prototype.resetAdjustments=function(){this.$element.css({paddingLeft:"",paddingRight:""})},c.prototype.checkScrollbar=function(){this.bodyIsOverflowing=document.body.scrollHeight>document.documentElement.clientHeight,this.scrollbarWidth=this.measureScrollbar()},c.prototype.setScrollbar=function(){var a=parseInt(this.$body.css("padding-right")||0,10);this.bodyIsOverflowing&&this.$body.css("padding-right",a+this.scrollbarWidth)},c.prototype.resetScrollbar=function(){this.$body.css("padding-right","")},c.prototype.measureScrollbar=function(){var a=document.createElement("div");a.className="modal-scrollbar-measure",this.$body.append(a);var b=a.offsetWidth-a.clientWidth;return this.$body[0].removeChild(a),b};var d=a.fn.modal;a.fn.modal=b,a.fn.modal.Constructor=c,a.fn.modal.noConflict=function(){return a.fn.modal=d,this},a(document).on("click.bs.modal.data-api",'[data-toggle="modal"]',function(c){var d=a(this),e=d.attr("href"),f=a(d.attr("data-target")||e&&e.replace(/.*(?=#[^\s]+$)/,"")),g=f.data("bs.modal")?"toggle":a.extend({remote:!/#/.test(e)&&e},f.data(),d.data());d.is("a")&&c.preventDefault(),f.one("show.bs.modal",function(a){a.isDefaultPrevented()||f.one("hidden.bs.modal",function(){d.is(":visible")&&d.trigger("focus")})}),b.call(f,g,this)})}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.tooltip"),f="object"==typeof b&&b;(e||"destroy"!=b)&&(e||d.data("bs.tooltip",e=new c(this,f)),"string"==typeof b&&e[b]())})}var c=function(a,b){this.type=this.options=this.enabled=this.timeout=this.hoverState=this.$element=null,this.init("tooltip",a,b)};c.VERSION="3.3.2",c.TRANSITION_DURATION=150,c.DEFAULTS={animation:!0,placement:"top",selector:!1,template:'',trigger:"hover focus",title:"",delay:0,html:!1,container:!1,viewport:{selector:"body",padding:0}},c.prototype.init=function(b,c,d){this.enabled=!0,this.type=b,this.$element=a(c),this.options=this.getOptions(d),this.$viewport=this.options.viewport&&a(this.options.viewport.selector||this.options.viewport);for(var e=this.options.trigger.split(" "),f=e.length;f--;){var g=e[f];if("click"==g)this.$element.on("click."+this.type,this.options.selector,a.proxy(this.toggle,this));else if("manual"!=g){var h="hover"==g?"mouseenter":"focusin",i="hover"==g?"mouseleave":"focusout";this.$element.on(h+"."+this.type,this.options.selector,a.proxy(this.enter,this)),this.$element.on(i+"."+this.type,this.options.selector,a.proxy(this.leave,this))}}this.options.selector?this._options=a.extend({},this.options,{trigger:"manual",selector:""}):this.fixTitle()},c.prototype.getDefaults=function(){return c.DEFAULTS},c.prototype.getOptions=function(b){return b=a.extend({},this.getDefaults(),this.$element.data(),b),b.delay&&"number"==typeof b.delay&&(b.delay={show:b.delay,hide:b.delay}),b},c.prototype.getDelegateOptions=function(){var b={},c=this.getDefaults();return this._options&&a.each(this._options,function(a,d){c[a]!=d&&(b[a]=d)}),b},c.prototype.enter=function(b){var c=b instanceof this.constructor?b:a(b.currentTarget).data("bs."+this.type);return c&&c.$tip&&c.$tip.is(":visible")?void(c.hoverState="in"):(c||(c=new this.constructor(b.currentTarget,this.getDelegateOptions()),a(b.currentTarget).data("bs."+this.type,c)),clearTimeout(c.timeout),c.hoverState="in",c.options.delay&&c.options.delay.show?void(c.timeout=setTimeout(function(){"in"==c.hoverState&&c.show()},c.options.delay.show)):c.show())},c.prototype.leave=function(b){var c=b instanceof this.constructor?b:a(b.currentTarget).data("bs."+this.type);return c||(c=new this.constructor(b.currentTarget,this.getDelegateOptions()),a(b.currentTarget).data("bs."+this.type,c)),clearTimeout(c.timeout),c.hoverState="out",c.options.delay&&c.options.delay.hide?void(c.timeout=setTimeout(function(){"out"==c.hoverState&&c.hide()},c.options.delay.hide)):c.hide()},c.prototype.show=function(){var b=a.Event("show.bs."+this.type);if(this.hasContent()&&this.enabled){this.$element.trigger(b);var d=a.contains(this.$element[0].ownerDocument.documentElement,this.$element[0]);if(b.isDefaultPrevented()||!d)return;var e=this,f=this.tip(),g=this.getUID(this.type);this.setContent(),f.attr("id",g),this.$element.attr("aria-describedby",g),this.options.animation&&f.addClass("fade");var h="function"==typeof this.options.placement?this.options.placement.call(this,f[0],this.$element[0]):this.options.placement,i=/\s?auto?\s?/i,j=i.test(h);j&&(h=h.replace(i,"")||"top"),f.detach().css({top:0,left:0,display:"block"}).addClass(h).data("bs."+this.type,this),this.options.container?f.appendTo(this.options.container):f.insertAfter(this.$element);var k=this.getPosition(),l=f[0].offsetWidth,m=f[0].offsetHeight;if(j){var n=h,o=this.options.container?a(this.options.container):this.$element.parent(),p=this.getPosition(o);h="bottom"==h&&k.bottom+m>p.bottom?"top":"top"==h&&k.top-mp.width?"left":"left"==h&&k.left-lg.top+g.height&&(e.top=g.top+g.height-i)}else{var j=b.left-f,k=b.left+f+c;jg.width&&(e.left=g.left+g.width-k)}return e},c.prototype.getTitle=function(){var a,b=this.$element,c=this.options;return a=b.attr("data-original-title")||("function"==typeof c.title?c.title.call(b[0]):c.title)},c.prototype.getUID=function(a){do a+=~~(1e6*Math.random());while(document.getElementById(a));return a},c.prototype.tip=function(){return this.$tip=this.$tip||a(this.options.template)},c.prototype.arrow=function(){return this.$arrow=this.$arrow||this.tip().find(".tooltip-arrow")},c.prototype.enable=function(){this.enabled=!0},c.prototype.disable=function(){this.enabled=!1},c.prototype.toggleEnabled=function(){this.enabled=!this.enabled},c.prototype.toggle=function(b){var c=this;b&&(c=a(b.currentTarget).data("bs."+this.type),c||(c=new this.constructor(b.currentTarget,this.getDelegateOptions()),a(b.currentTarget).data("bs."+this.type,c))),c.tip().hasClass("in")?c.leave(c):c.enter(c)},c.prototype.destroy=function(){var a=this;clearTimeout(this.timeout),this.hide(function(){a.$element.off("."+a.type).removeData("bs."+a.type)})};var d=a.fn.tooltip;a.fn.tooltip=b,a.fn.tooltip.Constructor=c,a.fn.tooltip.noConflict=function(){return a.fn.tooltip=d,this}}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.popover"),f="object"==typeof b&&b;(e||"destroy"!=b)&&(e||d.data("bs.popover",e=new c(this,f)),"string"==typeof b&&e[b]())})}var c=function(a,b){this.init("popover",a,b)};if(!a.fn.tooltip)throw new Error("Popover requires tooltip.js");c.VERSION="3.3.2",c.DEFAULTS=a.extend({},a.fn.tooltip.Constructor.DEFAULTS,{placement:"right",trigger:"click",content:"",template:''}),c.prototype=a.extend({},a.fn.tooltip.Constructor.prototype),c.prototype.constructor=c,c.prototype.getDefaults=function(){return c.DEFAULTS},c.prototype.setContent=function(){var a=this.tip(),b=this.getTitle(),c=this.getContent();a.find(".popover-title")[this.options.html?"html":"text"](b),a.find(".popover-content").children().detach().end()[this.options.html?"string"==typeof c?"html":"append":"text"](c),a.removeClass("fade top bottom left right in"),a.find(".popover-title").html()||a.find(".popover-title").hide()},c.prototype.hasContent=function(){return this.getTitle()||this.getContent()},c.prototype.getContent=function(){var a=this.$element,b=this.options;return a.attr("data-content")||("function"==typeof b.content?b.content.call(a[0]):b.content)},c.prototype.arrow=function(){return this.$arrow=this.$arrow||this.tip().find(".arrow")},c.prototype.tip=function(){return this.$tip||(this.$tip=a(this.options.template)),this.$tip};var d=a.fn.popover;a.fn.popover=b,a.fn.popover.Constructor=c,a.fn.popover.noConflict=function(){return a.fn.popover=d,this}}(jQuery),+function(a){"use strict";function b(c,d){var e=a.proxy(this.process,this);this.$body=a("body"),this.$scrollElement=a(a(c).is("body")?window:c),this.options=a.extend({},b.DEFAULTS,d),this.selector=(this.options.target||"")+" .nav li > a",this.offsets=[],this.targets=[],this.activeTarget=null,this.scrollHeight=0,this.$scrollElement.on("scroll.bs.scrollspy",e),this.refresh(),this.process()}function c(c){return this.each(function(){var d=a(this),e=d.data("bs.scrollspy"),f="object"==typeof c&&c;e||d.data("bs.scrollspy",e=new b(this,f)),"string"==typeof c&&e[c]()})}b.VERSION="3.3.2",b.DEFAULTS={offset:10},b.prototype.getScrollHeight=function(){return this.$scrollElement[0].scrollHeight||Math.max(this.$body[0].scrollHeight,document.documentElement.scrollHeight)},b.prototype.refresh=function(){var b="offset",c=0;a.isWindow(this.$scrollElement[0])||(b="position",c=this.$scrollElement.scrollTop()),this.offsets=[],this.targets=[],this.scrollHeight=this.getScrollHeight();var d=this;this.$body.find(this.selector).map(function(){var d=a(this),e=d.data("target")||d.attr("href"),f=/^#./.test(e)&&a(e);return f&&f.length&&f.is(":visible")&&[[f[b]().top+c,e]]||null}).sort(function(a,b){return a[0]-b[0]}).each(function(){d.offsets.push(this[0]),d.targets.push(this[1])})},b.prototype.process=function(){var a,b=this.$scrollElement.scrollTop()+this.options.offset,c=this.getScrollHeight(),d=this.options.offset+c-this.$scrollElement.height(),e=this.offsets,f=this.targets,g=this.activeTarget;if(this.scrollHeight!=c&&this.refresh(),b>=d)return g!=(a=f[f.length-1])&&this.activate(a);if(g&&b=e[a]&&(!e[a+1]||b<=e[a+1])&&this.activate(f[a])},b.prototype.activate=function(b){this.activeTarget=b,this.clear();var c=this.selector+'[data-target="'+b+'"],'+this.selector+'[href="'+b+'"]',d=a(c).parents("li").addClass("active");d.parent(".dropdown-menu").length&&(d=d.closest("li.dropdown").addClass("active")),d.trigger("activate.bs.scrollspy")},b.prototype.clear=function(){a(this.selector).parentsUntil(this.options.target,".active").removeClass("active")};var d=a.fn.scrollspy;a.fn.scrollspy=c,a.fn.scrollspy.Constructor=b,a.fn.scrollspy.noConflict=function(){return a.fn.scrollspy=d,this},a(window).on("load.bs.scrollspy.data-api",function(){a('[data-spy="scroll"]').each(function(){var b=a(this);c.call(b,b.data())})})}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.tab");e||d.data("bs.tab",e=new c(this)),"string"==typeof b&&e[b]()})}var c=function(b){this.element=a(b)};c.VERSION="3.3.2",c.TRANSITION_DURATION=150,c.prototype.show=function(){var b=this.element,c=b.closest("ul:not(.dropdown-menu)"),d=b.data("target");if(d||(d=b.attr("href"),d=d&&d.replace(/.*(?=#[^\s]*$)/,"")),!b.parent("li").hasClass("active")){var e=c.find(".active:last a"),f=a.Event("hide.bs.tab",{relatedTarget:b[0]}),g=a.Event("show.bs.tab",{relatedTarget:e[0]});if(e.trigger(f),b.trigger(g),!g.isDefaultPrevented()&&!f.isDefaultPrevented()){var h=a(d);this.activate(b.closest("li"),c),this.activate(h,h.parent(),function(){e.trigger({type:"hidden.bs.tab",relatedTarget:b[0]}),b.trigger({type:"shown.bs.tab",relatedTarget:e[0]})})}}},c.prototype.activate=function(b,d,e){function f(){g.removeClass("active").find("> .dropdown-menu > .active").removeClass("active").end().find('[data-toggle="tab"]').attr("aria-expanded",!1),b.addClass("active").find('[data-toggle="tab"]').attr("aria-expanded",!0),h?(b[0].offsetWidth,b.addClass("in")):b.removeClass("fade"),b.parent(".dropdown-menu")&&b.closest("li.dropdown").addClass("active").end().find('[data-toggle="tab"]').attr("aria-expanded",!0),e&&e()
7 | }var g=d.find("> .active"),h=e&&a.support.transition&&(g.length&&g.hasClass("fade")||!!d.find("> .fade").length);g.length&&h?g.one("bsTransitionEnd",f).emulateTransitionEnd(c.TRANSITION_DURATION):f(),g.removeClass("in")};var d=a.fn.tab;a.fn.tab=b,a.fn.tab.Constructor=c,a.fn.tab.noConflict=function(){return a.fn.tab=d,this};var e=function(c){c.preventDefault(),b.call(a(this),"show")};a(document).on("click.bs.tab.data-api",'[data-toggle="tab"]',e).on("click.bs.tab.data-api",'[data-toggle="pill"]',e)}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.affix"),f="object"==typeof b&&b;e||d.data("bs.affix",e=new c(this,f)),"string"==typeof b&&e[b]()})}var c=function(b,d){this.options=a.extend({},c.DEFAULTS,d),this.$target=a(this.options.target).on("scroll.bs.affix.data-api",a.proxy(this.checkPosition,this)).on("click.bs.affix.data-api",a.proxy(this.checkPositionWithEventLoop,this)),this.$element=a(b),this.affixed=this.unpin=this.pinnedOffset=null,this.checkPosition()};c.VERSION="3.3.2",c.RESET="affix affix-top affix-bottom",c.DEFAULTS={offset:0,target:window},c.prototype.getState=function(a,b,c,d){var e=this.$target.scrollTop(),f=this.$element.offset(),g=this.$target.height();if(null!=c&&"top"==this.affixed)return c>e?"top":!1;if("bottom"==this.affixed)return null!=c?e+this.unpin<=f.top?!1:"bottom":a-d>=e+g?!1:"bottom";var h=null==this.affixed,i=h?e:f.top,j=h?g:b;return null!=c&&c>=e?"top":null!=d&&i+j>=a-d?"bottom":!1},c.prototype.getPinnedOffset=function(){if(this.pinnedOffset)return this.pinnedOffset;this.$element.removeClass(c.RESET).addClass("affix");var a=this.$target.scrollTop(),b=this.$element.offset();return this.pinnedOffset=b.top-a},c.prototype.checkPositionWithEventLoop=function(){setTimeout(a.proxy(this.checkPosition,this),1)},c.prototype.checkPosition=function(){if(this.$element.is(":visible")){var b=this.$element.height(),d=this.options.offset,e=d.top,f=d.bottom,g=a("body").height();"object"!=typeof d&&(f=e=d),"function"==typeof e&&(e=d.top(this.$element)),"function"==typeof f&&(f=d.bottom(this.$element));var h=this.getState(g,b,e,f);if(this.affixed!=h){null!=this.unpin&&this.$element.css("top","");var i="affix"+(h?"-"+h:""),j=a.Event(i+".bs.affix");if(this.$element.trigger(j),j.isDefaultPrevented())return;this.affixed=h,this.unpin="bottom"==h?this.getPinnedOffset():null,this.$element.removeClass(c.RESET).addClass(i).trigger(i.replace("affix","affixed")+".bs.affix")}"bottom"==h&&this.$element.offset({top:g-b-f})}};var d=a.fn.affix;a.fn.affix=b,a.fn.affix.Constructor=c,a.fn.affix.noConflict=function(){return a.fn.affix=d,this},a(window).on("load",function(){a('[data-spy="affix"]').each(function(){var c=a(this),d=c.data();d.offset=d.offset||{},null!=d.offsetBottom&&(d.offset.bottom=d.offsetBottom),null!=d.offsetTop&&(d.offset.top=d.offsetTop),b.call(c,d)})})}(jQuery);
--------------------------------------------------------------------------------
/CityWebServer/Helpers/ApacheMimeTypes.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 |
5 | // Source: https://raw.githubusercontent.com/cymen/ApacheMimeTypesToDotNet/master/ApacheMimeTypes.cs
6 | namespace ApacheMimeTypes
7 | {
8 | internal class Apache
9 | {
10 | private static Dictionary _mimeTypes = new Dictionary
11 | {
12 | { "123", "application/vnd.lotus-1-2-3" },
13 | { "3dml", "text/vnd.in3d.3dml" },
14 | { "3g2", "video/3gpp2" },
15 | { "3gp", "video/3gpp" },
16 | { "7z", "application/x-7z-compressed" },
17 | { "aab", "application/x-authorware-bin" },
18 | { "aac", "audio/x-aac" },
19 | { "aam", "application/x-authorware-map" },
20 | { "aas", "application/x-authorware-seg" },
21 | { "abw", "application/x-abiword" },
22 | { "ac", "application/pkix-attr-cert" },
23 | { "acc", "application/vnd.americandynamics.acc" },
24 | { "ace", "application/x-ace-compressed" },
25 | { "acu", "application/vnd.acucobol" },
26 | { "acutc", "application/vnd.acucorp" },
27 | { "adp", "audio/adpcm" },
28 | { "aep", "application/vnd.audiograph" },
29 | { "afm", "application/x-font-type1" },
30 | { "afp", "application/vnd.ibm.modcap" },
31 | { "ahead", "application/vnd.ahead.space" },
32 | { "ai", "application/postscript" },
33 | { "aif", "audio/x-aiff" },
34 | { "aifc", "audio/x-aiff" },
35 | { "aiff", "audio/x-aiff" },
36 | { "air", "application/vnd.adobe.air-application-installer-package+zip" },
37 | { "ait", "application/vnd.dvb.ait" },
38 | { "ami", "application/vnd.amiga.ami" },
39 | { "apk", "application/vnd.android.package-archive" },
40 | { "application", "application/x-ms-application" },
41 | { "apr", "application/vnd.lotus-approach" },
42 | { "asc", "application/pgp-signature" },
43 | { "asf", "video/x-ms-asf" },
44 | { "asm", "text/x-asm" },
45 | { "aso", "application/vnd.accpac.simply.aso" },
46 | { "asx", "video/x-ms-asf" },
47 | { "atc", "application/vnd.acucorp" },
48 | { "atom", "application/atom+xml" },
49 | { "atomcat", "application/atomcat+xml" },
50 | { "atomsvc", "application/atomsvc+xml" },
51 | { "atx", "application/vnd.antix.game-component" },
52 | { "au", "audio/basic" },
53 | { "avi", "video/x-msvideo" },
54 | { "aw", "application/applixware" },
55 | { "azf", "application/vnd.airzip.filesecure.azf" },
56 | { "azs", "application/vnd.airzip.filesecure.azs" },
57 | { "azw", "application/vnd.amazon.ebook" },
58 | { "bat", "application/x-msdownload" },
59 | { "bcpio", "application/x-bcpio" },
60 | { "bdf", "application/x-font-bdf" },
61 | { "bdm", "application/vnd.syncml.dm+wbxml" },
62 | { "bed", "application/vnd.realvnc.bed" },
63 | { "bh2", "application/vnd.fujitsu.oasysprs" },
64 | { "bin", "application/octet-stream" },
65 | { "bmi", "application/vnd.bmi" },
66 | { "bmp", "image/bmp" },
67 | { "book", "application/vnd.framemaker" },
68 | { "box", "application/vnd.previewsystems.box" },
69 | { "boz", "application/x-bzip2" },
70 | { "bpk", "application/octet-stream" },
71 | { "btif", "image/prs.btif" },
72 | { "bz", "application/x-bzip" },
73 | { "bz2", "application/x-bzip2" },
74 | { "c", "text/x-c" },
75 | { "c11amc", "application/vnd.cluetrust.cartomobile-config" },
76 | { "c11amz", "application/vnd.cluetrust.cartomobile-config-pkg" },
77 | { "c4d", "application/vnd.clonk.c4group" },
78 | { "c4f", "application/vnd.clonk.c4group" },
79 | { "c4g", "application/vnd.clonk.c4group" },
80 | { "c4p", "application/vnd.clonk.c4group" },
81 | { "c4u", "application/vnd.clonk.c4group" },
82 | { "cab", "application/vnd.ms-cab-compressed" },
83 | { "car", "application/vnd.curl.car" },
84 | { "cat", "application/vnd.ms-pki.seccat" },
85 | { "cc", "text/x-c" },
86 | { "cct", "application/x-director" },
87 | { "ccxml", "application/ccxml+xml" },
88 | { "cdbcmsg", "application/vnd.contact.cmsg" },
89 | { "cdf", "application/x-netcdf" },
90 | { "cdkey", "application/vnd.mediastation.cdkey" },
91 | { "cdmia", "application/cdmi-capability" },
92 | { "cdmic", "application/cdmi-container" },
93 | { "cdmid", "application/cdmi-domain" },
94 | { "cdmio", "application/cdmi-object" },
95 | { "cdmiq", "application/cdmi-queue" },
96 | { "cdx", "chemical/x-cdx" },
97 | { "cdxml", "application/vnd.chemdraw+xml" },
98 | { "cdy", "application/vnd.cinderella" },
99 | { "cer", "application/pkix-cert" },
100 | { "cgm", "image/cgm" },
101 | { "chat", "application/x-chat" },
102 | { "chm", "application/vnd.ms-htmlhelp" },
103 | { "chrt", "application/vnd.kde.kchart" },
104 | { "cif", "chemical/x-cif" },
105 | { "cii", "application/vnd.anser-web-certificate-issue-initiation" },
106 | { "cil", "application/vnd.ms-artgalry" },
107 | { "cla", "application/vnd.claymore" },
108 | { "class", "application/java-vm" },
109 | { "clkk", "application/vnd.crick.clicker.keyboard" },
110 | { "clkp", "application/vnd.crick.clicker.palette" },
111 | { "clkt", "application/vnd.crick.clicker.template" },
112 | { "clkw", "application/vnd.crick.clicker.wordbank" },
113 | { "clkx", "application/vnd.crick.clicker" },
114 | { "clp", "application/x-msclip" },
115 | { "cmc", "application/vnd.cosmocaller" },
116 | { "cmdf", "chemical/x-cmdf" },
117 | { "cml", "chemical/x-cml" },
118 | { "cmp", "application/vnd.yellowriver-custom-menu" },
119 | { "cmx", "image/x-cmx" },
120 | { "cod", "application/vnd.rim.cod" },
121 | { "com", "application/x-msdownload" },
122 | { "conf", "text/plain" },
123 | { "cpio", "application/x-cpio" },
124 | { "cpp", "text/x-c" },
125 | { "cpt", "application/mac-compactpro" },
126 | { "crd", "application/x-mscardfile" },
127 | { "crl", "application/pkix-crl" },
128 | { "crt", "application/x-x509-ca-cert" },
129 | { "cryptonote", "application/vnd.rig.cryptonote" },
130 | { "csh", "application/x-csh" },
131 | { "csml", "chemical/x-csml" },
132 | { "csp", "application/vnd.commonspace" },
133 | { "css", "text/css" },
134 | { "cst", "application/x-director" },
135 | { "csv", "text/csv" },
136 | { "cu", "application/cu-seeme" },
137 | { "curl", "text/vnd.curl" },
138 | { "cww", "application/prs.cww" },
139 | { "cxt", "application/x-director" },
140 | { "cxx", "text/x-c" },
141 | { "dae", "model/vnd.collada+xml" },
142 | { "daf", "application/vnd.mobius.daf" },
143 | { "dataless", "application/vnd.fdsn.seed" },
144 | { "davmount", "application/davmount+xml" },
145 | { "dcr", "application/x-director" },
146 | { "dcurl", "text/vnd.curl.dcurl" },
147 | { "dd2", "application/vnd.oma.dd2+xml" },
148 | { "ddd", "application/vnd.fujixerox.ddd" },
149 | { "deb", "application/x-debian-package" },
150 | { "def", "text/plain" },
151 | { "deploy", "application/octet-stream" },
152 | { "der", "application/x-x509-ca-cert" },
153 | { "dfac", "application/vnd.dreamfactory" },
154 | { "dic", "text/x-c" },
155 | { "dir", "application/x-director" },
156 | { "dis", "application/vnd.mobius.dis" },
157 | { "dist", "application/octet-stream" },
158 | { "distz", "application/octet-stream" },
159 | { "djv", "image/vnd.djvu" },
160 | { "djvu", "image/vnd.djvu" },
161 | { "dll", "application/x-msdownload" },
162 | { "dmg", "application/octet-stream" },
163 | { "dms", "application/octet-stream" },
164 | { "dna", "application/vnd.dna" },
165 | { "doc", "application/msword" },
166 | { "docm", "application/vnd.ms-word.document.macroenabled.12" },
167 | { "docx", "application/vnd.openxmlformats-officedocument.wordprocessingml.document" },
168 | { "dot", "application/msword" },
169 | { "dotm", "application/vnd.ms-word.template.macroenabled.12" },
170 | { "dotx", "application/vnd.openxmlformats-officedocument.wordprocessingml.template" },
171 | { "dp", "application/vnd.osgi.dp" },
172 | { "dpg", "application/vnd.dpgraph" },
173 | { "dra", "audio/vnd.dra" },
174 | { "dsc", "text/prs.lines.tag" },
175 | { "dssc", "application/dssc+der" },
176 | { "dtb", "application/x-dtbook+xml" },
177 | { "dtd", "application/xml-dtd" },
178 | { "dts", "audio/vnd.dts" },
179 | { "dtshd", "audio/vnd.dts.hd" },
180 | { "dump", "application/octet-stream" },
181 | { "dvi", "application/x-dvi" },
182 | { "dwf", "model/vnd.dwf" },
183 | { "dwg", "image/vnd.dwg" },
184 | { "dxf", "image/vnd.dxf" },
185 | { "dxp", "application/vnd.spotfire.dxp" },
186 | { "dxr", "application/x-director" },
187 | { "ecelp4800", "audio/vnd.nuera.ecelp4800" },
188 | { "ecelp7470", "audio/vnd.nuera.ecelp7470" },
189 | { "ecelp9600", "audio/vnd.nuera.ecelp9600" },
190 | { "ecma", "application/ecmascript" },
191 | { "edm", "application/vnd.novadigm.edm" },
192 | { "edx", "application/vnd.novadigm.edx" },
193 | { "efif", "application/vnd.picsel" },
194 | { "ei6", "application/vnd.pg.osasli" },
195 | { "elc", "application/octet-stream" },
196 | { "eml", "message/rfc822" },
197 | { "emma", "application/emma+xml" },
198 | { "eol", "audio/vnd.digital-winds" },
199 | { "eot", "application/vnd.ms-fontobject" },
200 | { "eps", "application/postscript" },
201 | { "epub", "application/epub+zip" },
202 | { "es3", "application/vnd.eszigno3+xml" },
203 | { "esf", "application/vnd.epson.esf" },
204 | { "et3", "application/vnd.eszigno3+xml" },
205 | { "etx", "text/x-setext" },
206 | { "exe", "application/x-msdownload" },
207 | { "exi", "application/exi" },
208 | { "ext", "application/vnd.novadigm.ext" },
209 | { "ez", "application/andrew-inset" },
210 | { "ez2", "application/vnd.ezpix-album" },
211 | { "ez3", "application/vnd.ezpix-package" },
212 | { "f", "text/x-fortran" },
213 | { "f4v", "video/x-f4v" },
214 | { "f77", "text/x-fortran" },
215 | { "f90", "text/x-fortran" },
216 | { "fbs", "image/vnd.fastbidsheet" },
217 | { "fcs", "application/vnd.isac.fcs" },
218 | { "fdf", "application/vnd.fdf" },
219 | { "fe_launch", "application/vnd.denovo.fcselayout-link" },
220 | { "fg5", "application/vnd.fujitsu.oasysgp" },
221 | { "fgd", "application/x-director" },
222 | { "fh", "image/x-freehand" },
223 | { "fh4", "image/x-freehand" },
224 | { "fh5", "image/x-freehand" },
225 | { "fh7", "image/x-freehand" },
226 | { "fhc", "image/x-freehand" },
227 | { "fig", "application/x-xfig" },
228 | { "fli", "video/x-fli" },
229 | { "flo", "application/vnd.micrografx.flo" },
230 | { "flv", "video/x-flv" },
231 | { "flw", "application/vnd.kde.kivio" },
232 | { "flx", "text/vnd.fmi.flexstor" },
233 | { "fly", "text/vnd.fly" },
234 | { "fm", "application/vnd.framemaker" },
235 | { "fnc", "application/vnd.frogans.fnc" },
236 | { "for", "text/x-fortran" },
237 | { "fpx", "image/vnd.fpx" },
238 | { "frame", "application/vnd.framemaker" },
239 | { "fsc", "application/vnd.fsc.weblaunch" },
240 | { "fst", "image/vnd.fst" },
241 | { "ftc", "application/vnd.fluxtime.clip" },
242 | { "fti", "application/vnd.anser-web-funds-transfer-initiation" },
243 | { "fvt", "video/vnd.fvt" },
244 | { "fxp", "application/vnd.adobe.fxp" },
245 | { "fxpl", "application/vnd.adobe.fxp" },
246 | { "fzs", "application/vnd.fuzzysheet" },
247 | { "g2w", "application/vnd.geoplan" },
248 | { "g3", "image/g3fax" },
249 | { "g3w", "application/vnd.geospace" },
250 | { "gac", "application/vnd.groove-account" },
251 | { "gdl", "model/vnd.gdl" },
252 | { "geo", "application/vnd.dynageo" },
253 | { "gex", "application/vnd.geometry-explorer" },
254 | { "ggb", "application/vnd.geogebra.file" },
255 | { "ggt", "application/vnd.geogebra.tool" },
256 | { "ghf", "application/vnd.groove-help" },
257 | { "gif", "image/gif" },
258 | { "gim", "application/vnd.groove-identity-message" },
259 | { "gmx", "application/vnd.gmx" },
260 | { "gnumeric", "application/x-gnumeric" },
261 | { "gph", "application/vnd.flographit" },
262 | { "gqf", "application/vnd.grafeq" },
263 | { "gqs", "application/vnd.grafeq" },
264 | { "gram", "application/srgs" },
265 | { "gre", "application/vnd.geometry-explorer" },
266 | { "grv", "application/vnd.groove-injector" },
267 | { "grxml", "application/srgs+xml" },
268 | { "gsf", "application/x-font-ghostscript" },
269 | { "gtar", "application/x-gtar" },
270 | { "gtm", "application/vnd.groove-tool-message" },
271 | { "gtw", "model/vnd.gtw" },
272 | { "gv", "text/vnd.graphviz" },
273 | { "gxt", "application/vnd.geonext" },
274 | { "h", "text/x-c" },
275 | { "h261", "video/h261" },
276 | { "h263", "video/h263" },
277 | { "h264", "video/h264" },
278 | { "hal", "application/vnd.hal+xml" },
279 | { "hbci", "application/vnd.hbci" },
280 | { "hdf", "application/x-hdf" },
281 | { "hh", "text/x-c" },
282 | { "hlp", "application/winhlp" },
283 | { "hpgl", "application/vnd.hp-hpgl" },
284 | { "hpid", "application/vnd.hp-hpid" },
285 | { "hps", "application/vnd.hp-hps" },
286 | { "hqx", "application/mac-binhex40" },
287 | { "htke", "application/vnd.kenameaapp" },
288 | { "htm", "text/html" },
289 | { "html", "text/html" },
290 | { "hvd", "application/vnd.yamaha.hv-dic" },
291 | { "hvp", "application/vnd.yamaha.hv-voice" },
292 | { "hvs", "application/vnd.yamaha.hv-script" },
293 | { "i2g", "application/vnd.intergeo" },
294 | { "icc", "application/vnd.iccprofile" },
295 | { "ice", "x-conference/x-cooltalk" },
296 | { "icm", "application/vnd.iccprofile" },
297 | { "ico", "image/x-icon" },
298 | { "ics", "text/calendar" },
299 | { "ief", "image/ief" },
300 | { "ifb", "text/calendar" },
301 | { "ifm", "application/vnd.shana.informed.formdata" },
302 | { "iges", "model/iges" },
303 | { "igl", "application/vnd.igloader" },
304 | { "igm", "application/vnd.insors.igm" },
305 | { "igs", "model/iges" },
306 | { "igx", "application/vnd.micrografx.igx" },
307 | { "iif", "application/vnd.shana.informed.interchange" },
308 | { "imp", "application/vnd.accpac.simply.imp" },
309 | { "ims", "application/vnd.ms-ims" },
310 | { "in", "text/plain" },
311 | { "ipfix", "application/ipfix" },
312 | { "ipk", "application/vnd.shana.informed.package" },
313 | { "irm", "application/vnd.ibm.rights-management" },
314 | { "irp", "application/vnd.irepository.package+xml" },
315 | { "iso", "application/octet-stream" },
316 | { "itp", "application/vnd.shana.informed.formtemplate" },
317 | { "ivp", "application/vnd.immervision-ivp" },
318 | { "ivu", "application/vnd.immervision-ivu" },
319 | { "jad", "text/vnd.sun.j2me.app-descriptor" },
320 | { "jam", "application/vnd.jam" },
321 | { "jar", "application/java-archive" },
322 | { "java", "text/x-java-source" },
323 | { "jisp", "application/vnd.jisp" },
324 | { "jlt", "application/vnd.hp-jlyt" },
325 | { "jnlp", "application/x-java-jnlp-file" },
326 | { "joda", "application/vnd.joost.joda-archive" },
327 | { "jpe", "image/jpeg" },
328 | { "jpeg", "image/jpeg" },
329 | { "jpg", "image/jpeg" },
330 | { "jpgm", "video/jpm" },
331 | { "jpgv", "video/jpeg" },
332 | { "jpm", "video/jpm" },
333 | { "js", "application/javascript" },
334 | { "json", "application/json" },
335 | { "kar", "audio/midi" },
336 | { "karbon", "application/vnd.kde.karbon" },
337 | { "kfo", "application/vnd.kde.kformula" },
338 | { "kia", "application/vnd.kidspiration" },
339 | { "kml", "application/vnd.google-earth.kml+xml" },
340 | { "kmz", "application/vnd.google-earth.kmz" },
341 | { "kne", "application/vnd.kinar" },
342 | { "knp", "application/vnd.kinar" },
343 | { "kon", "application/vnd.kde.kontour" },
344 | { "kpr", "application/vnd.kde.kpresenter" },
345 | { "kpt", "application/vnd.kde.kpresenter" },
346 | { "ksp", "application/vnd.kde.kspread" },
347 | { "ktr", "application/vnd.kahootz" },
348 | { "ktx", "image/ktx" },
349 | { "ktz", "application/vnd.kahootz" },
350 | { "kwd", "application/vnd.kde.kword" },
351 | { "kwt", "application/vnd.kde.kword" },
352 | { "lasxml", "application/vnd.las.las+xml" },
353 | { "latex", "application/x-latex" },
354 | { "lbd", "application/vnd.llamagraphics.life-balance.desktop" },
355 | { "lbe", "application/vnd.llamagraphics.life-balance.exchange+xml" },
356 | { "les", "application/vnd.hhe.lesson-player" },
357 | { "lha", "application/octet-stream" },
358 | { "link66", "application/vnd.route66.link66+xml" },
359 | { "list", "text/plain" },
360 | { "list3820", "application/vnd.ibm.modcap" },
361 | { "listafp", "application/vnd.ibm.modcap" },
362 | { "log", "text/plain" },
363 | { "lostxml", "application/lost+xml" },
364 | { "lrf", "application/octet-stream" },
365 | { "lrm", "application/vnd.ms-lrm" },
366 | { "ltf", "application/vnd.frogans.ltf" },
367 | { "lvp", "audio/vnd.lucent.voice" },
368 | { "lwp", "application/vnd.lotus-wordpro" },
369 | { "lzh", "application/octet-stream" },
370 | { "m13", "application/x-msmediaview" },
371 | { "m14", "application/x-msmediaview" },
372 | { "m1v", "video/mpeg" },
373 | { "m21", "application/mp21" },
374 | { "m2a", "audio/mpeg" },
375 | { "m2v", "video/mpeg" },
376 | { "m3a", "audio/mpeg" },
377 | { "m3u", "audio/x-mpegurl" },
378 | { "m3u8", "application/vnd.apple.mpegurl" },
379 | { "m4u", "video/vnd.mpegurl" },
380 | { "m4v", "video/x-m4v" },
381 | { "ma", "application/mathematica" },
382 | { "mads", "application/mads+xml" },
383 | { "mag", "application/vnd.ecowin.chart" },
384 | { "maker", "application/vnd.framemaker" },
385 | { "man", "text/troff" },
386 | { "mathml", "application/mathml+xml" },
387 | { "mb", "application/mathematica" },
388 | { "mbk", "application/vnd.mobius.mbk" },
389 | { "mbox", "application/mbox" },
390 | { "mc1", "application/vnd.medcalcdata" },
391 | { "mcd", "application/vnd.mcd" },
392 | { "mcurl", "text/vnd.curl.mcurl" },
393 | { "mdb", "application/x-msaccess" },
394 | { "mdi", "image/vnd.ms-modi" },
395 | { "me", "text/troff" },
396 | { "mesh", "model/mesh" },
397 | { "meta4", "application/metalink4+xml" },
398 | { "mets", "application/mets+xml" },
399 | { "mfm", "application/vnd.mfmp" },
400 | { "mgp", "application/vnd.osgeo.mapguide.package" },
401 | { "mgz", "application/vnd.proteus.magazine" },
402 | { "mid", "audio/midi" },
403 | { "midi", "audio/midi" },
404 | { "mif", "application/vnd.mif" },
405 | { "mime", "message/rfc822" },
406 | { "mj2", "video/mj2" },
407 | { "mjp2", "video/mj2" },
408 | { "mlp", "application/vnd.dolby.mlp" },
409 | { "mmd", "application/vnd.chipnuts.karaoke-mmd" },
410 | { "mmf", "application/vnd.smaf" },
411 | { "mmr", "image/vnd.fujixerox.edmics-mmr" },
412 | { "mny", "application/x-msmoney" },
413 | { "mobi", "application/x-mobipocket-ebook" },
414 | { "mods", "application/mods+xml" },
415 | { "mov", "video/quicktime" },
416 | { "movie", "video/x-sgi-movie" },
417 | { "mp2", "audio/mpeg" },
418 | { "mp21", "application/mp21" },
419 | { "mp2a", "audio/mpeg" },
420 | { "mp3", "audio/mpeg" },
421 | { "mp4", "video/mp4" },
422 | { "mp4a", "audio/mp4" },
423 | { "mp4s", "application/mp4" },
424 | { "mp4v", "video/mp4" },
425 | { "mpc", "application/vnd.mophun.certificate" },
426 | { "mpe", "video/mpeg" },
427 | { "mpeg", "video/mpeg" },
428 | { "mpg", "video/mpeg" },
429 | { "mpg4", "video/mp4" },
430 | { "mpga", "audio/mpeg" },
431 | { "mpkg", "application/vnd.apple.installer+xml" },
432 | { "mpm", "application/vnd.blueice.multipass" },
433 | { "mpn", "application/vnd.mophun.application" },
434 | { "mpp", "application/vnd.ms-project" },
435 | { "mpt", "application/vnd.ms-project" },
436 | { "mpy", "application/vnd.ibm.minipay" },
437 | { "mqy", "application/vnd.mobius.mqy" },
438 | { "mrc", "application/marc" },
439 | { "mrcx", "application/marcxml+xml" },
440 | { "ms", "text/troff" },
441 | { "mscml", "application/mediaservercontrol+xml" },
442 | { "mseed", "application/vnd.fdsn.mseed" },
443 | { "mseq", "application/vnd.mseq" },
444 | { "msf", "application/vnd.epson.msf" },
445 | { "msh", "model/mesh" },
446 | { "msi", "application/x-msdownload" },
447 | { "msl", "application/vnd.mobius.msl" },
448 | { "msty", "application/vnd.muvee.style" },
449 | { "mts", "model/vnd.mts" },
450 | { "mus", "application/vnd.musician" },
451 | { "musicxml", "application/vnd.recordare.musicxml+xml" },
452 | { "mvb", "application/x-msmediaview" },
453 | { "mwf", "application/vnd.mfer" },
454 | { "mxf", "application/mxf" },
455 | { "mxl", "application/vnd.recordare.musicxml" },
456 | { "mxml", "application/xv+xml" },
457 | { "mxs", "application/vnd.triscape.mxs" },
458 | { "mxu", "video/vnd.mpegurl" },
459 | { "n3", "text/n3" },
460 | { "nb", "application/mathematica" },
461 | { "nbp", "application/vnd.wolfram.player" },
462 | { "nc", "application/x-netcdf" },
463 | { "ncx", "application/x-dtbncx+xml" },
464 | { "n-gage", "application/vnd.nokia.n-gage.symbian.install" },
465 | { "ngdat", "application/vnd.nokia.n-gage.data" },
466 | { "nlu", "application/vnd.neurolanguage.nlu" },
467 | { "nml", "application/vnd.enliven" },
468 | { "nnd", "application/vnd.noblenet-directory" },
469 | { "nns", "application/vnd.noblenet-sealer" },
470 | { "nnw", "application/vnd.noblenet-web" },
471 | { "npx", "image/vnd.net-fpx" },
472 | { "nsf", "application/vnd.lotus-notes" },
473 | { "oa2", "application/vnd.fujitsu.oasys2" },
474 | { "oa3", "application/vnd.fujitsu.oasys3" },
475 | { "oas", "application/vnd.fujitsu.oasys" },
476 | { "obd", "application/x-msbinder" },
477 | { "oda", "application/oda" },
478 | { "odb", "application/vnd.oasis.opendocument.database" },
479 | { "odc", "application/vnd.oasis.opendocument.chart" },
480 | { "odf", "application/vnd.oasis.opendocument.formula" },
481 | { "odft", "application/vnd.oasis.opendocument.formula-template" },
482 | { "odg", "application/vnd.oasis.opendocument.graphics" },
483 | { "odi", "application/vnd.oasis.opendocument.image" },
484 | { "odm", "application/vnd.oasis.opendocument.text-master" },
485 | { "odp", "application/vnd.oasis.opendocument.presentation" },
486 | { "ods", "application/vnd.oasis.opendocument.spreadsheet" },
487 | { "odt", "application/vnd.oasis.opendocument.text" },
488 | { "oga", "audio/ogg" },
489 | { "ogg", "audio/ogg" },
490 | { "ogv", "video/ogg" },
491 | { "ogx", "application/ogg" },
492 | { "onepkg", "application/onenote" },
493 | { "onetmp", "application/onenote" },
494 | { "onetoc", "application/onenote" },
495 | { "onetoc2", "application/onenote" },
496 | { "opf", "application/oebps-package+xml" },
497 | { "oprc", "application/vnd.palm" },
498 | { "org", "application/vnd.lotus-organizer" },
499 | { "osf", "application/vnd.yamaha.openscoreformat" },
500 | { "osfpvg", "application/vnd.yamaha.openscoreformat.osfpvg+xml" },
501 | { "otc", "application/vnd.oasis.opendocument.chart-template" },
502 | { "otf", "application/x-font-otf" },
503 | { "otg", "application/vnd.oasis.opendocument.graphics-template" },
504 | { "oth", "application/vnd.oasis.opendocument.text-web" },
505 | { "oti", "application/vnd.oasis.opendocument.image-template" },
506 | { "otp", "application/vnd.oasis.opendocument.presentation-template" },
507 | { "ots", "application/vnd.oasis.opendocument.spreadsheet-template" },
508 | { "ott", "application/vnd.oasis.opendocument.text-template" },
509 | { "oxt", "application/vnd.openofficeorg.extension" },
510 | { "p", "text/x-pascal" },
511 | { "p10", "application/pkcs10" },
512 | { "p12", "application/x-pkcs12" },
513 | { "p7b", "application/x-pkcs7-certificates" },
514 | { "p7c", "application/pkcs7-mime" },
515 | { "p7m", "application/pkcs7-mime" },
516 | { "p7r", "application/x-pkcs7-certreqresp" },
517 | { "p7s", "application/pkcs7-signature" },
518 | { "p8", "application/pkcs8" },
519 | { "pas", "text/x-pascal" },
520 | { "paw", "application/vnd.pawaafile" },
521 | { "pbd", "application/vnd.powerbuilder6" },
522 | { "pbm", "image/x-portable-bitmap" },
523 | { "pcf", "application/x-font-pcf" },
524 | { "pcl", "application/vnd.hp-pcl" },
525 | { "pclxl", "application/vnd.hp-pclxl" },
526 | { "pct", "image/x-pict" },
527 | { "pcurl", "application/vnd.curl.pcurl" },
528 | { "pcx", "image/x-pcx" },
529 | { "pdb", "application/vnd.palm" },
530 | { "pdf", "application/pdf" },
531 | { "pfa", "application/x-font-type1" },
532 | { "pfb", "application/x-font-type1" },
533 | { "pfm", "application/x-font-type1" },
534 | { "pfr", "application/font-tdpfr" },
535 | { "pfx", "application/x-pkcs12" },
536 | { "pgm", "image/x-portable-graymap" },
537 | { "pgn", "application/x-chess-pgn" },
538 | { "pgp", "application/pgp-encrypted" },
539 | { "pic", "image/x-pict" },
540 | { "pkg", "application/octet-stream" },
541 | { "pki", "application/pkixcmp" },
542 | { "pkipath", "application/pkix-pkipath" },
543 | { "plb", "application/vnd.3gpp.pic-bw-large" },
544 | { "plc", "application/vnd.mobius.plc" },
545 | { "plf", "application/vnd.pocketlearn" },
546 | { "pls", "application/pls+xml" },
547 | { "pml", "application/vnd.ctc-posml" },
548 | { "png", "image/png" },
549 | { "pnm", "image/x-portable-anymap" },
550 | { "portpkg", "application/vnd.macports.portpkg" },
551 | { "pot", "application/vnd.ms-powerpoint" },
552 | { "potm", "application/vnd.ms-powerpoint.template.macroenabled.12" },
553 | { "potx", "application/vnd.openxmlformats-officedocument.presentationml.template" },
554 | { "ppam", "application/vnd.ms-powerpoint.addin.macroenabled.12" },
555 | { "ppd", "application/vnd.cups-ppd" },
556 | { "ppm", "image/x-portable-pixmap" },
557 | { "pps", "application/vnd.ms-powerpoint" },
558 | { "ppsm", "application/vnd.ms-powerpoint.slideshow.macroenabled.12" },
559 | { "ppsx", "application/vnd.openxmlformats-officedocument.presentationml.slideshow" },
560 | { "ppt", "application/vnd.ms-powerpoint" },
561 | { "pptm", "application/vnd.ms-powerpoint.presentation.macroenabled.12" },
562 | { "pptx", "application/vnd.openxmlformats-officedocument.presentationml.presentation" },
563 | { "pqa", "application/vnd.palm" },
564 | { "prc", "application/x-mobipocket-ebook" },
565 | { "pre", "application/vnd.lotus-freelance" },
566 | { "prf", "application/pics-rules" },
567 | { "ps", "application/postscript" },
568 | { "psb", "application/vnd.3gpp.pic-bw-small" },
569 | { "psd", "image/vnd.adobe.photoshop" },
570 | { "psf", "application/x-font-linux-psf" },
571 | { "pskcxml", "application/pskc+xml" },
572 | { "ptid", "application/vnd.pvi.ptid1" },
573 | { "pub", "application/x-mspublisher" },
574 | { "pvb", "application/vnd.3gpp.pic-bw-var" },
575 | { "pwn", "application/vnd.3m.post-it-notes" },
576 | { "pya", "audio/vnd.ms-playready.media.pya" },
577 | { "pyv", "video/vnd.ms-playready.media.pyv" },
578 | { "qam", "application/vnd.epson.quickanime" },
579 | { "qbo", "application/vnd.intu.qbo" },
580 | { "qfx", "application/vnd.intu.qfx" },
581 | { "qps", "application/vnd.publishare-delta-tree" },
582 | { "qt", "video/quicktime" },
583 | { "qwd", "application/vnd.quark.quarkxpress" },
584 | { "qwt", "application/vnd.quark.quarkxpress" },
585 | { "qxb", "application/vnd.quark.quarkxpress" },
586 | { "qxd", "application/vnd.quark.quarkxpress" },
587 | { "qxl", "application/vnd.quark.quarkxpress" },
588 | { "qxt", "application/vnd.quark.quarkxpress" },
589 | { "ra", "audio/x-pn-realaudio" },
590 | { "ram", "audio/x-pn-realaudio" },
591 | { "rar", "application/x-rar-compressed" },
592 | { "ras", "image/x-cmu-raster" },
593 | { "rcprofile", "application/vnd.ipunplugged.rcprofile" },
594 | { "rdf", "application/rdf+xml" },
595 | { "rdz", "application/vnd.data-vision.rdz" },
596 | { "rep", "application/vnd.businessobjects" },
597 | { "res", "application/x-dtbresource+xml" },
598 | { "rgb", "image/x-rgb" },
599 | { "rif", "application/reginfo+xml" },
600 | { "rip", "audio/vnd.rip" },
601 | { "rl", "application/resource-lists+xml" },
602 | { "rlc", "image/vnd.fujixerox.edmics-rlc" },
603 | { "rld", "application/resource-lists-diff+xml" },
604 | { "rm", "application/vnd.rn-realmedia" },
605 | { "rmi", "audio/midi" },
606 | { "rmp", "audio/x-pn-realaudio-plugin" },
607 | { "rms", "application/vnd.jcp.javame.midlet-rms" },
608 | { "rnc", "application/relax-ng-compact-syntax" },
609 | { "roff", "text/troff" },
610 | { "rp9", "application/vnd.cloanto.rp9" },
611 | { "rpss", "application/vnd.nokia.radio-presets" },
612 | { "rpst", "application/vnd.nokia.radio-preset" },
613 | { "rq", "application/sparql-query" },
614 | { "rs", "application/rls-services+xml" },
615 | { "rsd", "application/rsd+xml" },
616 | { "rss", "application/rss+xml" },
617 | { "rtf", "application/rtf" },
618 | { "rtx", "text/richtext" },
619 | { "s", "text/x-asm" },
620 | { "saf", "application/vnd.yamaha.smaf-audio" },
621 | { "sbml", "application/sbml+xml" },
622 | { "sc", "application/vnd.ibm.secure-container" },
623 | { "scd", "application/x-msschedule" },
624 | { "scm", "application/vnd.lotus-screencam" },
625 | { "scq", "application/scvp-cv-request" },
626 | { "scs", "application/scvp-cv-response" },
627 | { "scurl", "text/vnd.curl.scurl" },
628 | { "sda", "application/vnd.stardivision.draw" },
629 | { "sdc", "application/vnd.stardivision.calc" },
630 | { "sdd", "application/vnd.stardivision.impress" },
631 | { "sdkd", "application/vnd.solent.sdkm+xml" },
632 | { "sdkm", "application/vnd.solent.sdkm+xml" },
633 | { "sdp", "application/sdp" },
634 | { "sdw", "application/vnd.stardivision.writer" },
635 | { "see", "application/vnd.seemail" },
636 | { "seed", "application/vnd.fdsn.seed" },
637 | { "sema", "application/vnd.sema" },
638 | { "semd", "application/vnd.semd" },
639 | { "semf", "application/vnd.semf" },
640 | { "ser", "application/java-serialized-object" },
641 | { "setpay", "application/set-payment-initiation" },
642 | { "setreg", "application/set-registration-initiation" },
643 | { "sfd-hdstx", "application/vnd.hydrostatix.sof-data" },
644 | { "sfs", "application/vnd.spotfire.sfs" },
645 | { "sgl", "application/vnd.stardivision.writer-global" },
646 | { "sgm", "text/sgml" },
647 | { "sgml", "text/sgml" },
648 | { "sh", "application/x-sh" },
649 | { "shar", "application/x-shar" },
650 | { "shf", "application/shf+xml" },
651 | { "sig", "application/pgp-signature" },
652 | { "silo", "model/mesh" },
653 | { "sis", "application/vnd.symbian.install" },
654 | { "sisx", "application/vnd.symbian.install" },
655 | { "sit", "application/x-stuffit" },
656 | { "sitx", "application/x-stuffitx" },
657 | { "skd", "application/vnd.koan" },
658 | { "skm", "application/vnd.koan" },
659 | { "skp", "application/vnd.koan" },
660 | { "skt", "application/vnd.koan" },
661 | { "sldm", "application/vnd.ms-powerpoint.slide.macroenabled.12" },
662 | { "sldx", "application/vnd.openxmlformats-officedocument.presentationml.slide" },
663 | { "slt", "application/vnd.epson.salt" },
664 | { "sm", "application/vnd.stepmania.stepchart" },
665 | { "smf", "application/vnd.stardivision.math" },
666 | { "smi", "application/smil+xml" },
667 | { "smil", "application/smil+xml" },
668 | { "snd", "audio/basic" },
669 | { "snf", "application/x-font-snf" },
670 | { "so", "application/octet-stream" },
671 | { "spc", "application/x-pkcs7-certificates" },
672 | { "spf", "application/vnd.yamaha.smaf-phrase" },
673 | { "spl", "application/x-futuresplash" },
674 | { "spot", "text/vnd.in3d.spot" },
675 | { "spp", "application/scvp-vp-response" },
676 | { "spq", "application/scvp-vp-request" },
677 | { "spx", "audio/ogg" },
678 | { "src", "application/x-wais-source" },
679 | { "sru", "application/sru+xml" },
680 | { "srx", "application/sparql-results+xml" },
681 | { "sse", "application/vnd.kodak-descriptor" },
682 | { "ssf", "application/vnd.epson.ssf" },
683 | { "ssml", "application/ssml+xml" },
684 | { "st", "application/vnd.sailingtracker.track" },
685 | { "stc", "application/vnd.sun.xml.calc.template" },
686 | { "std", "application/vnd.sun.xml.draw.template" },
687 | { "stf", "application/vnd.wt.stf" },
688 | { "sti", "application/vnd.sun.xml.impress.template" },
689 | { "stk", "application/hyperstudio" },
690 | { "stl", "application/vnd.ms-pki.stl" },
691 | { "str", "application/vnd.pg.format" },
692 | { "stw", "application/vnd.sun.xml.writer.template" },
693 | { "sub", "image/vnd.dvb.subtitle" },
694 | { "sus", "application/vnd.sus-calendar" },
695 | { "susp", "application/vnd.sus-calendar" },
696 | { "sv4cpio", "application/x-sv4cpio" },
697 | { "sv4crc", "application/x-sv4crc" },
698 | { "svc", "application/vnd.dvb.service" },
699 | { "svd", "application/vnd.svd" },
700 | { "svg", "image/svg+xml" },
701 | { "svgz", "image/svg+xml" },
702 | { "swa", "application/x-director" },
703 | { "swf", "application/x-shockwave-flash" },
704 | { "swi", "application/vnd.aristanetworks.swi" },
705 | { "sxc", "application/vnd.sun.xml.calc" },
706 | { "sxd", "application/vnd.sun.xml.draw" },
707 | { "sxg", "application/vnd.sun.xml.writer.global" },
708 | { "sxi", "application/vnd.sun.xml.impress" },
709 | { "sxm", "application/vnd.sun.xml.math" },
710 | { "sxw", "application/vnd.sun.xml.writer" },
711 | { "t", "text/troff" },
712 | { "tao", "application/vnd.tao.intent-module-archive" },
713 | { "tar", "application/x-tar" },
714 | { "tcap", "application/vnd.3gpp2.tcap" },
715 | { "tcl", "application/x-tcl" },
716 | { "teacher", "application/vnd.smart.teacher" },
717 | { "tei", "application/tei+xml" },
718 | { "teicorpus", "application/tei+xml" },
719 | { "tex", "application/x-tex" },
720 | { "texi", "application/x-texinfo" },
721 | { "texinfo", "application/x-texinfo" },
722 | { "text", "text/plain" },
723 | { "tfi", "application/thraud+xml" },
724 | { "tfm", "application/x-tex-tfm" },
725 | { "thmx", "application/vnd.ms-officetheme" },
726 | { "tif", "image/tiff" },
727 | { "tiff", "image/tiff" },
728 | { "tmo", "application/vnd.tmobile-livetv" },
729 | { "torrent", "application/x-bittorrent" },
730 | { "tpl", "application/vnd.groove-tool-template" },
731 | { "tpt", "application/vnd.trid.tpt" },
732 | { "tr", "text/troff" },
733 | { "tra", "application/vnd.trueapp" },
734 | { "trm", "application/x-msterminal" },
735 | { "tsd", "application/timestamped-data" },
736 | { "tsv", "text/tab-separated-values" },
737 | { "ttc", "application/x-font-ttf" },
738 | { "ttf", "application/x-font-ttf" },
739 | { "ttl", "text/turtle" },
740 | { "twd", "application/vnd.simtech-mindmapper" },
741 | { "twds", "application/vnd.simtech-mindmapper" },
742 | { "txd", "application/vnd.genomatix.tuxedo" },
743 | { "txf", "application/vnd.mobius.txf" },
744 | { "txt", "text/plain" },
745 | { "u32", "application/x-authorware-bin" },
746 | { "udeb", "application/x-debian-package" },
747 | { "ufd", "application/vnd.ufdl" },
748 | { "ufdl", "application/vnd.ufdl" },
749 | { "umj", "application/vnd.umajin" },
750 | { "unityweb", "application/vnd.unity" },
751 | { "uoml", "application/vnd.uoml+xml" },
752 | { "uri", "text/uri-list" },
753 | { "uris", "text/uri-list" },
754 | { "urls", "text/uri-list" },
755 | { "ustar", "application/x-ustar" },
756 | { "utz", "application/vnd.uiq.theme" },
757 | { "uu", "text/x-uuencode" },
758 | { "uva", "audio/vnd.dece.audio" },
759 | { "uvd", "application/vnd.dece.data" },
760 | { "uvf", "application/vnd.dece.data" },
761 | { "uvg", "image/vnd.dece.graphic" },
762 | { "uvh", "video/vnd.dece.hd" },
763 | { "uvi", "image/vnd.dece.graphic" },
764 | { "uvm", "video/vnd.dece.mobile" },
765 | { "uvp", "video/vnd.dece.pd" },
766 | { "uvs", "video/vnd.dece.sd" },
767 | { "uvt", "application/vnd.dece.ttml+xml" },
768 | { "uvu", "video/vnd.uvvu.mp4" },
769 | { "uvv", "video/vnd.dece.video" },
770 | { "uvva", "audio/vnd.dece.audio" },
771 | { "uvvd", "application/vnd.dece.data" },
772 | { "uvvf", "application/vnd.dece.data" },
773 | { "uvvg", "image/vnd.dece.graphic" },
774 | { "uvvh", "video/vnd.dece.hd" },
775 | { "uvvi", "image/vnd.dece.graphic" },
776 | { "uvvm", "video/vnd.dece.mobile" },
777 | { "uvvp", "video/vnd.dece.pd" },
778 | { "uvvs", "video/vnd.dece.sd" },
779 | { "uvvt", "application/vnd.dece.ttml+xml" },
780 | { "uvvu", "video/vnd.uvvu.mp4" },
781 | { "uvvv", "video/vnd.dece.video" },
782 | { "uvvx", "application/vnd.dece.unspecified" },
783 | { "uvx", "application/vnd.dece.unspecified" },
784 | { "vcd", "application/x-cdlink" },
785 | { "vcf", "text/x-vcard" },
786 | { "vcg", "application/vnd.groove-vcard" },
787 | { "vcs", "text/x-vcalendar" },
788 | { "vcx", "application/vnd.vcx" },
789 | { "vis", "application/vnd.visionary" },
790 | { "viv", "video/vnd.vivo" },
791 | { "vor", "application/vnd.stardivision.writer" },
792 | { "vox", "application/x-authorware-bin" },
793 | { "vrml", "model/vrml" },
794 | { "vsd", "application/vnd.visio" },
795 | { "vsf", "application/vnd.vsf" },
796 | { "vss", "application/vnd.visio" },
797 | { "vst", "application/vnd.visio" },
798 | { "vsw", "application/vnd.visio" },
799 | { "vtu", "model/vnd.vtu" },
800 | { "vxml", "application/voicexml+xml" },
801 | { "w3d", "application/x-director" },
802 | { "wad", "application/x-doom" },
803 | { "wav", "audio/x-wav" },
804 | { "wax", "audio/x-ms-wax" },
805 | { "wbmp", "image/vnd.wap.wbmp" },
806 | { "wbs", "application/vnd.criticaltools.wbs+xml" },
807 | { "wbxml", "application/vnd.wap.wbxml" },
808 | { "wcm", "application/vnd.ms-works" },
809 | { "wdb", "application/vnd.ms-works" },
810 | { "weba", "audio/webm" },
811 | { "webm", "video/webm" },
812 | { "webp", "image/webp" },
813 | { "wg", "application/vnd.pmi.widget" },
814 | { "wgt", "application/widget" },
815 | { "wks", "application/vnd.ms-works" },
816 | { "wm", "video/x-ms-wm" },
817 | { "wma", "audio/x-ms-wma" },
818 | { "wmd", "application/x-ms-wmd" },
819 | { "wmf", "application/x-msmetafile" },
820 | { "wml", "text/vnd.wap.wml" },
821 | { "wmlc", "application/vnd.wap.wmlc" },
822 | { "wmls", "text/vnd.wap.wmlscript" },
823 | { "wmlsc", "application/vnd.wap.wmlscriptc" },
824 | { "wmv", "video/x-ms-wmv" },
825 | { "wmx", "video/x-ms-wmx" },
826 | { "wmz", "application/x-ms-wmz" },
827 | { "woff", "application/x-font-woff" },
828 | { "wpd", "application/vnd.wordperfect" },
829 | { "wpl", "application/vnd.ms-wpl" },
830 | { "wps", "application/vnd.ms-works" },
831 | { "wqd", "application/vnd.wqd" },
832 | { "wri", "application/x-mswrite" },
833 | { "wrl", "model/vrml" },
834 | { "wsdl", "application/wsdl+xml" },
835 | { "wspolicy", "application/wspolicy+xml" },
836 | { "wtb", "application/vnd.webturbo" },
837 | { "wvx", "video/x-ms-wvx" },
838 | { "x32", "application/x-authorware-bin" },
839 | { "x3d", "application/vnd.hzn-3d-crossword" },
840 | { "xap", "application/x-silverlight-app" },
841 | { "xar", "application/vnd.xara" },
842 | { "xbap", "application/x-ms-xbap" },
843 | { "xbd", "application/vnd.fujixerox.docuworks.binder" },
844 | { "xbm", "image/x-xbitmap" },
845 | { "xdf", "application/xcap-diff+xml" },
846 | { "xdm", "application/vnd.syncml.dm+xml" },
847 | { "xdp", "application/vnd.adobe.xdp+xml" },
848 | { "xdssc", "application/dssc+xml" },
849 | { "xdw", "application/vnd.fujixerox.docuworks" },
850 | { "xenc", "application/xenc+xml" },
851 | { "xer", "application/patch-ops-error+xml" },
852 | { "xfdf", "application/vnd.adobe.xfdf" },
853 | { "xfdl", "application/vnd.xfdl" },
854 | { "xht", "application/xhtml+xml" },
855 | { "xhtml", "application/xhtml+xml" },
856 | { "xhvml", "application/xv+xml" },
857 | { "xif", "image/vnd.xiff" },
858 | { "xla", "application/vnd.ms-excel" },
859 | { "xlam", "application/vnd.ms-excel.addin.macroenabled.12" },
860 | { "xlc", "application/vnd.ms-excel" },
861 | { "xlm", "application/vnd.ms-excel" },
862 | { "xls", "application/vnd.ms-excel" },
863 | { "xlsb", "application/vnd.ms-excel.sheet.binary.macroenabled.12" },
864 | { "xlsm", "application/vnd.ms-excel.sheet.macroenabled.12" },
865 | { "xlsx", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" },
866 | { "xlt", "application/vnd.ms-excel" },
867 | { "xltm", "application/vnd.ms-excel.template.macroenabled.12" },
868 | { "xltx", "application/vnd.openxmlformats-officedocument.spreadsheetml.template" },
869 | { "xlw", "application/vnd.ms-excel" },
870 | { "xml", "application/xml" },
871 | { "xo", "application/vnd.olpc-sugar" },
872 | { "xop", "application/xop+xml" },
873 | { "xpi", "application/x-xpinstall" },
874 | { "xpm", "image/x-xpixmap" },
875 | { "xpr", "application/vnd.is-xpr" },
876 | { "xps", "application/vnd.ms-xpsdocument" },
877 | { "xpw", "application/vnd.intercon.formnet" },
878 | { "xpx", "application/vnd.intercon.formnet" },
879 | { "xsl", "application/xml" },
880 | { "xslt", "application/xslt+xml" },
881 | { "xsm", "application/vnd.syncml+xml" },
882 | { "xspf", "application/xspf+xml" },
883 | { "xul", "application/vnd.mozilla.xul+xml" },
884 | { "xvm", "application/xv+xml" },
885 | { "xvml", "application/xv+xml" },
886 | { "xwd", "image/x-xwindowdump" },
887 | { "xyz", "chemical/x-xyz" },
888 | { "yang", "application/yang" },
889 | { "yin", "application/yin+xml" },
890 | { "zaz", "application/vnd.zzazz.deck+xml" },
891 | { "zip", "application/zip" },
892 | { "zir", "application/vnd.zul" },
893 | { "zirz", "application/vnd.zul" },
894 | { "zmm", "application/vnd.handheld-entertainment+xml" },
895 | };
896 |
897 | public static Dictionary MimeTypes
898 | {
899 | get { return _mimeTypes; }
900 | }
901 |
902 | public static String GetMime(String extension)
903 | {
904 | extension = extension.Replace(".", "");
905 | extension = extension.ToLower();
906 | if (_mimeTypes.Count(obj => obj.Key == extension) == 1)
907 | {
908 | return _mimeTypes.FirstOrDefault(obj => obj.Key == extension).Value;
909 | }
910 | return "text/plain";
911 | }
912 | }
913 | }
--------------------------------------------------------------------------------