diff --git a/CodeSmith/CSharp/ApiGenerate.cst b/CodeSmith/CSharp/ApiGenerate.cst new file mode 100644 index 0000000..cc7834e --- /dev/null +++ b/CodeSmith/CSharp/ApiGenerate.cst @@ -0,0 +1,226 @@ +<%-- +Author: yubaolee +Description: 用于生成OpenAuth.WebApi接口相关代码,包括controller/app/实体/dbcontext +--%> +<%@ Template Language="C#" TargetLanguage="Text" Debug="True" OutputType="Normal" %> + +<%@ Assembly Name="SchemaExplorer" %> +<%@ Assembly Name="CodeSmith.CustomProperties" %> + +<%@ Assembly Name="Mono.Cecil" Path="..\Common" %> +<%@ Assembly Name="ICSharpCode.NRefactory" Path="..\Common" %> +<%@ Assembly Name="ICSharpCode.NRefactory.CSharp" Path="..\Common" %> + +<%@ Assembly Src="Internal\Model.cs" %> +<%@ Assembly Src="Internal\Extensions.cs" %> +<%@ Assembly Src="Internal\Generator.cs" %> +<%@ Assembly Src="Internal\Parser.cs" %> + +<%@ Import Namespace="System.Collections.Generic" %> +<%@ Import Namespace="System.IO" %> +<%@ Import Namespace="System.Linq" %> +<%@ Import Namespace="System.Text" %> +<%@ Import Namespace="System.Text.RegularExpressions" %> +<%@ Import Namespace="System.Diagnostics" %> + +<%@ Import Namespace="SchemaMapper" %> + +<%@ Property Name="WholeDb" +Type="System.Boolean" +Category="1.Database" +Default="true" +Description="是否直接生成选定数据库中的所有表" %> + +<%@ Property Name="HeaderModel" +Type="System.Boolean" +Category="1.Database" +Default="true" +Description="是否为启用头表模式,即类似‘入库订单’界面" %> + +<%@ Property Name="SourceDatabase" + Type="SchemaExplorer.DatabaseSchema" + Category="1.Database" + Description="The source database." %> + +<%@ Property Name="SourceTables" +Type="SchemaExplorer.TableSchemaCollection" +Category="1.Database" Description="可以选择一个或多个表(使用Ctrl键)" %> + +<%@ Property Name="directory" + Type="System.String" + Default=".\" + Optional="True" + Description="代码生成路径" + Editor="System.Windows.Forms.Design.FolderNameEditor, System.Design, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" %> + +<%@ Property Name="ContextNamespace" + Type="System.String" + Category="2.Class" + Default="OpenAuth.Repository" + OnChanged="OnContextNamespaceChanged" + Description="DbContext默认命名空间,尽量不要更改"%> +<%@ Property Name="EntityNamespace" + Type="System.String" + Default="OpenAuth.Repository.Domain" + Category="2.Class" + Description="实体默认命名空间,尽量不要更改"%> + + +<%@ Register Name="EntityGeneratedClass" + Template="Internal\Entity.Generated.cst" + MergeProperties="False" %> + +<%@ Register Name="ContextGeneratedClass" + Template="Internal\Context.Generated.cst" + MergeProperties="True" %> + +<%@ Register Name="ApplicationGenerateClass" + Template="ApiGenerate\Application.cst" + MergeProperties="False" %> +<%@ Register Name="RequestGenerateClass" + Template="ApiGenerate\Request.cst" + MergeProperties="False" %> +<%@ Register Name="ModifyReqGenerateClass" + Template="ApiGenerate\ModifyReq.cst" + MergeProperties="False" %> +<%@ Register Name="ControllerGenerateClass" + Template="ApiGenerate\Controller.cst" + MergeProperties="False" %> + +开始创建OpenAuth.Core WebApi相关代码 ... +<% Generate(); %> + + \ No newline at end of file diff --git a/CodeSmith/CSharp/ApiGenerate/Application.cst b/CodeSmith/CSharp/ApiGenerate/Application.cst new file mode 100644 index 0000000..9bcc9ed --- /dev/null +++ b/CodeSmith/CSharp/ApiGenerate/Application.cst @@ -0,0 +1,157 @@ +<%-- +Name: Database Table Properties +Author: yubaolee +Description: Create a list of properties from a database table +--%> +<%@ CodeTemplate Language="C#" Encoding="utf-8" TargetLanguage="C#" Debug="True" Description="应用层" %> +<%@ Map Name="CSharpAlias" Src="System-CSharpAlias" Description="System to C# Type Map" %> +<%@ Assembly Name="SchemaExplorer" %> +<%@ Import Namespace="SchemaExplorer" %> +<%@ Property Name="Table" + Type="SchemaExplorer.TableSchema" %> +<%@ Property Name="HeaderModel" +Type="System.Boolean" +Category="1.Database" +Default="true" +Description="是否为启用头表模式,即类似‘入库订单’界面" %> +<%@ Assembly Src="../Web/Util.cs" %> +<%@ Import Namespace="Util" %> +using System; +using System.Linq; +using Infrastructure; +using OpenAuth.App.Interface; +using OpenAuth.App.Request; +using OpenAuth.App.Response; +using OpenAuth.Repository; +using OpenAuth.Repository.Domain; +using OpenAuth.Repository.Interface; +namespace OpenAuth.App +{ + public class <%=Table.Name%>App : BaseStringApp<<%=Table.Name%>, OpenAuthDBContext> + { + private RevelanceManagerApp _revelanceApp; + /// + /// 加载列表 + /// + public TableData Load(Query<%=Table.Name%>ListReq request) + { + var loginContext = _auth.GetCurrentUser(); + if (loginContext == null) + { + throw new CommonException("登录已过期", Define.INVALID_TOKEN); + } + //如果是WebAPI,请务必改为loginContext.GetTableColumns("<%=Table.Name%>"); + var columnFields = loginContext.GetTableColumnsFromDb("<%=Table.Name%>"); + if (columnFields == null || columnFields.Count == 0) + { + throw new Exception("请在代码生成界面配置Category表的字段属性"); + } + var result = new TableData(); + var objs = UnitWork.Find<<%=Table.Name%>>(null); + if (!string.IsNullOrEmpty(request.key)) + { + objs = objs.Where(u => u.Id.Contains(request.key)); + } + var propertyStr = string.Join(',', columnFields.Select(u =>u.ColumnName)); + result.columnFields = columnFields; + result.data = objs.OrderBy(u => u.Id) + .Skip((request.page - 1) * request.limit) + .Take(request.limit).Select($"new ({propertyStr})"); + result.count = objs.Count(); + return result; + } + <% + if(Table.Name.Contains("Tbl") && (!Table.Name.Contains("Dtbl")) && this.HeaderModel){ + var dtblName = Table.Name.Replace("Tbl","Dtbl"); //明细表的表名 + %> + public void Add(AddOrUpdate<%=Table.Name%>Req req) + { + var obj = req.MapTo<<%=Table.Name%>>(); + //todo:根据自己的业务场景,补充或调整字段 + obj.CreateTime = DateTime.Now; + var user = _auth.GetCurrentUser().User; + obj.CreateUserId = user.Id; + obj.CreateUserName = user.Name; + UnitWork.Add(obj); + if (req.<%=dtblName%>Reqs != null && req.<%=dtblName%>Reqs.Any()) + { + foreach (var detail in req.<%=dtblName%>Reqs) + { + detail.ForeignKeyId = obj.Id; //todo:调整自己的明细表外键 + _<%=dtblName%>App.AddNoSave(detail); + } + } + UnitWork.Save(); + } + public void Update(AddOrUpdate<%=Table.Name%>Req obj) + { + var user = _auth.GetCurrentUser().User; + if (obj.<%=dtblName%>Reqs != null && obj.<%=dtblName%>Reqs.Any()) + { + //id为空的添加 + foreach (var detail in obj.<%=dtblName%>Reqs.Where(u =>string.IsNullOrEmpty(u.Id))) + { + detail.ForeignKeyId = obj.Id; //todo:调整自己的明细表外键 + _<%=dtblName%>App.AddNoSave(detail); + } + //id比数据库少的,删除 + var containids = obj.<%=dtblName%>Reqs.Select(u => u.Id) + .Where(u =>!string.IsNullOrEmpty(u)).ToList(); + if (containids.Any()) + { + UnitWork.Delete<<%=dtblName%>>(u =>(!containids.Contains(u.Id)) && u.ForeignKeyId == obj.Id); //todo:调整自己的明细表外键 + } + //更新id相同的 + foreach (var detail in obj.<%=dtblName%>Reqs.Where(u =>!string.IsNullOrEmpty(u.Id))) + { + _<%=dtblName%>App.Update(detail); + } + } + <%CreateUpdate();%> + UnitWork.Save(); + } + <% + }else{ %> + public void Add(AddOrUpdate<%=Table.Name%>Req req) + { + var obj = req.MapTo<<%=Table.Name%>>(); + //todo:根据自己的业务场景,补充或调整字段 + //比如:obj.CreateTime = DateTime.Now; + // var user = _auth.GetCurrentUser().User; + // obj.CreateUserId = user.Id; + // obj.CreateUserName = user.Name; + Repository.Add(obj); + } + public void Update(AddOrUpdate<%=Table.Name%>Req obj) + { + <%CreateUpdate();%> + } + <% + } + %> + public <%=Table.Name%>App(IUnitWork unitWork, IRepository<<%=Table.Name%>, OpenAuthDBContext> repository, + RevelanceManagerApp app, IAuth auth) : base(unitWork, repository,auth) + { + _revelanceApp = app; + } + } +} + \ No newline at end of file diff --git a/CodeSmith/CSharp/ApiGenerate/Controller.cst b/CodeSmith/CSharp/ApiGenerate/Controller.cst new file mode 100644 index 0000000..30119a0 --- /dev/null +++ b/CodeSmith/CSharp/ApiGenerate/Controller.cst @@ -0,0 +1,128 @@ +<%-- +Name: Database Table Properties +Author: yubaolee +Description: Create a list of properties from a database table +--%> +<%@ CodeTemplate Language="C#" Encoding="utf-8" TargetLanguage="C#" Debug="True" Description="控制器" %> +<%@ Property Name="ModuleName" Type="String" Category="Context" Description="模块名称" %> +<%@ Map Name="CSharpAlias" Src="System-CSharpAlias" Description="System to C# Type Map" %> +<%@ Assembly Name="SchemaExplorer" %> +<%@ Import Namespace="SchemaExplorer" %> + +using System; +using Infrastructure; +using Microsoft.AspNetCore.Mvc; +using OpenAuth.App; +using OpenAuth.App.Request; +using OpenAuth.App.Response; +using OpenAuth.Repository.Domain; + +namespace OpenAuth.WebApi.Controllers +{ + /// + /// <%=ModuleName%>操作 + /// + [Route("api/[controller]/[action]")] + [ApiController] + public class <%=ModuleName%>sController : ControllerBase + { + private readonly <%=ModuleName%>App _app; + + /// + /// //获取详情 + /// + [HttpGet] + public Response<<%=ModuleName%>> Get(string id) + { + var result = new Response<<%=ModuleName%>>(); + try + { + result.Result = _app.Get(id); + } + catch (Exception ex) + { + result.Code = 500; + result.Message = ex.InnerException?.Message ?? ex.Message; + } + + return result; + } + + /// + /// 添加 + /// + [HttpPost] + public Response Add(AddOrUpdate<%=ModuleName%>Req obj) + { + var result = new Response(); + try + { + _app.Add(obj); + + } + catch (Exception ex) + { + result.Code = 500; + result.Message = ex.InnerException?.Message ?? ex.Message; + } + + return result; + } + + /// + /// 修改 + /// + [HttpPost] + public Response Update(AddOrUpdate<%=ModuleName%>Req obj) + { + var result = new Response(); + try + { + _app.Update(obj); + + } + catch (Exception ex) + { + result.Code = 500; + result.Message = ex.InnerException?.Message ?? ex.Message; + } + + return result; + } + + /// + /// 加载列表 + /// + [HttpGet] + public TableData Load([FromQuery]Query<%=ModuleName%>ListReq request) + { + return _app.Load(request); + } + + /// + /// 批量删除 + /// + [HttpPost] + public Response Delete([FromBody]string[] ids) + { + var result = new Response(); + try + { + _app.Delete(ids); + + } + catch (Exception ex) + { + result.Code = 500; + result.Message = ex.InnerException?.Message ?? ex.Message; + } + + return result; + } + + public <%=ModuleName%>sController(<%=ModuleName%>App app) + { + _app = app; + } + } +} diff --git a/CodeSmith/CSharp/ApiGenerate/ModifyReq.cst b/CodeSmith/CSharp/ApiGenerate/ModifyReq.cst new file mode 100644 index 0000000..34bbb56 --- /dev/null +++ b/CodeSmith/CSharp/ApiGenerate/ModifyReq.cst @@ -0,0 +1,66 @@ +<%@ Template Language="C#" TargetLanguage="C#" Debug="True" Encoding="UTF-8" %> + +<%@ Assembly Src="../Internal/Model.cs" %> +<%@ Assembly Src="../Internal/Extensions.cs" %> + +<%@ Import Namespace="System.Collections.Generic" %> +<%@ Import Namespace="System.Linq" %> +<%@ Import Namespace="System.Text" %> +<%@ Import Namespace="System.Text.RegularExpressions" %> + +<%@ Import Namespace="SchemaMapper" %> + +<%@ Property Name="Table" + Type="SchemaExplorer.TableSchema" %> + +<%@ Property Name="HeaderModel" +Type="System.Boolean" +Category="1.Database" +Default="true" +Description="是否为启用头表模式,即类似‘入库订单’界面" %> + +<%@ Property Name="EntityNamespace" + Type="System.String" %> +//------------------------------------------------------------------------------ +// +// This code was generated by a CodeSmith Template. +// +// DO NOT MODIFY contents of this file. Changes to this +// file will be lost if the code is regenerated. +// Author:Yubao Li +// +//------------------------------------------------------------------------------ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.ComponentModel.DataAnnotations.Schema; +using System.Text; +using OpenAuth.Repository.Core; + +namespace OpenAuth.App.Request +{ + /// + /// <%= Table.Description %> + /// + [Table("<%= Table.Name%>")] + public partial class AddOrUpdate<%= Table.Name %>Req + { + + <% foreach(ColumnSchema p in Table.Columns) { + + %> + /// + /// <%=p.Description %> + /// + public <%= p.SystemType.ToNullableType(p.AllowDBNull == true) %> <%= p.Name%> { get; set; } + <% } %> + + //todo:根据自己的业务场景添加需要的字段 + <% + if(Table.Name.Contains("Tbl") && (!Table.Name.Contains("Dtbl")) && this.HeaderModel){ + var dtblName = Table.Name.Replace("Tbl","Dtbl"); //明细表的表名 + %> + public ListReq> <%=dtblName%>Reqs { get; set; } + <% } %> + } +} \ No newline at end of file diff --git a/CodeSmith/CSharp/ApiGenerate/Request.cst b/CodeSmith/CSharp/ApiGenerate/Request.cst new file mode 100644 index 0000000..6b9b70e --- /dev/null +++ b/CodeSmith/CSharp/ApiGenerate/Request.cst @@ -0,0 +1,17 @@ +<%-- +Name: Database Table Properties +Author: yubaolee +Description: Create a list of properties from a database table +--%> +<%@ CodeTemplate Language="C#" Encoding="utf-8" TargetLanguage="C#" Debug="False" Description="应用层" %> +<%@ Property Name="ModuleName" Type="String" Category="Context" Description="模块名称" %> +<%@ Map Name="CSharpAlias" Src="System-CSharpAlias" Description="System to C# Type Map" %> +<%@ Assembly Name="SchemaExplorer" %> +<%@ Import Namespace="SchemaExplorer" %> +namespace OpenAuth.App.Request +{ + public class Query<%=ModuleName%>ListReq : PageReq + { + //todo:根据自己的业务场景添加需要的字段 + } +} \ No newline at end of file diff --git a/CodeSmith/CSharp/Internal/Context.Generated.cst b/CodeSmith/CSharp/Internal/Context.Generated.cst new file mode 100644 index 0000000..eddb09d --- /dev/null +++ b/CodeSmith/CSharp/Internal/Context.Generated.cst @@ -0,0 +1,81 @@ +<%@ Template Language="C#" TargetLanguage="C#" Debug="True" Encoding="UTF-8" %> + +<%@ Assembly Src="../Internal/Model.cs" %> +<%@ Assembly Src="../Internal/Extensions.cs" %> + +<%@ Import Namespace="System.Collections.Generic" %> +<%@ Import Namespace="System.Linq" %> +<%@ Import Namespace="System.Text" %> +<%@ Import Namespace="System.Text.RegularExpressions" %> + +<%@ Import Namespace="SchemaMapper" %> + +<%@ Property Name="WholeDb" +Type="System.Boolean" +Category="1.Database" +Default="true" +Description="是否为整个数据库" %> + +<%@ Property Name="SourceDatabase" + Type="SchemaExplorer.DatabaseSchema" + Category="1.Database" + Description="The source database." %> + +<%@ Property Name="SourceTables" +Type="SchemaExplorer.TableSchemaCollection" +Category="1.Database" Description="选择部分表" %> + +<%@ Property Name="ContextNamespace" Type="System.String" %> +<%@ Property Name="EntityNamespace" Type="System.String" %> + + +using Microsoft.EntityFrameworkCore; +using <%= EntityNamespace %>; + +namespace <%= ContextNamespace %> +{ + <% + string dbContextName; + if(WholeDb){ + dbContextName = SourceDatabase.Name.ToSafeName(); + } + else{ + dbContextName = SourceTables.First().Database.Name.ToSafeName(); + } + dbContextName = StringUtil.ToPascalCase(dbContextName); + Response.WriteLine(" public partial class "+ dbContextName +"Context: DbContext"); + + %> + { + + protected override void OnModelCreating(ModelBuilder modelBuilder) + { + //当主键为联合主键时,需要把这里的内容拷贝到对应的位置 + <% + TableSchemaCollection tables; + if(WholeDb){ + tables = SourceDatabase.Tables; + } + else{ + tables = SourceTables; + } + + foreach(TableSchema table in tables) + { + if(table.PrimaryKeys.Count <=1) continue; + var keys = string.Join(",", table.Columns.Where(u=>u.IsPrimaryKeyMember==true) + .Select(u =>"c."+u.Name)); + Response.WriteLine(" modelBuilder.Entity<"+table.Name+">()"); + Response.WriteLine(" .HasKey(c => new { "+keys+" });"); + } + %> + } + + <% + foreach(TableSchema table in tables) + { + Response.WriteLine(" public virtual DbSet<"+table.Name+"> "+StringUtil.ToPascalCase(StringUtil.ToPlural(table.Name))+" { get; set; }"); + } + %> + } +} \ No newline at end of file diff --git a/CodeSmith/CSharp/Internal/Entity.Generated.cst b/CodeSmith/CSharp/Internal/Entity.Generated.cst new file mode 100644 index 0000000..59ecd68 --- /dev/null +++ b/CodeSmith/CSharp/Internal/Entity.Generated.cst @@ -0,0 +1,75 @@ +<%@ Template Language="C#" TargetLanguage="C#" Debug="True" Encoding="UTF-8" %> + +<%@ Assembly Src="../Internal/Model.cs" %> +<%@ Assembly Src="../Internal/Extensions.cs" %> + +<%@ Import Namespace="System.Collections.Generic" %> +<%@ Import Namespace="System.Linq" %> +<%@ Import Namespace="System.Text" %> +<%@ Import Namespace="System.Text.RegularExpressions" %> + +<%@ Import Namespace="SchemaMapper" %> + +<%@ Property Name="Table" + Type="SchemaExplorer.TableSchema" %> + +<%@ Property Name="EntityNamespace" + Type="System.String" %> +//------------------------------------------------------------------------------ +// +// This code was generated by a CodeSmith Template. +// +// DO NOT MODIFY contents of this file. Changes to this +// file will be lost if the code is regenerated. +// Author:Yubao Li +// +//------------------------------------------------------------------------------ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.ComponentModel.DataAnnotations.Schema; +using System.Text; +using OpenAuth.Repository.Core; + +namespace <%= EntityNamespace %> +{ + /// + /// <%= Table.Description %> + /// + [Table("<%= Table.Name%>")] + public partial class <%= Table.Name %> : StringEntity + { + public <%= Table.Name %>() + { + <% foreach(ColumnSchema p in Table.Columns) { + if(p.IsPrimaryKeyMember) continue; + string type = p.SystemType.ToNullableType(p.AllowDBNull == true); + if(type =="int" || type=="decimal") + Response.WriteLine(" this."+p.Name+"= 0;"); + else if(type =="string") + Response.WriteLine(" this."+p.Name+"= string.Empty;"); + else if(type.ToLower().Contains("datetime")) + Response.WriteLine(" this."+p.Name+"= DateTime.Now;"); + } // foreach %> + } + + + <% + foreach(ColumnSchema p in Table.Columns) { + if(p.IsPrimaryKeyMember) continue; + %> + /// + /// <%=p.Description %> + /// + [Description("<%=p.Description%>")] + <%if(p.Name.LastIndexOf("Id") != -1){%> + [Browsable(false)] + <%}%> + <%if(p.DataType == DbType.Byte){%> + public bool <%= p.Name%> { get; set; } + <%}else{%> + public <%= p.SystemType.ToNullableType(p.AllowDBNull == true) %> <%= p.Name%> { get; set; } + <%}%> + <% } // foreach %> + } +} \ No newline at end of file diff --git a/CodeSmith/CSharp/Internal/Extensions.cs b/CodeSmith/CSharp/Internal/Extensions.cs new file mode 100644 index 0000000..0e8dc28 --- /dev/null +++ b/CodeSmith/CSharp/Internal/Extensions.cs @@ -0,0 +1,187 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using CodeSmith.Engine; + +namespace SchemaMapper +{ + public enum CodeLanguage + { + CSharp, + VisualBasic + } + + public static class Extensions + { + private static readonly HashSet _csharpKeywords; + private static readonly HashSet _visualBasicKeywords; + private static readonly Dictionary _csharpTypeAlias; + + static Extensions() + { + _csharpKeywords = new HashSet(StringComparer.Ordinal) + { + "as", "do", "if", "in", "is", + "for", "int", "new", "out", "ref", "try", + "base", "bool", "byte", "case", "char", "else", "enum", "goto", "lock", "long", "null", "this", "true", "uint", "void", + "break", "catch", "class", "const", "event", "false", "fixed", "float", "sbyte", "short", "throw", "ulong", "using", "while", + "double", "extern", "object", "params", "public", "return", "sealed", "sizeof", "static", "string", "struct", "switch", "typeof", "unsafe", "ushort", + "checked", "decimal", "default", "finally", "foreach", "private", "virtual", + "abstract", "continue", "delegate", "explicit", "implicit", "internal", "operator", "override", "readonly", "volatile", + "__arglist", "__makeref", "__reftype", "interface", "namespace", "protected", "unchecked", + "__refvalue", "stackalloc" + }; + + _visualBasicKeywords = new HashSet(StringComparer.OrdinalIgnoreCase) + { + "as", "do", "if", "in", "is", "me", "of", "on", "or", "to", + "and", "dim", "end", "for", "get", "let", "lib", "mod", "new", "not", "rem", "set", "sub", "try", "xor", + "ansi", "auto", "byte", "call", "case", "cdbl", "cdec", "char", "cint", "clng", "cobj", "csng", "cstr", "date", "each", "else", + "enum", "exit", "goto", "like", "long", "loop", "next", "step", "stop", "then", "true", "wend", "when", "with", + "alias", "byref", "byval", "catch", "cbool", "cbyte", "cchar", "cdate", "class", "const", "ctype", "cuint", "culng", "endif", "erase", "error", + "event", "false", "gosub", "isnot", "redim", "sbyte", "short", "throw", "ulong", "until", "using", "while", + "csbyte", "cshort", "double", "elseif", "friend", "global", "module", "mybase", "object", "option", "orelse", "public", "resume", "return", "select", "shared", + "single", "static", "string", "typeof", "ushort", + "andalso", "boolean", "cushort", "decimal", "declare", "default", "finally", "gettype", "handles", "imports", "integer", "myclass", "nothing", "partial", "private", "shadows", + "trycast", "unicode", "variant", + "assembly", "continue", "delegate", "function", "inherits", "operator", "optional", "preserve", "property", "readonly", "synclock", "uinteger", "widening", + "addressof", "interface", "namespace", "narrowing", "overloads", "overrides", "protected", "structure", "writeonly", + "addhandler", "directcast", "implements", "paramarray", "raiseevent", "withevents", + "mustinherit", "overridable", + "mustoverride", + "removehandler", + "class_finalize", "notinheritable", "notoverridable", + "class_initialize" + }; + + _csharpTypeAlias = new Dictionary(16) + { + {"System.Int16", "short"}, + {"System.Int32", "int"}, + {"System.Int64", "long"}, + {"System.String", "string"}, + {"System.Object", "object"}, + {"System.Boolean", "bool"}, + {"System.Void", "void"}, + {"System.Char", "char"}, + {"System.Byte", "byte"}, + {"System.UInt16", "ushort"}, + {"System.UInt32", "uint"}, + {"System.UInt64", "ulong"}, + {"System.SByte", "sbyte"}, + {"System.Single", "float"}, + {"System.Double", "double"}, + {"System.Decimal", "decimal"} + }; + } + + public static string ToCamelCase(this string name) + { + return StringUtil.ToCamelCase(name); + } + + public static string ToPascalCase(this string name) + { + return StringUtil.ToPascalCase(name); + } + + + public static string ToFieldName(this string name) + { + return "_" + StringUtil.ToCamelCase(name); + } + + public static string MakeUnique(this string name, Func exists) + { + string uniqueName = name; + int count = 1; + + while (exists(uniqueName)) + uniqueName = string.Concat(name, count++); + + return uniqueName; + } + + public static bool IsKeyword(this string text, CodeLanguage language = CodeLanguage.CSharp) + { + return language == CodeLanguage.VisualBasic + ? _visualBasicKeywords.Contains(text) + : _csharpKeywords.Contains(text); + } + + public static string ToSafeName(this string name, CodeLanguage language = CodeLanguage.CSharp) + { + if (!name.IsKeyword(language)) + return name; + + return language == CodeLanguage.VisualBasic + ? string.Format("[{0}]", name) + : "@" + name; + } + + public static string ToType(this Type type, CodeLanguage language = CodeLanguage.CSharp) + { + return ToType(type.FullName, language); + } + + public static string ToType(this string type, CodeLanguage language = CodeLanguage.CSharp) + { + if (type == "System.Xml.XmlDocument") + type = "System.String"; + + string t; + if (language == CodeLanguage.CSharp && _csharpTypeAlias.TryGetValue(type, out t)) + return t; + + + return type; + } + + public static string ToNullableType(this Type type, bool isNullable = false, CodeLanguage language = CodeLanguage.CSharp) + { + return ToNullableType(type.FullName, isNullable, language); + } + + public static string ToNullableType(this string type, bool isNullable = false, CodeLanguage language = CodeLanguage.CSharp) + { + bool isValueType = type.IsValueType(); + + type = type.ToType(language); + + if (!isValueType || !isNullable) + return type; + + return language == CodeLanguage.VisualBasic + ? string.Format("Nullable(Of {0})", type) + : type + "?"; + } + + public static bool IsValueType(this string type) + { + if (!type.StartsWith("System.")) + return false; + + var t = Type.GetType(type, false); + return t != null && t.IsValueType; + } + + public static string ToDelimitedString(this IEnumerable values, string delimiter, string format = null) + { + var sb = new StringBuilder(); + foreach (var i in values) + { + if (sb.Length > 0) + sb.Append(delimiter); + + if (string.IsNullOrEmpty(format)) + sb.Append(i); + else + sb.AppendFormat(format, i); + } + + return sb.ToString(); + } + + } +} diff --git a/CodeSmith/CSharp/Internal/Generator.cs b/CodeSmith/CSharp/Internal/Generator.cs new file mode 100644 index 0000000..751b669 --- /dev/null +++ b/CodeSmith/CSharp/Internal/Generator.cs @@ -0,0 +1,837 @@ +using System; +using System.Collections; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Text.RegularExpressions; +using CodeSmith.Engine; +using SchemaExplorer; + +namespace SchemaMapper +{ + public enum TableNaming + { + Mixed = 0, + Plural = 1, + Singular = 2 + } + + public enum EntityNaming + { + Preserve = 0, + Plural = 1, + Singular = 2 + } + + public enum RelationshipNaming + { + None = 0, + Plural = 1, + ListSuffix = 2 + } + + public enum ContextNaming + { + Preserve = 0, + Plural = 1, + TableSuffix = 2 + } + + public class GeneratorSettings + { + public GeneratorSettings() + { + RelationshipNaming = RelationshipNaming.ListSuffix; + EntityNaming = EntityNaming.Singular; + TableNaming = TableNaming.Singular; + CleanExpressions = new List { @"^\d+" }; + IgnoreExpressions = new List(); + } + + public TableNaming TableNaming { get; set; } + + public EntityNaming EntityNaming { get; set; } + + public RelationshipNaming RelationshipNaming { get; set; } + + public ContextNaming ContextNaming { get; set; } + + public List IgnoreExpressions { get; set; } + + public List CleanExpressions { get; set; } + + public bool InclusionMode { get; set; } + + public bool IsIgnored(string name) + { + if (IgnoreExpressions.Count == 0) + return false; + + bool isMatch = IgnoreExpressions.Any(regex => Regex.IsMatch(name, regex)); + + return InclusionMode ? !isMatch : isMatch; + } + + public string CleanName(string name) + { + if (CleanExpressions.Count == 0) + return name; + + foreach (var regex in CleanExpressions.Where(r => !string.IsNullOrEmpty(r))) + if (Regex.IsMatch(name, regex)) + return Regex.Replace(name, regex, ""); + + return name; + } + + public string RelationshipName(string name) + { + if (RelationshipNaming == RelationshipNaming.None) + return name; + + if (RelationshipNaming == RelationshipNaming.ListSuffix) + return name + "List"; + + return StringUtil.ToPascalCase(StringUtil.ToPlural(name)); + } + + public string ContextName(string name) + { + if (ContextNaming == ContextNaming.Preserve) + return name; + + if (ContextNaming == ContextNaming.TableSuffix) + return name + "Table"; + + return StringUtil.ToPascalCase(StringUtil.ToPlural(name)); + } + + public string EntityName(string name) + { + if (TableNaming != TableNaming.Plural && EntityNaming == EntityNaming.Plural) + name = StringUtil.ToPlural(name); + else if (TableNaming != TableNaming.Singular && EntityNaming == EntityNaming.Singular) + name = StringUtil.ToSingular(name); + + return StringUtil.ToPascalCase(name); + } + } + + public class SchemaItemProcessedEventArgs : EventArgs + { + public SchemaItemProcessedEventArgs(string name) + { + _name = name; + } + + private readonly string _name; + public string Name + { + get { return _name; } + } + } + + public class UniqueNamer + { + private readonly ConcurrentDictionary> _names; + + public UniqueNamer() + { + _names = new ConcurrentDictionary>(StringComparer.OrdinalIgnoreCase); + Comparer = StringComparer.CurrentCulture; + + // add existing + UniqueContextName("ChangeTracker"); + UniqueContextName("Configuration"); + UniqueContextName("Database"); + UniqueContextName("InternalContext"); + } + + public IEqualityComparer Comparer { get; set; } + + public string UniqueName(string bucketName, string name) + { + var hashSet = _names.GetOrAdd(bucketName, k => new HashSet(Comparer)); + string result = name.MakeUnique(hashSet.Contains); + hashSet.Add(result); + + return result; + } + + public string UniqueClassName(string className) + { + const string globalClassName = "global::ClassName"; + return UniqueName(globalClassName, className); + } + + public string UniqueContextName(string name) + { + const string globalContextname = "global::ContextName"; + return UniqueName(globalContextname, name); + } + + public string UniqueRelationshipName(string name) + { + const string globalContextname = "global::RelationshipName"; + return UniqueName(globalContextname, name); + } + + } + + public class Generator + { + private readonly UniqueNamer _namer; + + public Generator() + { + _settings = new GeneratorSettings(); + _namer = new UniqueNamer(); + } + + public event EventHandler SchemaItemProcessed; + protected void OnSchemaItemProcessed(string name) + { + var handler = SchemaItemProcessed; + if (handler == null) + return; + + handler(this, new SchemaItemProcessedEventArgs(name)); + } + + private readonly GeneratorSettings _settings; + public GeneratorSettings Settings + { + get { return _settings; } + } + + //按表信息创建DbContext + public EntityContext Generate(TableSchema tableSchema) + { + // only DeepLoad when in ignore mode + tableSchema.DeepLoad = !Settings.InclusionMode; + + var entityContext = new EntityContext(); + entityContext.DatabaseName = tableSchema.Database.Name; + + string dataContextName = StringUtil.ToPascalCase(tableSchema.Database.Name) + "Context"; + dataContextName = _namer.UniqueClassName(dataContextName); + + entityContext.ClassName = dataContextName; + + GetEntity(entityContext, tableSchema); + + + return entityContext; + } + + //按数据库连接信息创建DbContext + public EntityContext Generate(DatabaseSchema databaseSchema) + { + // only DeepLoad when in ignore mode + databaseSchema.DeepLoad = !Settings.InclusionMode; + + var entityContext = new EntityContext(); + entityContext.DatabaseName = databaseSchema.Name; + + string dataContextName = StringUtil.ToPascalCase(databaseSchema.Name) + "Context"; + dataContextName = _namer.UniqueClassName(dataContextName); + + entityContext.ClassName = dataContextName; + + foreach (TableSchema t in databaseSchema.Tables) + { + if (Settings.IsIgnored(t.FullName)) + { + Debug.WriteLine("Skipping Table: " + t.FullName); + } + else if (IsManyToMany(t)) + { + CreateManyToMany(entityContext, t); + } + else + { + Debug.WriteLine("Getting Table Schema: " + t.FullName); + GetEntity(entityContext, t); + } + + OnSchemaItemProcessed(t.FullName); + } + + return entityContext; + } + + //根据DbContext和tableSchema获取实体 + public Entity GetEntity(EntityContext entityContext, TableSchema tableSchema, bool processRelationships = true, bool processMethods = true) + { + string key = tableSchema.FullName; + + Entity entity = entityContext.Entities.ByTable(key) + ?? CreateEntity(entityContext, tableSchema); + + if (!entity.Properties.IsProcessed) + CreateProperties(entity, tableSchema); + + if (processRelationships && !entity.Relationships.IsProcessed) + CreateRelationships(entityContext, entity, tableSchema); + + if (processMethods && !entity.Methods.IsProcessed) + CreateMethods(entity, tableSchema); + + entity.IsProcessed = true; + return entity; + } + + private Entity CreateEntity(EntityContext entityContext, TableSchema tableSchema) + { + var entity = new Entity + { + FullName = tableSchema.FullName, + TableName = tableSchema.Name, + TableSchema = tableSchema.Owner, + Description = tableSchema.Description + }; + + string className = ToClassName(tableSchema.Name); + className = _namer.UniqueClassName(className); + + string mappingName = className + "Map"; + mappingName = _namer.UniqueClassName(mappingName); + + string contextName = Settings.ContextName(className); + contextName = ToPropertyName(entityContext.ClassName, contextName); + contextName = _namer.UniqueContextName(contextName); + + entity.ClassName = className; + entity.ContextName = contextName; + entity.MappingName = mappingName; + + entityContext.Entities.Add(entity); + + return entity; + } + + /// + /// 创建实体的属性 + /// + private void CreateProperties(Entity entity, TableSchema tableSchema) + { + foreach (ColumnSchema columnSchema in tableSchema.Columns) + { + // skip unsupported type + if (columnSchema.NativeType.Equals("hierarchyid", StringComparison.OrdinalIgnoreCase) + || columnSchema.NativeType.Equals("sql_variant", StringComparison.OrdinalIgnoreCase)) + { + Debug.WriteLine(string.Format("Skipping column '{0}' because it has an unsupported db type '{1}'.", + columnSchema.Name, columnSchema.NativeType)); + continue; + } + + Property property = entity.Properties.ByColumn(columnSchema.Name); + + if (property == null) + { + property = new Property { ColumnName = columnSchema.Name }; + entity.Properties.Add(property); + } + + string propertyName = ToPropertyName(entity.ClassName, columnSchema.Name); + propertyName = _namer.UniqueName(entity.ClassName, propertyName); + + property.PropertyName = propertyName; + + property.DataType = columnSchema.DataType; + property.SystemType = columnSchema.SystemType; + property.NativeType = columnSchema.NativeType; + property.Description = columnSchema.Description; + + property.IsPrimaryKey = columnSchema.IsPrimaryKeyMember; + property.IsForeignKey = columnSchema.IsForeignKeyMember; + property.IsNullable = columnSchema.AllowDBNull; + + property.IsIdentity = IsIdentity(columnSchema); + property.IsRowVersion = IsRowVersion(columnSchema); + property.IsAutoGenerated = IsDbGenerated(columnSchema); + + if (columnSchema.IsUnique) + property.IsUnique = columnSchema.IsUnique; + + if (property.SystemType == typeof(string) + || property.SystemType == typeof(byte[])) + { + property.MaxLength = columnSchema.Size; + } + + if (property.SystemType == typeof(float) + || property.SystemType == typeof(double) + || property.SystemType == typeof(decimal)) + { + property.Precision = columnSchema.Precision; + property.Scale = columnSchema.Scale; + } + + property.IsProcessed = true; + } + + entity.Properties.IsProcessed = true; + } + + + private void CreateRelationships(EntityContext entityContext, Entity entity, TableSchema tableSchema) + { + foreach (TableKeySchema tableKey in tableSchema.ForeignKeys) + { + if (Settings.IsIgnored(tableKey.ForeignKeyTable.FullName) + || Settings.IsIgnored(tableKey.PrimaryKeyTable.FullName)) + { + Debug.WriteLine("Skipping relationship '{0}' because table '{1}' or '{2}' is ignored.", + tableKey.FullName, tableKey.ForeignKeyTable.FullName, tableKey.PrimaryKeyTable.FullName); + + continue; + } + + CreateRelationship(entityContext, entity, tableKey); + } + + entity.Relationships.IsProcessed = true; + } + + private void CreateRelationship(EntityContext entityContext, Entity foreignEntity, TableKeySchema tableKeySchema) + { + Entity primaryEntity = GetEntity(entityContext, tableKeySchema.PrimaryKeyTable, false, false); + + string primaryName = primaryEntity.ClassName; + string foreignName = foreignEntity.ClassName; + + string relationshipName = tableKeySchema.Name; + relationshipName = _namer.UniqueRelationshipName(relationshipName); + + bool isCascadeDelete = IsCascadeDelete(tableKeySchema); + bool foreignMembersRequired; + bool primaryMembersRequired; + + var foreignMembers = GetKeyMembers(foreignEntity, tableKeySchema.ForeignKeyMemberColumns, tableKeySchema.Name, out foreignMembersRequired); + var primaryMembers = GetKeyMembers(primaryEntity, tableKeySchema.PrimaryKeyMemberColumns, tableKeySchema.Name, out primaryMembersRequired); + + Relationship foreignRelationship = foreignEntity.Relationships + .FirstOrDefault(r => r.RelationshipName == relationshipName && r.IsForeignKey); + + if (foreignRelationship == null) + { + foreignRelationship = new Relationship { RelationshipName = relationshipName }; + foreignEntity.Relationships.Add(foreignRelationship); + } + foreignRelationship.IsMapped = true; + foreignRelationship.IsForeignKey = true; + foreignRelationship.ThisCardinality = foreignMembersRequired ? Cardinality.One : Cardinality.ZeroOrOne; + foreignRelationship.ThisEntity = foreignName; + foreignRelationship.ThisProperties = new List(foreignMembers); + foreignRelationship.OtherEntity = primaryName; + foreignRelationship.OtherProperties = new List(primaryMembers); + foreignRelationship.CascadeDelete = isCascadeDelete; + + string prefix = GetMemberPrefix(foreignRelationship, primaryName, foreignName); + + string foreignPropertyName = ToPropertyName(foreignEntity.ClassName, prefix + primaryName); + foreignPropertyName = _namer.UniqueName(foreignEntity.ClassName, foreignPropertyName); + foreignRelationship.ThisPropertyName = foreignPropertyName; + + // add reverse + Relationship primaryRelationship = primaryEntity.Relationships + .FirstOrDefault(r => r.RelationshipName == relationshipName && r.IsForeignKey == false); + + if (primaryRelationship == null) + { + primaryRelationship = new Relationship { RelationshipName = relationshipName }; + primaryEntity.Relationships.Add(primaryRelationship); + } + + primaryRelationship.IsMapped = false; + primaryRelationship.IsForeignKey = false; + primaryRelationship.ThisEntity = primaryName; + primaryRelationship.ThisProperties = new List(primaryMembers); + primaryRelationship.OtherEntity = foreignName; + primaryRelationship.OtherProperties = new List(foreignMembers); + primaryRelationship.CascadeDelete = isCascadeDelete; + + bool isOneToOne = IsOneToOne(tableKeySchema, foreignRelationship); + + if (isOneToOne) + primaryRelationship.ThisCardinality = primaryMembersRequired ? Cardinality.One : Cardinality.ZeroOrOne; + else + primaryRelationship.ThisCardinality = Cardinality.Many; + + string primaryPropertyName = prefix + foreignName; + if (!isOneToOne) + primaryPropertyName = Settings.RelationshipName(primaryPropertyName); + + primaryPropertyName = ToPropertyName(primaryEntity.ClassName, primaryPropertyName); + primaryPropertyName = _namer.UniqueName(primaryEntity.ClassName, primaryPropertyName); + + primaryRelationship.ThisPropertyName = primaryPropertyName; + + foreignRelationship.OtherPropertyName = primaryRelationship.ThisPropertyName; + foreignRelationship.OtherCardinality = primaryRelationship.ThisCardinality; + + primaryRelationship.OtherPropertyName = foreignRelationship.ThisPropertyName; + primaryRelationship.OtherCardinality = foreignRelationship.ThisCardinality; + + foreignRelationship.IsProcessed = true; + primaryRelationship.IsProcessed = true; + } + + private void CreateManyToMany(EntityContext entityContext, TableSchema joinTable) + { + if (joinTable.ForeignKeys.Count != 2) + return; + + var joinTableName = joinTable.Name; + var joinSchemaName = joinTable.Owner; + + // first fkey is always left, second fkey is right + var leftForeignKey = joinTable.ForeignKeys[0]; + var leftTable = leftForeignKey.PrimaryKeyTable; + var joinLeftColumn = leftForeignKey.ForeignKeyMemberColumns.Select(c => c.Name).ToList(); + var leftEntity = GetEntity(entityContext, leftTable, false, false); + + var rightForeignKey = joinTable.ForeignKeys[1]; + var rightTable = rightForeignKey.PrimaryKeyTable; + var joinRightColumn = rightForeignKey.ForeignKeyMemberColumns.Select(c => c.Name).ToList(); + var rightEntity = GetEntity(entityContext, rightTable, false, false); + + string leftPropertyName = Settings.RelationshipName(rightEntity.ClassName); + leftPropertyName = _namer.UniqueName(leftEntity.ClassName, leftPropertyName); + + string rightPropertyName = Settings.RelationshipName(leftEntity.ClassName); + rightPropertyName = _namer.UniqueName(rightEntity.ClassName, rightPropertyName); + + string relationshipName = string.Format("{0}|{1}", + leftForeignKey.Name, + rightForeignKey.Name); + + relationshipName = _namer.UniqueRelationshipName(relationshipName); + + var left = new Relationship { RelationshipName = relationshipName }; + left.IsForeignKey = false; + left.IsMapped = true; + + left.ThisCardinality = Cardinality.Many; + left.ThisEntity = leftEntity.ClassName; + left.ThisPropertyName = leftPropertyName; + + left.OtherCardinality = Cardinality.Many; + left.OtherEntity = rightEntity.ClassName; + left.OtherPropertyName = rightPropertyName; + + left.JoinTable = joinTableName; + left.JoinSchema = joinSchemaName; + left.JoinThisColumn = new List(joinLeftColumn); + left.JoinOtherColumn = new List(joinRightColumn); + + leftEntity.Relationships.Add(left); + + var right = new Relationship { RelationshipName = relationshipName }; + right.IsForeignKey = false; + right.IsMapped = false; + + right.ThisCardinality = Cardinality.Many; + right.ThisEntity = rightEntity.ClassName; + right.ThisPropertyName = rightPropertyName; + + right.OtherCardinality = Cardinality.Many; + right.OtherEntity = leftEntity.ClassName; + right.OtherPropertyName = leftPropertyName; + + right.JoinTable = joinTableName; + right.JoinSchema = joinSchemaName; + right.JoinThisColumn = new List(joinRightColumn); + right.JoinOtherColumn = new List(joinLeftColumn); + + rightEntity.Relationships.Add(right); + } + + + private void CreateMethods(Entity entity, TableSchema tableSchema) + { + if (tableSchema.HasPrimaryKey) + { + var method = GetMethodFromColumns(entity, tableSchema.PrimaryKey.MemberColumns); + if (method != null) + { + method.IsKey = true; + method.SourceName = tableSchema.PrimaryKey.FullName; + + if (!entity.Methods.Any(m => m.NameSuffix == method.NameSuffix)) + entity.Methods.Add(method); + } + } + + GetIndexMethods(entity, tableSchema); + GetForeignKeyMethods(entity, tableSchema); + + entity.Methods.IsProcessed = true; + } + + private static void GetForeignKeyMethods(Entity entity, TableSchema table) + { + var columns = new List(); + + foreach (ColumnSchema column in table.ForeignKeyColumns) + { + columns.Add(column); + + Method method = GetMethodFromColumns(entity, columns); + if (method != null && !entity.Methods.Any(m => m.NameSuffix == method.NameSuffix)) + entity.Methods.Add(method); + + columns.Clear(); + } + } + + private static void GetIndexMethods(Entity entity, TableSchema table) + { + foreach (IndexSchema index in table.Indexes) + { + Method method = GetMethodFromColumns(entity, index.MemberColumns); + if (method == null) + continue; + + method.SourceName = index.FullName; + method.IsUnique = index.IsUnique; + method.IsIndex = true; + + if (!entity.Methods.Any(m => m.NameSuffix == method.NameSuffix)) + entity.Methods.Add(method); + } + } + + private static Method GetMethodFromColumns(Entity entity, IEnumerable columns) + { + var method = new Method(); + string methodName = string.Empty; + + foreach (var column in columns) + { + var property = entity.Properties.ByColumn(column.Name); + if (property == null) + continue; + + method.Properties.Add(property); + methodName += property.PropertyName; + } + + if (method.Properties.Count == 0) + return null; + + method.NameSuffix = methodName; + return method; + } + + + private static List GetKeyMembers(Entity entity, IEnumerable members, string name, out bool isRequired) + { + var keyMembers = new List(); + isRequired = false; + + foreach (var member in members) + { + var property = entity.Properties.ByColumn(member.Name); + + if (property == null) + throw new InvalidOperationException(string.Format( + "Could not find column {0} for relationship {1}.", + member.Name, + name)); + + if (!isRequired) + isRequired = property.IsRequired; + + keyMembers.Add(property.PropertyName); + } + + return keyMembers; + } + + private static string GetMemberPrefix(Relationship relationship, string primaryClass, string foreignClass) + { + string thisKey = relationship.ThisProperties.FirstOrDefault() ?? string.Empty; + string otherKey = relationship.OtherProperties.FirstOrDefault() ?? string.Empty; + + bool isSameName = thisKey.Equals(otherKey, StringComparison.OrdinalIgnoreCase); + isSameName = (isSameName || thisKey.Equals(primaryClass + otherKey, StringComparison.OrdinalIgnoreCase)); + + string prefix = string.Empty; + if (isSameName) + return prefix; + + prefix = thisKey.Replace(otherKey, ""); + prefix = prefix.Replace(primaryClass, ""); + prefix = prefix.Replace(foreignClass, ""); + prefix = Regex.Replace(prefix, @"(_ID|_id|_Id|\.ID|\.id|\.Id|ID|Id)$", ""); + prefix = Regex.Replace(prefix, @"^\d", ""); + + return prefix; + } + + private static bool IsOneToOne(TableKeySchema tableKeySchema, Relationship foreignRelationship) + { + bool isFkeyPkey = tableKeySchema.ForeignKeyTable.HasPrimaryKey + && tableKeySchema.ForeignKeyTable.PrimaryKey != null + && tableKeySchema.ForeignKeyTable.PrimaryKey.MemberColumns.Count == 1 + && tableKeySchema.ForeignKeyTable.PrimaryKey.MemberColumns.Contains( + foreignRelationship.ThisProperties.FirstOrDefault()); + + if (isFkeyPkey) + return true; + + // if f.key is unique + return tableKeySchema.ForeignKeyMemberColumns.All(column => column.IsUnique); + } + + private static bool IsManyToMany(TableSchema tableSchema) + { + // 1) Table must have Two ForeignKeys. + // 2) All columns must be either... + // a) Member of a Foreign Key. + // b) DbGenerated + + if (tableSchema.Columns.Count < 2) + return false; + + if (tableSchema.ForeignKeyColumns.Count != 2) + return false; + + // all columns are fkeys + if (tableSchema.Columns.Count == 2 && + tableSchema.ForeignKeyColumns.Count == 2) + return true; + + // check all non fkey columns to make sure db gen'd + return tableSchema.NonForeignKeyColumns.All(c => + IsDbGenerated(c) || HasDefaultValue(c)); + } + + #region Name Helpers + private string ToClassName(string name) + { + name = Settings.EntityName(name); + string legalName = ToLegalName(name); + + return legalName; + } + + private string ToPropertyName(string className, string name) + { + string propertyName = ToLegalName(name); + if (className.Equals(propertyName, StringComparison.OrdinalIgnoreCase)) + propertyName += "Member"; + + return propertyName; + } + + private string ToLegalName(string name) + { + string legalName = Settings.CleanName(name); + legalName = StringUtil.ToPascalCase(legalName); + + return legalName; + } + #endregion + + #region Column Flag Helpers + private static bool IsCascadeDelete(SchemaObjectBase column) + { + bool cascadeDelete = false; + string value; + try + { + if (column.ExtendedProperties.Contains(ExtendedPropertyNames.CascadeDelete)) + { + value = column.ExtendedProperties[ExtendedPropertyNames.CascadeDelete].Value.ToString(); + bool.TryParse(value, out cascadeDelete); + } + } + catch (Exception ex) + { + Debug.WriteLine("Error: " + ex.Message); + } + + return cascadeDelete; + } + + private static bool IsRowVersion(DataObjectBase column) + { + bool isTimeStamp = column.NativeType.Equals( + "timestamp", StringComparison.OrdinalIgnoreCase); + bool isRowVersion = column.NativeType.Equals( + "rowversion", StringComparison.OrdinalIgnoreCase); + + return (isTimeStamp || isRowVersion); + } + + private static bool IsDbGenerated(DataObjectBase column) + { + if (IsRowVersion(column)) + return true; + + if (IsIdentity(column)) + return true; + + bool isComputed = false; + string value; + try + { + if (column.ExtendedProperties.Contains(ExtendedPropertyNames.IsComputed)) + { + value = column.ExtendedProperties[ExtendedPropertyNames.IsComputed].Value.ToString(); + bool.TryParse(value, out isComputed); + } + } + catch (Exception ex) + { + Debug.WriteLine("Error: " + ex.Message); + } + + return isComputed; + } + + private static bool IsIdentity(DataObjectBase column) + { + string temp; + bool isIdentity = false; + try + { + if (column.ExtendedProperties.Contains(ExtendedPropertyNames.IsIdentity)) + { + temp = column.ExtendedProperties[ExtendedPropertyNames.IsIdentity].Value.ToString(); + bool.TryParse(temp, out isIdentity); + } + } + catch (Exception ex) + { + Debug.WriteLine("Error: " + ex.Message); + } + + return isIdentity; + } + + private static bool HasDefaultValue(DataObjectBase column) + { + try + { + if (!column.ExtendedProperties.Contains(ExtendedPropertyNames.DefaultValue)) + return false; + + string value = column.ExtendedProperties[ExtendedPropertyNames.DefaultValue].Value.ToString(); + return !string.IsNullOrEmpty(value); + } + catch (Exception ex) + { + Debug.WriteLine("Error: " + ex.Message); + } + + return false; + } + #endregion + } +} diff --git a/CodeSmith/CSharp/Internal/Model.cs b/CodeSmith/CSharp/Internal/Model.cs new file mode 100644 index 0000000..b7b2a6f --- /dev/null +++ b/CodeSmith/CSharp/Internal/Model.cs @@ -0,0 +1,370 @@ +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Data; +using System.Diagnostics; +using System.Linq; +using System.Xml.Serialization; + +namespace SchemaMapper +{ + #region Base + public enum Cardinality + { + ZeroOrOne, + One, + Many + } + + public class EntityBase + { + [XmlIgnore] + public bool IsProcessed { get; set; } + } + #endregion + + #region Model + [DebuggerDisplay("Context: {ContextName}, Database: {DatabaseName}")] + public class EntityContext : EntityBase + { + public EntityContext() + { + Entities = new EntityCollection(); + } + + public string ClassName { get; set; } + public string DatabaseName { get; set; } + + public EntityCollection Entities { get; set; } + + public void RenameEntity(string originalName, string newName) + { + if (originalName == newName) + return; + + Debug.WriteLine("Rename Entity '{0}' to '{1}'.", originalName, newName); + foreach (var entity in Entities) + { + if (entity.ClassName == originalName) + entity.ClassName = newName; + + foreach (var relationship in entity.Relationships) + { + if (relationship.ThisEntity == originalName) + relationship.ThisEntity = newName; + if (relationship.OtherEntity == originalName) + relationship.OtherEntity = newName; + } + } + } + + public void RenameProperty(string entityName, string originalName, string newName) + { + if (originalName == newName) + return; + + Debug.WriteLine("Rename Property '{0}' to '{1}' in Entity '{2}'.", originalName, newName, entityName); + foreach (var entity in Entities) + { + if (entity.ClassName == entityName) + { + var property = entity.Properties.ByProperty(originalName); + if (property != null) + property.PropertyName = newName; + } + + foreach (var relationship in entity.Relationships) + { + if (relationship.ThisEntity == entityName) + for (int i = 0; i < relationship.ThisProperties.Count; i++) + if (relationship.ThisProperties[i] == originalName) + relationship.ThisProperties[i] = newName; + + if (relationship.OtherEntity == entityName) + for (int i = 0; i < relationship.OtherProperties.Count; i++) + if (relationship.OtherProperties[i] == originalName) + relationship.OtherProperties[i] = newName; + } + } + + } + } + + [DebuggerDisplay("Class: {ClassName}, Table: {FullName}, Context: {ContextName}")] + public class Entity : EntityBase + { + public Entity() + { + Properties = new PropertyCollection(); + Relationships = new RelationshipCollection(); + Methods = new MethodCollection(); + } + + public string ContextName { get; set; } + public string ClassName { get; set; } + public string MappingName { get; set; } + public string Description{ get;set;} + + public string TableSchema { get; set; } + public string TableName { get; set; } + public string FullName { get; set; } + + public PropertyCollection Properties { get; set; } + public RelationshipCollection Relationships { get; set; } + public MethodCollection Methods { get; set; } + } + + [DebuggerDisplay("Property: {PropertyName}, Column: {ColumnName}, Type: {NativeType}")] + public class Property : EntityBase + { + public string PropertyName { get; set; } + public string ColumnName { get; set; } + public string Description { get; set; } + + public DbType DataType { get; set; } + public string NativeType { get; set; } + + [XmlIgnore] + public Type SystemType { get; set; } + + public int? Order { get; set; } + public bool OrderSpecified + { + get { return Order.HasValue; } + } + + public bool? IsNullable { get; set; } + public bool IsNullableSpecified + { + get { return IsNullable.HasValue; } + } + + public bool IsRequired + { + get { return IsNullable == false; } + set { IsNullable = !value; } + } + public bool IsOptional + { + get { return IsNullable == true; } + set { IsNullable = value; } + } + + public bool? IsPrimaryKey { get; set; } + public bool IsPrimaryKeySpecified + { + get { return IsPrimaryKey.HasValue; } + } + public bool? IsForeignKey { get; set; } + public bool IsForeignKeySpecified + { + get { return IsForeignKey.HasValue; } + } + + public bool? IsAutoGenerated { get; set; } + public bool IsAutoGeneratedSpecified + { + get { return IsAutoGenerated.HasValue; } + } + public bool? IsReadOnly { get; set; } + public bool IsReadOnlySpecified + { + get { return IsReadOnly.HasValue; } + } + public bool? IsRowVersion { get; set; } + public bool IsRowVersionSpecified + { + get { return IsRowVersion.HasValue; } + } + public bool? IsIdentity { get; set; } + public bool IsIdentitySpecified + { + get { return IsIdentity.HasValue; } + } + public bool? IsUnique { get; set; } + public bool IsUniqueSpecified + { + get { return IsUnique.HasValue; } + } + + public bool? IsUnicode { get; set; } + public bool IsUnicodeSpecified + { + get { return IsUnicode.HasValue; } + } + public bool? IsFixedLength { get; set; } + public bool IsFixedLengthSpecified + { + get { return IsFixedLength.HasValue; } + } + + public int? MaxLength { get; set; } + public bool MaxLengthSpecified + { + get { return MaxLength.HasValue; } + } + + public byte? Precision { get; set; } + public bool PrecisionSpecified + { + get { return Precision.HasValue; } + } + public int? Scale { get; set; } + public bool ScaleSpecified + { + get { return Scale.HasValue; } + } + } + + [DebuggerDisplay("Other: {OtherEntity}, Property: {OtherPropertyName}, Relationship: {RelationshipName}")] + public class Relationship : EntityBase + { + public Relationship() + { + OtherProperties = new List(); + ThisProperties = new List(); + } + + public string RelationshipName { get; set; } + + public string ThisEntity { get; set; } + public string ThisPropertyName { get; set; } + public Cardinality ThisCardinality { get; set; } + public List ThisProperties { get; set; } + + public string OtherEntity { get; set; } + public string OtherPropertyName { get; set; } + public Cardinality OtherCardinality { get; set; } + public List OtherProperties { get; set; } + + public bool? CascadeDelete { get; set; } + public bool IsForeignKey { get; set; } + public bool IsMapped { get; set; } + + public bool IsManyToMany + { + get + { + return ThisCardinality == Cardinality.Many + && OtherCardinality == Cardinality.Many; + } + } + + public bool IsOneToOne + { + get + { + return ThisCardinality != Cardinality.Many + && OtherCardinality != Cardinality.Many; + } + } + + public string JoinTable { get; set; } + public string JoinSchema { get; set; } + public List JoinThisColumn { get; set; } + public List JoinOtherColumn { get; set; } + + } + + [DebuggerDisplay("Suffix: {NameSuffix}, IsKey: {IsKey}, IsUnique: {IsUnique}")] + public class Method : EntityBase + { + public Method() + { + Properties = new List(); + } + + public string NameSuffix { get; set; } + public string SourceName { get; set; } + + public bool IsKey { get; set; } + public bool IsUnique { get; set; } + public bool IsIndex { get; set; } + + public List Properties { get; set; } + } + #endregion + + #region Collections + public class EntityCollection + : ObservableCollection + { + public bool IsProcessed { get; set; } + + public Entity ByTable(string fullName) + { + return this.FirstOrDefault(x => x.FullName == fullName); + } + + public Entity ByTable(string tableName, string tableSchema) + { + return this.FirstOrDefault(x => x.TableName == tableName && x.TableSchema == tableSchema); + } + + public Entity ByClass(string className) + { + return this.FirstOrDefault(x => x.ClassName == className); + } + + public Entity ByContext(string contextName) + { + return this.FirstOrDefault(x => x.ContextName == contextName); + } + } + + public class PropertyCollection + : ObservableCollection + { + public bool IsProcessed { get; set; } + + public IEnumerable PrimaryKeys + { + get { return this.Where(p => p.IsPrimaryKey == true); } + } + + public IEnumerable ForeignKeys + { + get { return this.Where(p => p.IsForeignKey == true); } + } + + public Property ByColumn(string columnName) + { + return this.FirstOrDefault(x => x.ColumnName == columnName); + } + + public Property ByProperty(string propertyName) + { + return this.FirstOrDefault(x => x.PropertyName == propertyName); + } + } + + public class RelationshipCollection + : ObservableCollection + { + public bool IsProcessed { get; set; } + + public Relationship ByName(string name) + { + return this.FirstOrDefault(x => x.RelationshipName == name); + } + + public Relationship ByProperty(string propertyName) + { + return this.FirstOrDefault(x => x.ThisPropertyName == propertyName); + } + + public Relationship ByOther(string name) + { + return this.FirstOrDefault(x => x.OtherEntity == name); + } + } + + public class MethodCollection + : ObservableCollection + { + public bool IsProcessed { get; set; } + } + + #endregion +} + diff --git a/CodeSmith/CSharp/Internal/Parser.cs b/CodeSmith/CSharp/Internal/Parser.cs new file mode 100644 index 0000000..95766be --- /dev/null +++ b/CodeSmith/CSharp/Internal/Parser.cs @@ -0,0 +1,639 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Text; +using ICSharpCode.NRefactory.CSharp; + +namespace SchemaMapper +{ + #region Mapping Parser + [DebuggerDisplay("Table: {TableName}, Entity: {EntityClass}, Mapping: {MappingClass}")] + public class ParsedEntity + { + public ParsedEntity() + { + Properties = new List(); + Relationships = new List(); + } + + public string EntityClass { get; set; } + public string MappingClass { get; set; } + + public string TableName { get; set; } + public string TableSchema { get; set; } + + public List Properties { get; private set; } + public List Relationships { get; private set; } + } + + [DebuggerDisplay("Column: {ColumnName}, Property: {PropertyName}")] + public class ParsedProperty + { + public string ColumnName { get; set; } + public string PropertyName { get; set; } + } + + [DebuggerDisplay("This: {ThisPropertyName}, Other: {OtherPropertyName}")] + public class ParsedRelationship + { + public ParsedRelationship() + { + ThisProperties = new List(); + JoinThisColumn = new List(); + JoinOtherColumn = new List(); + } + + public string ThisPropertyName { get; set; } + public List ThisProperties { get; private set; } + + public string OtherPropertyName { get; set; } + + public string JoinTable { get; set; } + public string JoinSchema { get; set; } + public List JoinThisColumn { get; private set; } + public List JoinOtherColumn { get; private set; } + } + + public class MappingVisitor : DepthFirstAstVisitor + { + public MappingVisitor() + { + MappingBaseType = "EntityTypeConfiguration"; + } + + public string MappingBaseType { get; set; } + public ParsedEntity ParsedEntity { get; set; } + + public override object VisitTypeDeclaration(TypeDeclaration typeDeclaration, object data) + { + var baseType = typeDeclaration.BaseTypes.OfType().FirstOrDefault(); + if (baseType == null || baseType.MemberName != MappingBaseType) + return base.VisitTypeDeclaration(typeDeclaration, data); + + var entity = baseType.TypeArguments.OfType().FirstOrDefault(); + if (entity == null) + return base.VisitTypeDeclaration(typeDeclaration, data); + + if (ParsedEntity == null) + ParsedEntity = new ParsedEntity(); + + ParsedEntity.EntityClass = entity.MemberName; + ParsedEntity.MappingClass = typeDeclaration.Name; + + return base.VisitTypeDeclaration(typeDeclaration, ParsedEntity); + } + + public override object VisitInvocationExpression(InvocationExpression invocationExpression, object data) + { + if (data == null) + return base.VisitInvocationExpression(invocationExpression, null); + + // visit all the methods + var identifier = invocationExpression.Target.Children.OfType().FirstOrDefault(); + string methodName = identifier == null ? string.Empty : identifier.Name; + + // the different types of incoming data, helps us know what we're parsing + var parsedEntity = data as ParsedEntity; + var parsedProperty = data as ParsedProperty; + var parsedRelationship = data as ParsedRelationship; + + switch (methodName) + { + case "ToTable": + var tableNameExpression = invocationExpression.Arguments + .OfType() + .ToArray(); + + string tableName = null; + string tableSchema = null; + + if (tableNameExpression.Length >= 1) + tableName = tableNameExpression[0].Value.ToString(); + if (tableNameExpression.Length >= 2) + tableSchema = tableNameExpression[1].Value.ToString(); + + // ToTable is either Entity -> Table map or Many to Many map + if (parsedEntity != null) + { + // when data is ParsedEntity, entity map + parsedEntity.TableName = tableName; + parsedEntity.TableSchema = tableSchema; + } + else if (parsedRelationship != null) + { + // when data is ParsedRelationship, many to many map + parsedRelationship.JoinTable = tableName; + parsedRelationship.JoinSchema = tableSchema; + } + break; + case "HasColumnName": + var columnNameExpression = invocationExpression.Arguments + .OfType() + .FirstOrDefault(); + + if (columnNameExpression == null) + break; + + // property to column map start. + string columnName = columnNameExpression.Value.ToString(); + var property = new ParsedProperty { ColumnName = columnName }; + ParsedEntity.Properties.Add(property); + + //only have column info at this point. pass data to get property name. + return base.VisitInvocationExpression(invocationExpression, property); + case "Property": + var propertyExpression = invocationExpression.Arguments + .OfType() + .FirstOrDefault(); + + if (parsedProperty == null || propertyExpression == null) + break; + + // ParsedProperty is passed in as data from HasColumnName, add property name + var propertyBodyExpression = propertyExpression.Body as MemberReferenceExpression; + if (propertyBodyExpression != null) + parsedProperty.PropertyName = propertyBodyExpression.MemberName; + + break; + case "Map": + // start a new Many to Many relationship + var mapRelation = new ParsedRelationship(); + ParsedEntity.Relationships.Add(mapRelation); + // pass to child nodes to fill in data + return base.VisitInvocationExpression(invocationExpression, mapRelation); + case "HasForeignKey": + var foreignExpression = invocationExpression.Arguments + .OfType() + .FirstOrDefault(); + + if (foreignExpression == null) + break; + + // when only 1 fkey, body will be member ref + if (foreignExpression.Body is MemberReferenceExpression) + { + var foreignBodyExpression = foreignExpression.Body as MemberReferenceExpression; + // start a new relationship + var foreignRelation = new ParsedRelationship(); + ParsedEntity.Relationships.Add(foreignRelation); + + foreignRelation.ThisProperties.Add(foreignBodyExpression.MemberName); + // pass as data for child nodes to fill in data + return base.VisitInvocationExpression(invocationExpression, foreignRelation); + } + // when more then 1 fkey, body will be an anonymous type + if (foreignExpression.Body is AnonymousTypeCreateExpression) + { + var foreignBodyExpression = foreignExpression.Body as AnonymousTypeCreateExpression; + var foreignRelation = new ParsedRelationship(); + ParsedEntity.Relationships.Add(foreignRelation); + + var properties = foreignBodyExpression.Children + .OfType() + .Select(m => m.MemberName); + + foreignRelation.ThisProperties.AddRange(properties); + + return base.VisitInvocationExpression(invocationExpression, foreignRelation); + } + break; + case "HasMany": + var hasManyExpression = invocationExpression.Arguments + .OfType() + .FirstOrDefault(); + + if (parsedRelationship == null || hasManyExpression == null) + break; + + // filling existing relationship data + var hasManyBodyExpression = hasManyExpression.Body as MemberReferenceExpression; + if (hasManyBodyExpression != null) + parsedRelationship.ThisPropertyName = hasManyBodyExpression.MemberName; + + break; + case "WithMany": + var withManyExpression = invocationExpression.Arguments + .OfType() + .FirstOrDefault(); + + if (parsedRelationship == null || withManyExpression == null) + break; + + // filling existing relationship data + var withManyBodyExpression = withManyExpression.Body as MemberReferenceExpression; + if (withManyBodyExpression != null) + parsedRelationship.OtherPropertyName = withManyBodyExpression.MemberName; + + break; + case "HasRequired": + case "HasOptional": + var hasExpression = invocationExpression.Arguments + .OfType() + .FirstOrDefault(); + + if (parsedRelationship == null || hasExpression == null) + break; + + // filling existing relationship data + var hasBodyExpression = hasExpression.Body as MemberReferenceExpression; + if (hasBodyExpression != null) + parsedRelationship.ThisPropertyName = hasBodyExpression.MemberName; + + break; + case "MapLeftKey": + if (parsedRelationship == null) + break; + + var leftKeyExpression = invocationExpression.Arguments + .OfType() + .Select(e => e.Value.ToString()); + + parsedRelationship.JoinThisColumn.AddRange(leftKeyExpression); + break; + case "MapRightKey": + if (parsedRelationship == null) + break; + + var rightKeyExpression = invocationExpression.Arguments + .OfType() + .Select(e => e.Value.ToString()); + + parsedRelationship.JoinOtherColumn.AddRange(rightKeyExpression); + break; + } + + return base.VisitInvocationExpression(invocationExpression, data); + } + } + + public static class MappingParser + { + public static ParsedEntity Parse(string mappingFile) + { + if (string.IsNullOrEmpty(mappingFile) || !File.Exists(mappingFile)) + return null; + + var parser = new CSharpParser(); + CompilationUnit compilationUnit; + + using (var stream = File.OpenText(mappingFile)) + compilationUnit = parser.Parse(stream, mappingFile); + + var visitor = new MappingVisitor(); + + visitor.VisitCompilationUnit(compilationUnit, null); + var parsedEntity = visitor.ParsedEntity; + + if (parsedEntity != null) + Debug.WriteLine("Parsed Mapping File: '{0}'; Properties: {1}; Relationships: {2}", + Path.GetFileName(mappingFile), + parsedEntity.Properties.Count, + parsedEntity.Relationships.Count); + + return parsedEntity; + } + } + #endregion + + #region Context Parser + [DebuggerDisplay("Context: {ContextClass}")] + public class ParsedContext + { + public ParsedContext() + { + Properties = new List(); + } + + public string ContextClass { get; set; } + + public List Properties { get; private set; } + } + + [DebuggerDisplay("Entity: {EntityClass}, Property: {ContextProperty}")] + public class ParsedEntitySet + { + public string EntityClass { get; set; } + public string ContextProperty { get; set; } + } + + public class ContextVisitor : DepthFirstAstVisitor + { + public ContextVisitor() + { + ContextBaseType = "DbContext"; + DataSetType = "DbSet"; + } + + public string ContextBaseType { get; set; } + public string DataSetType { get; set; } + + public ParsedContext ParsedContext { get; set; } + + public override object VisitTypeDeclaration(TypeDeclaration typeDeclaration, object data) + { + var baseType = typeDeclaration.BaseTypes + .OfType() + .FirstOrDefault(); + + // warning: if inherited from custom base type, this will break + // anyway to improve this? + if (baseType == null || baseType.MemberName != ContextBaseType) + return base.VisitTypeDeclaration(typeDeclaration, data); + + if (ParsedContext == null) + ParsedContext = new ParsedContext(); + + ParsedContext.ContextClass = typeDeclaration.Name; + + return base.VisitTypeDeclaration(typeDeclaration, ParsedContext); + } + + public override object VisitPropertyDeclaration(PropertyDeclaration propertyDeclaration, object data) + { + if (data == null) + return base.VisitPropertyDeclaration(propertyDeclaration, null); + + // look for property to return generic DbSet type + var memberType = propertyDeclaration.ReturnType as MemberType; + if (memberType == null || memberType.MemberName != DataSetType) + return base.VisitPropertyDeclaration(propertyDeclaration, data); + + // get the first generic type + var entityType = memberType.TypeArguments + .OfType() + .FirstOrDefault(); + + if (entityType == null) + return base.VisitPropertyDeclaration(propertyDeclaration, data); + + var entitySet = new ParsedEntitySet + { + EntityClass = entityType.MemberName, + ContextProperty = propertyDeclaration.Name + }; + + ParsedContext.Properties.Add(entitySet); + + return base.VisitPropertyDeclaration(propertyDeclaration, data); + } + } + + public static class ContextParser + { + public static ParsedContext Parse(string contextFile) + { + if (string.IsNullOrEmpty(contextFile) || !File.Exists(contextFile)) + return null; + + var parser = new CSharpParser(); + CompilationUnit compilationUnit; + + using (var stream = File.OpenText(contextFile)) + compilationUnit = parser.Parse(stream, contextFile); + + var visitor = new ContextVisitor(); + + visitor.VisitCompilationUnit(compilationUnit, null); + var parsedContext = visitor.ParsedContext; + + if (parsedContext != null) + Debug.WriteLine("Parsed Context File: '{0}'; Entities: {1}", + Path.GetFileName(contextFile), + parsedContext.Properties.Count); + + return parsedContext; + } + } + #endregion + + public static class Synchronizer + { + public static bool UpdateFromSource(EntityContext generatedContext, string contextDirectory, string mappingDirectory) + { + if (generatedContext == null) + return false; + + // make sure to update the entities before the context + UpdateFromMapping(generatedContext, mappingDirectory); + UpdateFromContext(generatedContext, contextDirectory); + return true; + } + + private static void UpdateFromContext(EntityContext generatedContext, string contextDirectory) + { + if (generatedContext == null + || contextDirectory == null + || !Directory.Exists(contextDirectory)) + return; + + // parse context + ParsedContext parsedContext = null; + var files = Directory.EnumerateFiles(contextDirectory, "*.Generated.cs").GetEnumerator(); + while (files.MoveNext() && parsedContext == null) + parsedContext = ContextParser.Parse(files.Current); + + if (parsedContext == null) + return; + + if (generatedContext.ClassName != parsedContext.ContextClass) + { + Debug.WriteLine("Rename Context Class'{0}' to '{1}'.", + generatedContext.ClassName, + parsedContext.ContextClass); + + generatedContext.ClassName = parsedContext.ContextClass; + } + + foreach (var parsedProperty in parsedContext.Properties) + { + var entity = generatedContext.Entities.ByClass(parsedProperty.EntityClass); + if (entity == null) + continue; + + + if (entity.ContextName == parsedProperty.ContextProperty) + continue; + + Debug.WriteLine("Rename Context Property'{0}' to '{1}'.", + entity.ContextName, + parsedProperty.ContextProperty); + + entity.ContextName = parsedProperty.ContextProperty; + } + } + + private static void UpdateFromMapping(EntityContext generatedContext, string mappingDirectory) + { + if (generatedContext == null + || mappingDirectory == null + || !Directory.Exists(mappingDirectory)) + return; + + // parse all mapping files + var mappingFiles = Directory.EnumerateFiles(mappingDirectory, "*.Generated.cs"); + var parsedEntities = mappingFiles + .Select(MappingParser.Parse) + .Where(parsedEntity => parsedEntity != null) + .ToList(); + + var relationshipQueue = new List>(); + + // update all entity and property names first because relationships are linked by property names + foreach (var parsedEntity in parsedEntities) + { + // find entity by table name to support renaming entity + var entity = generatedContext.Entities + .ByTable(parsedEntity.TableName, parsedEntity.TableSchema); + + if (entity == null) + continue; + + // sync names + if (entity.MappingName != parsedEntity.MappingClass) + { + Debug.WriteLine("Rename Mapping Class'{0}' to '{1}'.", + entity.MappingName, + parsedEntity.MappingClass); + + entity.MappingName = parsedEntity.MappingClass; + } + + // use rename api to make sure all instances are renamed + generatedContext.RenameEntity(entity.ClassName, parsedEntity.EntityClass); + + // sync properties + foreach (var parsedProperty in parsedEntity.Properties) + { + // find property by column name to support property name rename + var property = entity.Properties.ByColumn(parsedProperty.ColumnName); + if (property == null) + continue; + + // use rename api to make sure all instances are renamed + generatedContext.RenameProperty( + entity.ClassName, + property.PropertyName, + parsedProperty.PropertyName); + } + + // save relationship for later processing + var item = new Tuple(entity, parsedEntity); + relationshipQueue.Add(item); + } + + // update relationships last + foreach (var tuple in relationshipQueue) + UpdateRelationships(generatedContext, tuple.Item1, tuple.Item2); + } + + private static void UpdateRelationships(EntityContext generatedContext, Entity entity, ParsedEntity parsedEntity) + { + // sync relationships + foreach (var parsedRelationship in parsedEntity.Relationships.Where(r => r.JoinTable == null)) + { + var parsedProperties = parsedRelationship.ThisProperties; + var relationship = entity.Relationships + .Where(r => !r.IsManyToMany) + .FirstOrDefault(r => r.ThisProperties.Except(parsedProperties).Count() == 0); + + if (relationship == null) + continue; + + bool isThisSame = relationship.ThisPropertyName == parsedRelationship.ThisPropertyName; + bool isOtherSame = relationship.OtherPropertyName == parsedRelationship.OtherPropertyName; + + if (isThisSame && isOtherSame) + continue; + + if (!isThisSame) + { + Debug.WriteLine("Rename Relationship Property '{0}.{1}' to '{0}.{2}'.", + relationship.ThisEntity, + relationship.ThisPropertyName, + parsedRelationship.ThisPropertyName); + + relationship.ThisPropertyName = parsedRelationship.ThisPropertyName; + } + if (!isOtherSame) + { + Debug.WriteLine("Rename Relationship Property '{0}.{1}' to '{0}.{2}'.", + relationship.OtherEntity, + relationship.OtherPropertyName, + parsedRelationship.OtherPropertyName); + + relationship.OtherPropertyName = parsedRelationship.OtherPropertyName; + } + + // sync other relationship + var otherEntity = generatedContext.Entities.ByClass(relationship.OtherEntity); + if (otherEntity == null) + continue; + + var otherRelationship = otherEntity.Relationships.ByName(relationship.RelationshipName); + if (otherRelationship == null) + continue; + + otherRelationship.ThisPropertyName = relationship.OtherPropertyName; + otherRelationship.OtherPropertyName = relationship.ThisPropertyName; + } + + // sync many to many + foreach (var parsedRelationship in parsedEntity.Relationships.Where(r => r.JoinTable != null)) + { + var joinThisColumn = parsedRelationship.JoinThisColumn; + var joinOtherColumn = parsedRelationship.JoinOtherColumn; + + var relationship = entity.Relationships + .Where(r => r.IsManyToMany) + .FirstOrDefault(r => + r.JoinThisColumn.Except(joinThisColumn).Count() == 0 && + r.JoinOtherColumn.Except(joinOtherColumn).Count() == 0 && + r.JoinTable == parsedRelationship.JoinTable && + r.JoinSchema == parsedRelationship.JoinSchema); + + if (relationship == null) + continue; + + + bool isThisSame = relationship.ThisPropertyName == parsedRelationship.ThisPropertyName; + bool isOtherSame = relationship.OtherPropertyName == parsedRelationship.OtherPropertyName; + + if (isThisSame && isOtherSame) + continue; + + if (!isThisSame) + { + Debug.WriteLine("Rename Relationship Property '{0}.{1}' to '{0}.{2}'.", + relationship.ThisEntity, + relationship.ThisPropertyName, + parsedRelationship.ThisPropertyName); + + relationship.ThisPropertyName = parsedRelationship.ThisPropertyName; + } + if (!isOtherSame) + { + Debug.WriteLine("Rename Relationship Property '{0}.{1}' to '{0}.{2}'.", + relationship.OtherEntity, + relationship.OtherPropertyName, + parsedRelationship.OtherPropertyName); + + relationship.OtherPropertyName = parsedRelationship.OtherPropertyName; + } + + // sync other relationship + var otherEntity = generatedContext.Entities.ByClass(relationship.OtherEntity); + if (otherEntity == null) + continue; + + var otherRelationship = otherEntity.Relationships.ByName(relationship.RelationshipName); + if (otherRelationship == null) + continue; + + otherRelationship.ThisPropertyName = relationship.OtherPropertyName; + otherRelationship.OtherPropertyName = relationship.ThisPropertyName; + } + } + } +} diff --git a/CodeSmith/CSharp/Web/Controller.cst b/CodeSmith/CSharp/Web/Controller.cst new file mode 100644 index 0000000..9957a1c --- /dev/null +++ b/CodeSmith/CSharp/Web/Controller.cst @@ -0,0 +1,105 @@ +<%-- +Name: Database Table Properties +Author: yubaolee +Description: Create a list of properties from a database table +--%> +<%@ CodeTemplate Language="C#" Encoding="utf-8" TargetLanguage="C#" Debug="False" Description="控制器" %> +<%@ Property Name="ModuleName" Type="String" Category="Context" Description="模块名称" %> +<%@ Map Name="CSharpAlias" Src="System-CSharpAlias" Description="System to C# Type Map" %> +<%@ Assembly Name="SchemaExplorer" %> +<%@ Import Namespace="SchemaExplorer" %> + +using System; +using System.Collections.Generic; +using System.Linq; +using Infrastructure; +using Microsoft.AspNetCore.Mvc; +using OpenAuth.App; +using OpenAuth.App.Interface; +using OpenAuth.App.Request; +using OpenAuth.Repository.Domain; + +namespace OpenAuth.Mvc.Controllers +{ + public class <%=ModuleName%>sController : BaseController + { + private readonly <%=ModuleName%>App _app; + + public <%=ModuleName%>sController(<%=ModuleName%>App app, IAuth auth) : base(auth) + { + _app = app; + } + + //主页 + public ActionResult Index() + { + return View(); + } + + /// + /// MVC界面添加 + /// + /// + /// + [HttpPost] + public string Add(AddOrUpdate<%=ModuleName%>Req obj) + { + try + { + _app.Add(obj); + + } + catch (Exception ex) + { + Result.Code = 500; + Result.Message = ex.Message; + } + return JsonHelper.Instance.Serialize(Result); + } + + /// + /// MVC界面修改 + /// + /// + /// + [HttpPost] + public string Update(AddOrUpdate<%=ModuleName%>Req obj) + { + try + { + _app.Update(obj); + + } + catch (Exception ex) + { + Result.Code = 500; + Result.Message = ex.Message; + } + return JsonHelper.Instance.Serialize(Result); + } + + /// + /// 加载列表 + /// + public string Load([FromQuery]Query<%=ModuleName%>ListReq request) + { + return JsonHelper.Instance.Serialize(_app.Load(request)); + } + + [HttpPost] + public string Delete(string[] ids) + { + try + { + _app.Delete(ids); + } + catch (Exception e) + { + Result.Code = 500; + Result.Message = e.Message; + } + + return JsonHelper.Instance.Serialize(Result); + } + } +} \ No newline at end of file diff --git a/CodeSmith/CSharp/Web/Index.cshtml.cst b/CodeSmith/CSharp/Web/Index.cshtml.cst new file mode 100644 index 0000000..7618888 --- /dev/null +++ b/CodeSmith/CSharp/Web/Index.cshtml.cst @@ -0,0 +1,98 @@ +<%-- +Name: 列表页面 +Author: yubaolee +Description: 列表页面 +--%> +<%@ CodeTemplate Language="C#" TargetLanguage="C#" Debug="False" Encoding="utf-8" Description="添加模块" %> +<%@ Property Name="SourceTable" Type="SchemaExplorer.TableSchema" Category="Context" +Description="连接的数据库" %> +<%@ Property Name="ModuleName" Type="String" Category="Context" Description="模块名称" %> + +<%@ Map Name="CSharpAlias" Src="System-CSharpAlias" Description="System to C# Type Map" %> +<%@ Assembly Name="SchemaExplorer" %> +<%@ Import Namespace="SchemaExplorer" %> +<%@ Assembly Src="Util.cs" %> +<%@ Import Namespace="Util" %> + +@section header +{ + +} + + +
+ + +
+ + + +
+ + + + + + + + diff --git a/CodeSmith/CSharp/Web/Util.cs b/CodeSmith/CSharp/Web/Util.cs new file mode 100644 index 0000000..a6832e4 --- /dev/null +++ b/CodeSmith/CSharp/Web/Util.cs @@ -0,0 +1,39 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Text; +using CodeSmith.Engine; +using SchemaExplorer; + +namespace Util{ + public class Tools{ + public static String GetDescription(ColumnSchema column) { //得到字段的描述 + if(string.IsNullOrEmpty(column.Description)) + return column.Name; + else + return column.Description; + } + + public static bool NeedCascade(TableSchema SourceTable){ //判断表中是否需要下拉选择树 + return SourceTable.Columns.Contains("ParentId") + || SourceTable.Columns.Contains("CascadeId") ; + } + + + public static string CreateBlank(int level){ + if(level == 1){ + return " "; + } + else{ + var twoblanks = " "; + for (int i = level-1; i > 1; i--) + { + twoblanks +=twoblanks; + } + return CreateBlank(1) + twoblanks; + } + } + } +} \ No newline at end of file diff --git a/CodeSmith/CSharp/Web/index.js.cst b/CodeSmith/CSharp/Web/index.js.cst new file mode 100644 index 0000000..f76bdda --- /dev/null +++ b/CodeSmith/CSharp/Web/index.js.cst @@ -0,0 +1,186 @@ +<%-- +Name: 主JS界面 +Author: yubaolee +--%> +<%@ CodeTemplate Language="C#" TargetLanguage="C#" Debug="False" Encoding="utf-8" Description="添加模块" %> +<%@ Property Name="ModuleName" Type="String" Category="Context" Description="模块名称" %> + +<%@ Map Name="CSharpAlias" Src="System-CSharpAlias" Description="System to C# Type Map" %> +<%@ Assembly Name="SchemaExplorer" %> +<%@ Import Namespace="SchemaExplorer" %> +<%@ Assembly Src="Util.cs" %> +<%@ Import Namespace="Util" %> + +layui.config({ + base: "/js/" +}).use(['form', 'vue', 'ztree', 'layer', 'jquery', 'table', 'droptree', 'openauth', 'utils'], function () { + var form = layui.form, + layer = layui.layer, + $ = layui.jquery; + var table = layui.table; + var openauth = layui.openauth; + var toplayer = (top == undefined || top.layer === undefined) ? layer : top.layer; //顶层的LAYER + + $("#menus").loadMenus("<%=ModuleName%>"); + + + //加载表头 + $.getJSON('/<%=ModuleName%>s/Load', + { page: 1, limit: 1 }, + function (data) { + var columns = data.columnFields.filter(u => u.IsList ===true).map(function (e) { + return { + field: e.ColumnName, + title: e.Comment + }; + }); + columns.unshift({ + type: 'checkbox', + fixed: 'left' + }); + table.render({ + elem: '#mainList', + page: true, + url: '/<%=ModuleName%>s/Load', + cols: [columns] + , response: { + statusCode: 200 //规定成功的状态码,默认:0 + } + }); + }); + + + //主列表加载,可反复调用进行刷新 + var config = {}; //table的参数,如搜索key,点击tree的id + var mainList = function(options) { + if (options != undefined) { + $.extend(config, options); + } + table.reload('mainList', + { + url: '/<%=ModuleName%>s/Load', + where: config + , response: { + statusCode: 200 //规定成功的状态码,默认:0 + } + }); + }; + mainList(); + + //添加(编辑)对话框 + var editDlg = function () { + var vm; + var update = false; //是否为更新 + var show = function (data) { + var title = update ? "编辑信息" : "添加"; + layer.open({ + title: title, + area: ["500px", "400px"], + type: 1, + content: $('#divEdit'), + success: function () { + if(vm == undefined){ + vm = new Vue({ + el: "#formEdit", + data(){ + return { + tmp:data //使用一个tmp封装一下,后面可以直接用vm.tmp赋值 + } + }, + watch:{ + tmp(val){ + this.$nextTick(function () { + form.render(); //刷新select等 + layui.droptree("/Applications/GetList", "#AppName", "#AppId", false); + + }) + } + }, + mounted(){ + form.render(); + layui.droptree("/Applications/GetList", "#AppName", "#AppId", false); + + } + }); + }else{ + vm.tmp = Object.assign({}, vm.tmp,data) + } + }, + end: mainList + }); + var url = "/<%=ModuleName%>s/Add"; + if (update) { + url = "/<%=ModuleName%>s/Update"; + } + //提交数据 + form.on('submit(formSubmit)', + function (data) { + $.post(url, + data.field, + function (data) { + layer.msg(data.Message); + }, + "json"); + return false; + }); + } + return { + add: function () { //弹出添加 + update = false; + show({ + Id: '' + }); + }, + update: function (data) { //弹出编辑框 + update = true; + show(data); + } + }; + }(); + + //监听表格内部按钮 + table.on('tool(list)', function (obj) { + var data = obj.data; + if (obj.event === 'detail') { //查看 + layer.msg('ID:' + data.Id + ' 的查看操作'); + } + }); + + + //监听页面主按钮操作 + var active = { + btnDel: function () { //批量删除 + var checkStatus = table.checkStatus('mainList') + , data = checkStatus.data; + openauth.del("/<%=ModuleName%>s/Delete", + data.map(function (e) { return e.Id; }), + mainList); + } + , btnAdd: function () { //添加 + editDlg.add(); + } + , btnEdit: function () { //编辑 + var checkStatus = table.checkStatus('mainList') + , data = checkStatus.data; + if (data.length != 1) { + layer.msg("请选择编辑的行,且同时只能编辑一行"); + return; + } + editDlg.update(data[0]); + } + + , search: function () { //搜索 + mainList({ key: $('#key').val() }); + } + , btnRefresh: function () { + mainList(); + } + }; + + $('.toolList .layui-btn').on('click', function () { + var type = $(this).data('type'); + active[type] ? active[type].call(this) : ''; + }); + + //监听页面主按钮操作 end +}) diff --git a/CodeSmith/CSharp/WebGenerate.cst b/CodeSmith/CSharp/WebGenerate.cst new file mode 100644 index 0000000..c050980 --- /dev/null +++ b/CodeSmith/CSharp/WebGenerate.cst @@ -0,0 +1,130 @@ +<%-- +Author: yubaolee +Description: 用于生成OpenAuth.Core开源版前端Web界面,包括mvc controller/csthml/js +--%> +<%@ Template Language="C#" TargetLanguage="Text" Debug="True" OutputType="None" %> + +<%@ Assembly Name="SchemaExplorer" %> +<%@ Assembly Name="CodeSmith.CustomProperties" %> + +<%@ Assembly Name="Mono.Cecil" Path="..\Common" %> +<%@ Assembly Name="ICSharpCode.NRefactory" Path="..\Common" %> +<%@ Assembly Name="ICSharpCode.NRefactory.CSharp" Path="..\Common" %> + +<%@ Assembly Src="Internal\Model.cs" %> +<%@ Assembly Src="Internal\Extensions.cs" %> +<%@ Assembly Src="Internal\Generator.cs" %> +<%@ Assembly Src="Internal\Parser.cs" %> + +<%@ Import Namespace="System.Collections.Generic" %> +<%@ Import Namespace="System.IO" %> +<%@ Import Namespace="System.Linq" %> +<%@ Import Namespace="System.Text" %> +<%@ Import Namespace="System.Text.RegularExpressions" %> + +<%@ Import Namespace="SchemaMapper" %> + +<%@ Property Name="SourceTable" +Type="SchemaExplorer.TableSchema" +Category="Context" +Description="连接的数据库" +OnChanged="OnSourceDatabaseChanged"%> + +<%@ Property Name="ModuleName" + Type="System.String" + Description="模块名称,如:User"%> +<%@ Property Name="directory" + Type="System.String" + Default=".\" + Optional="True" + Description="代码生成路径" + Editor="System.Windows.Forms.Design.FolderNameEditor, System.Design, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" %> + +<%@ Register Name="HtmlGenerateClass" + Template="Web\Index.cshtml.cst" + MergeProperties="False" %> +<%@ Register Name="JSGenerateClass" + Template="Web\index.js.cst" + MergeProperties="False" %> +<%@ Register Name="ControllerGenerateClass" + Template="Web\Controller.cst" + MergeProperties="False" %> +Generating Entities ... +<% Generate(); %> + + \ No newline at end of file diff --git a/CodeSmith/Common/ICSharpCode.NRefactory.CSharp.dll b/CodeSmith/Common/ICSharpCode.NRefactory.CSharp.dll new file mode 100644 index 0000000..978ef71 Binary files /dev/null and b/CodeSmith/Common/ICSharpCode.NRefactory.CSharp.dll differ diff --git a/CodeSmith/Common/ICSharpCode.NRefactory.dll b/CodeSmith/Common/ICSharpCode.NRefactory.dll new file mode 100644 index 0000000..7a156bc Binary files /dev/null and b/CodeSmith/Common/ICSharpCode.NRefactory.dll differ diff --git a/CodeSmith/Common/Mono.Cecil.dll b/CodeSmith/Common/Mono.Cecil.dll new file mode 100644 index 0000000..d22da17 Binary files /dev/null and b/CodeSmith/Common/Mono.Cecil.dll differ diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..b7a9eed --- /dev/null +++ b/Dockerfile @@ -0,0 +1,36 @@ +##See https://aka.ms/customizecontainer to learn how to customize your debug container and how Visual Studio uses this Dockerfile to build your images for faster debugging. +#FROM mcr.microsoft.com/dotnet/aspnet:6.0 AS base +#WORKDIR /app +#EXPOSE 10010 +# +#COPY . . +#ENV ASPNETCORE_URLS=http://+:10010 +#ENTRYPOINT ["dotnet", "/app/OpenAuth.WebApi.dll"] + + +#See https://aka.ms/customizecontainer to learn how to customize your debug container and how Visual Studio uses this Dockerfile to build your images for faster debugging. + +FROM mcr.microsoft.com/dotnet/aspnet:6.0 AS base +WORKDIR /app +EXPOSE 10010 + +FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build +WORKDIR /src +COPY ["OpenAuth.WebApi/OpenAuth.WebApi.csproj", "OpenAuth.WebApi/"] +COPY ["Infrastructure/Infrastructure.csproj", "Infrastructure/"] +COPY ["OpenAuth.App/OpenAuth.App.csproj", "OpenAuth.App/"] +COPY ["OpenAuth.Repository/OpenAuth.Repository.csproj", "OpenAuth.Repository/"] +RUN dotnet restore "OpenAuth.WebApi/OpenAuth.WebApi.csproj" +COPY . . +WORKDIR "/src/OpenAuth.WebApi" +RUN dotnet build "OpenAuth.WebApi.csproj" -c Release -o /app/build + +FROM build AS publish +RUN dotnet publish "OpenAuth.WebApi.csproj" -c Release -o /app/publish /p:UseAppHost=false + +FROM base AS final +WORKDIR /app +COPY --from=publish /app/publish . +ENV ASPNETCORE_URLS=http://+:10010 +ENTRYPOINT ["dotnet", "OpenAuth.WebApi.dll"] + diff --git a/Infrastructure/AppSetting.cs b/Infrastructure/AppSetting.cs new file mode 100644 index 0000000..861f385 --- /dev/null +++ b/Infrastructure/AppSetting.cs @@ -0,0 +1,55 @@ +using System.Collections.Generic; + +namespace Infrastructure +{ + /// + /// 配置项 + /// + public class AppSetting + { + + public AppSetting() + { + SSOPassport = "http://localhost:52789"; + Version = ""; + UploadPath = ""; + IdentityServerUrl = ""; + } + /// + /// SSO地址 + /// + public string SSOPassport { get; set; } + + /// + /// 版本信息 + /// 如果为demo,则屏蔽Post请求 + /// + public string Version { get; set; } + + /// + /// 数据库类型 SqlServer、MySql + /// + public Dictionary DbTypes { get; set; } + + /// 附件上传路径 + public string UploadPath { get; set; } + + //identity授权的地址 + public string IdentityServerUrl { get; set; } + + /// + /// Redis服务器配置 + /// + //public string RedisConf { get; set; } + public RedisConf RedisConf { get; set; } + + //是否是Identity授权方式 + public bool IsIdentityAuth => !string.IsNullOrEmpty(IdentityServerUrl); + } + + public class RedisConf + { + public string Conn { get; set; } + public int Database { get; set; } + } +} diff --git a/Infrastructure/AutoMapperExt.cs b/Infrastructure/AutoMapperExt.cs new file mode 100644 index 0000000..7dc7c53 --- /dev/null +++ b/Infrastructure/AutoMapperExt.cs @@ -0,0 +1,74 @@ +// *********************************************************************** +// Assembly : FairUtility +// Author : Yubao Li +// Created : 08-27-2015 +// +// Last Modified By : Yubao Li +// Last Modified On : 08-27-2015 +// *********************************************************************** +// +// Copyright (c) . All rights reserved. +// +// +// *********************************************************************** + +using System; +using AutoMapper; +using System.Collections; +using System.Collections.Generic; + +namespace Infrastructure +{ + public static class AutoMapperExt + { + /// + /// 类型映射 + /// + public static T MapTo(this object obj) + { + if (obj == null) return default(T); + + var config = new MapperConfiguration(cfg=>cfg.CreateMap(obj.GetType(),typeof(T))); + var mapper = config.CreateMapper(); + return mapper.Map(obj); + } + + /// + /// 集合列表类型映射 + /// + public static List MapToList(this IEnumerable source) + { + Type sourceType = source.GetType().GetGenericArguments()[0]; //获取枚举的成员类型 + var config = new MapperConfiguration(cfg => cfg.CreateMap(sourceType, typeof(TDestination))); + var mapper = config.CreateMapper(); + + return mapper.Map>(source); + } + + /// + /// 集合列表类型映射 + /// + public static List MapToList(this IEnumerable source) + { + var config = new MapperConfiguration(cfg => cfg.CreateMap(typeof(TSource), typeof(TDestination))); + var mapper = config.CreateMapper(); + + return mapper.Map>(source); + } + + /// + /// 类型映射 + /// + public static TDestination MapTo(this TSource source, TDestination destination) + where TSource : class + where TDestination : class + { + if (source == null) return destination; + + var config = new MapperConfiguration(cfg => cfg.CreateMap(typeof(TSource), typeof(TDestination))); + var mapper = config.CreateMapper(); + return mapper.Map(source); + } + + } +} \ No newline at end of file diff --git a/Infrastructure/Cache/CacheContext.cs b/Infrastructure/Cache/CacheContext.cs new file mode 100644 index 0000000..a11f0a0 --- /dev/null +++ b/Infrastructure/Cache/CacheContext.cs @@ -0,0 +1,88 @@ +// *********************************************************************** +// Assembly : Helper +// Author : yubaolee +// Created : 12-16-2016 +// +// Last Modified By : yubaolee +// Last Modified On : 12-21-2016 +// 使用微软默认带超时的Cache +// File: CacheContext.cs +// *********************************************************************** + +using System; +using System.Collections.Concurrent; +using Microsoft.Extensions.Caching.Memory; + +namespace Infrastructure.Cache +{ + public class CacheContext : ICacheContext + { + private IMemoryCache _objCache; + private static readonly ConcurrentDictionary> queueDic = new ConcurrentDictionary>(); + public CacheContext(IMemoryCache objCache) + { + _objCache = objCache; + } + + public override T Get(string key) + { + return _objCache.Get(key); + } + + public override bool Set(string key, T t, DateTime expire) + { + var obj = Get(key); + if (obj != null) + { + Remove(key); + } + + _objCache.Set(key, t, new MemoryCacheEntryOptions() + .SetAbsoluteExpiration(expire)); //绝对过期时间 + + return true; + } + + public override bool Remove(string key) + { + _objCache.Remove(key); + return true; + } + /// + /// 添加并发锁 + /// + /// 值 + /// 执行方法 + /// 键 + /// + public override async Task Lock(string id, Func> func, string key = "hc") + { + var queue = queueDic.GetOrAdd(id, new ConcurrentQueue()); + var token = Guid.NewGuid().ToString(); + queue.Enqueue(token); + queue.TryPeek(out string newToken); + if (token != newToken) + { + while (true) + { + Thread.Sleep(10); + queue.TryPeek(out newToken); + if (token == newToken) + { + break; + } + } + } + + try + { + return await func(id); + } + finally + { + queue.TryDequeue(out string _); + } + + } + } +} diff --git a/Infrastructure/Cache/EnyimMemcachedContext.cs b/Infrastructure/Cache/EnyimMemcachedContext.cs new file mode 100644 index 0000000..c2fc64c --- /dev/null +++ b/Infrastructure/Cache/EnyimMemcachedContext.cs @@ -0,0 +1,55 @@ +// *********************************************************************** +// Assembly : Infrastructure +// Author : yubaolee +// Created : 06-21-2016 +// +// Last Modified By : yubaolee +// Last Modified On : 06-21-2016 +// Contact : Add services.AddEnyimMemcached(...) +// and app.UseEnyimMemcached() in Startup. +// File: EnyimMemcachedContext.cs +// *********************************************************************** + + + +using System; +using Enyim.Caching; +using Enyim.Caching.Memcached; + +namespace Infrastructure.Cache +{ + public sealed class EnyimMemcachedContext : ICacheContext + { + private IMemcachedClient _memcachedClient; + + public EnyimMemcachedContext(IMemcachedClient client) + { + _memcachedClient = client; + } + + /// + /// + /// + /// + /// + /// + public override T Get(string key) + { + return _memcachedClient.Get(key); + } + + public override bool Set(string key, T t, DateTime expire) + { + return _memcachedClient.Store(StoreMode.Set, key, t, expire); + } + + public override bool Remove(string key) + { + return _memcachedClient.Remove(key); + } + public override Task Lock(string id, Func> func, string key = "hc") + { + return func(id); + } + } +} \ No newline at end of file diff --git a/Infrastructure/Cache/ICacheContext.cs b/Infrastructure/Cache/ICacheContext.cs new file mode 100644 index 0000000..89580b7 --- /dev/null +++ b/Infrastructure/Cache/ICacheContext.cs @@ -0,0 +1,36 @@ +using System; + +namespace Infrastructure.Cache +{ + /// + /// 缓存接口 + /// + public abstract class ICacheContext + { + /// + /// 获取缓存项 + /// + /// 缓存对象类型 + /// 键 + /// 缓存对象 + public abstract T Get(string key); + + /// + /// 设置缓存项 + /// + /// 缓存对象类型 + /// 键 + /// 缓存对象 + /// true成功,false失败 + public abstract bool Set(string key, T t, DateTime expire); + + /// + /// 移除一个缓存项 + /// + /// 缓存项key + /// true成功,false失败 + public abstract bool Remove(string key); + public abstract Task Lock(string id, Func> func, string key = "hc"); + + } +} \ No newline at end of file diff --git a/Infrastructure/Cache/RedisCacheContext.cs b/Infrastructure/Cache/RedisCacheContext.cs new file mode 100644 index 0000000..ceedd6d --- /dev/null +++ b/Infrastructure/Cache/RedisCacheContext.cs @@ -0,0 +1,63 @@ +using System; +using Enyim.Caching; +using Enyim.Caching.Memcached; +using Microsoft.Extensions.Options; +using StackExchange.Redis; + +namespace Infrastructure.Cache +{ + /// + /// 缓存redis实现 + /// + public sealed class RedisCacheContext : ICacheContext + { + private ConnectionMultiplexer _conn { get; set; } + private IDatabase iDatabase { get; set; } + + public RedisCacheContext(IOptions options) + { + _conn = ConnectionMultiplexer.Connect(options.Value.RedisConf.Conn); + iDatabase = _conn.GetDatabase(options.Value.RedisConf.Database); + } + + public override T Get(string key) + { + RedisValue value = iDatabase.StringGet(key); + if (!value.HasValue) + { + return default(T); + } + + if (typeof(T) == typeof(string)) + { + return (T)Convert.ChangeType(value, typeof(T)); + } + else + { + return JsonHelper.Instance.Deserialize(value); + } + } + + public override bool Set(string key, T t, DateTime expire) + { + if (typeof(T) == typeof(string)) + { + return iDatabase.StringSet(key, t.ToString(), expire - DateTime.Now); + } + else + { + return iDatabase.StringSet(key, JsonHelper.Instance.Serialize(t), expire - DateTime.Now); + } + } + + public override bool Remove(string key) + { + return iDatabase.KeyDelete(key); + } + + public override Task Lock(string id, Func> func, string key = "hc") + { + return func(id); + } + } +} \ No newline at end of file diff --git a/Infrastructure/CommonException.cs b/Infrastructure/CommonException.cs new file mode 100644 index 0000000..32e0cc2 --- /dev/null +++ b/Infrastructure/CommonException.cs @@ -0,0 +1,22 @@ + +using System; + +namespace Infrastructure +{ + public class CommonException : Exception + { + private int _code; + + public CommonException(string message, int code) + : base(message) + { + this._code = code; + } + + public int Code + { + get { return _code; } + } + + } +} diff --git a/Infrastructure/Const/FlowInstanceStatus.cs b/Infrastructure/Const/FlowInstanceStatus.cs new file mode 100644 index 0000000..3e3c86a --- /dev/null +++ b/Infrastructure/Const/FlowInstanceStatus.cs @@ -0,0 +1,30 @@ +namespace Infrastructure.Const +{ + /// + /// 流程状态 + /// + public struct FlowInstanceStatus + { + /// + /// 撤销、召回 + /// + public const int Draft = -1; + /// + /// 正在运行 + /// + public const int Running = 0; + /// + /// 完成 + /// + public const int Finished = 1; + /// + /// 不同意 + /// + public const int Disagree = 3; + /// + /// 驳回 + /// + public const int Rejected = 4; + + } +} \ No newline at end of file diff --git a/Infrastructure/Const/HtmlElementType.cs b/Infrastructure/Const/HtmlElementType.cs new file mode 100644 index 0000000..0389706 --- /dev/null +++ b/Infrastructure/Const/HtmlElementType.cs @@ -0,0 +1,26 @@ +namespace Infrastructure.Const +{ + public struct HtmlElementType + { + public const string drop = "drop"; + public const string droplist = "droplist"; + public const string select = "select"; + public const string selectlist = "selectlist"; + public const string checkbox = "checkbox"; + public const string textarea = "textarea"; + public const string thanorequal = "thanorequal"; + public const string lessorequal = "lessorequal"; + + + public const string gt = "gt"; + public const string lt = "lt"; + public const string GT = ">"; + public const string LT = "<"; + public const string like = "like"; + + public const string ThanOrEqual = ">="; + public const string LessOrequal = "<="; + public const string Contains = "in"; + public const string Equal = "="; + } +} \ No newline at end of file diff --git a/Infrastructure/Const/JobStatus.cs b/Infrastructure/Const/JobStatus.cs new file mode 100644 index 0000000..7616d75 --- /dev/null +++ b/Infrastructure/Const/JobStatus.cs @@ -0,0 +1,17 @@ +namespace Infrastructure.Const +{ + /// + /// 定时任务状态 + /// + public enum JobStatus + { + /// + /// 未启动 + /// + NotRun, + /// + /// 正在运行 + /// + Running + } +} \ No newline at end of file diff --git a/Infrastructure/Const/LinqExpressionType.cs b/Infrastructure/Const/LinqExpressionType.cs new file mode 100644 index 0000000..ecc32e2 --- /dev/null +++ b/Infrastructure/Const/LinqExpressionType.cs @@ -0,0 +1,15 @@ +namespace Infrastructure.Const +{ + public enum LinqExpressionType + { + Equal=0,//= + NotEqual=1,//!= + GreaterThan,//> + LessThan,//< + ThanOrEqual,//>= + LessThanOrEqual,//<= + In, + Contains,//Contains + NotContains//NotContains + } +} diff --git a/Infrastructure/Const/QueryOrderBy.cs b/Infrastructure/Const/QueryOrderBy.cs new file mode 100644 index 0000000..f409707 --- /dev/null +++ b/Infrastructure/Const/QueryOrderBy.cs @@ -0,0 +1,8 @@ +namespace Infrastructure.Const +{ + public enum QueryOrderBy + { + Desc=1, + Asc=2 + } +} diff --git a/Infrastructure/Const/ResponseType.cs b/Infrastructure/Const/ResponseType.cs new file mode 100644 index 0000000..1552f90 --- /dev/null +++ b/Infrastructure/Const/ResponseType.cs @@ -0,0 +1,27 @@ +namespace Infrastructure.Const +{ + public enum ResponseType + { + ServerError = 1, + LoginExpiration = 302, + ParametersLack = 303, + TokenExpiration, + PINError, + NoPermissions, + NoRolePermissions, + LoginError, + AccountLocked, + LoginSuccess, + SaveSuccess, + AuditSuccess, + OperSuccess, + RegisterSuccess, + ModifyPwdSuccess, + EidtSuccess, + DelSuccess, + NoKey, + NoKeyDel, + KeyError, + Other + } +} diff --git a/Infrastructure/Const/SqlDbTypeName.cs b/Infrastructure/Const/SqlDbTypeName.cs new file mode 100644 index 0000000..2bc9bc7 --- /dev/null +++ b/Infrastructure/Const/SqlDbTypeName.cs @@ -0,0 +1,24 @@ +namespace Infrastructure.Const +{ + public struct SqlDbTypeName + { + public const string NVarChar = "nvarchar"; + public const string VarChar = "varchar"; + public const string NChar = "nchar"; + public const string Char = "char"; + public const string Text = "text"; + public const string Int = "int"; + public const string BigInt = "bigint"; + public const string DateTime = "datetime"; + public const string Date = "date"; + public const string SmallDateTime = "smalldatetime"; + public const string SmallDate = "smalldate"; + public const string Float = "float"; + public const string Decimal = "decimal"; + public const string Double = "double"; + public const string Bit = "bit"; + public const string Bool = "bool"; + public const string UniqueIdentifier = "uniqueidentifier"; + + } +} diff --git a/Infrastructure/Database/DbDataConvertExtensions.cs b/Infrastructure/Database/DbDataConvertExtensions.cs new file mode 100644 index 0000000..517f7ad --- /dev/null +++ b/Infrastructure/Database/DbDataConvertExtensions.cs @@ -0,0 +1,412 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations.Schema; +using System.Data; +using System.Linq; +using System.Reflection; +using System.Threading.Tasks; +using Infrastructure.Extensions; + +namespace Infrastructure.Database +{ + /// + /// 数据库数据转换拓展 + /// + public static class DbDataConvertExtensions + { + /// + /// 将 DataTable 转 List 集合 + /// + /// 返回值类型 + /// DataTable + /// List{T} + public static List ToList(this DataTable dataTable) + { + return dataTable.ToList(typeof(List)) as List; + } + + /// + /// 将 DataTable 转 List 集合 + /// + /// 返回值类型 + /// DataTable + /// List{T} + public static async Task> ToListAsync(this DataTable dataTable) + { + var list = await dataTable.ToListAsync(typeof(List)); + return list as List; + } + + /// + /// 将 DataSet 转 元组 + /// + /// 元组元素类型 + /// DataSet + /// 元组类型 + public static List ToList(this DataSet dataSet) + { + var tuple = dataSet.ToList(typeof(List)); + return tuple[0] as List; + } + + /// + /// 将 DataSet 转 元组 + /// + /// 元组元素类型 + /// 元组元素类型 + /// DataSet + /// 元组类型 + public static (List list1, List list2) ToList(this DataSet dataSet) + { + var tuple = dataSet.ToList(typeof(List), typeof(List)); + return (tuple[0] as List, tuple[1] as List); + } + + /// + /// 将 DataSet 转 元组 + /// + /// 元组元素类型 + /// 元组元素类型 + /// 元组元素类型 + /// DataSet + /// 元组类型 + public static (List list1, List list2, List list3) ToList(this DataSet dataSet) + { + var tuple = dataSet.ToList(typeof(List), typeof(List), typeof(List)); + return (tuple[0] as List, tuple[1] as List, tuple[2] as List); + } + + /// + /// 将 DataSet 转 元组 + /// + /// 元组元素类型 + /// 元组元素类型 + /// 元组元素类型 + /// 元组元素类型 + /// DataSet + /// 元组类型 + public static (List list1, List list2, List list3, List list4) ToList(this DataSet dataSet) + { + var tuple = dataSet.ToList(typeof(List), typeof(List), typeof(List), typeof(List)); + return (tuple[0] as List, tuple[1] as List, tuple[2] as List, tuple[3] as List); + } + + /// + /// 将 DataSet 转 元组 + /// + /// 元组元素类型 + /// 元组元素类型 + /// 元组元素类型 + /// 元组元素类型 + /// 元组元素类型 + /// DataSet + /// 元组类型 + public static (List list1, List list2, List list3, List list4, List list5) ToList(this DataSet dataSet) + { + var tuple = dataSet.ToList(typeof(List), typeof(List), typeof(List), typeof(List), typeof(List)); + return (tuple[0] as List, tuple[1] as List, tuple[2] as List, tuple[3] as List, tuple[4] as List); + } + + /// + /// 将 DataSet 转 元组 + /// + /// 元组元素类型 + /// 元组元素类型 + /// 元组元素类型 + /// 元组元素类型 + /// 元组元素类型 + /// 元组元素类型 + /// DataSet + /// 元组类型 + public static (List list1, List list2, List list3, List list4, List list5, List list6) ToList(this DataSet dataSet) + { + var tuple = dataSet.ToList(typeof(List), typeof(List), typeof(List), typeof(List), typeof(List), typeof(List)); + return (tuple[0] as List, tuple[1] as List, tuple[2] as List, tuple[3] as List, tuple[4] as List, tuple[5] as List); + } + + /// + /// 将 DataSet 转 元组 + /// + /// 元组元素类型 + /// 元组元素类型 + /// 元组元素类型 + /// 元组元素类型 + /// 元组元素类型 + /// 元组元素类型 + /// 元组元素类型 + /// DataSet + /// 元组类型 + public static (List list1, List list2, List list3, List list4, List list5, List list6, List list7) ToList(this DataSet dataSet) + { + var tuple = dataSet.ToList(typeof(List), typeof(List), typeof(List), typeof(List), typeof(List), typeof(List), typeof(List)); + return (tuple[0] as List, tuple[1] as List, tuple[2] as List, tuple[3] as List, tuple[4] as List, tuple[5] as List, tuple[6] as List); + } + + /// + /// 将 DataSet 转 元组 + /// + /// 元组元素类型 + /// 元组元素类型 + /// 元组元素类型 + /// 元组元素类型 + /// 元组元素类型 + /// 元组元素类型 + /// 元组元素类型 + /// 元组元素类型 + /// DataSet + /// 元组类型 + public static (List list1, List list2, List list3, List list4, List list5, List list6, List list7, List list8) ToList(this DataSet dataSet) + { + var tuple = dataSet.ToList(typeof(List), typeof(List), typeof(List), typeof(List), typeof(List), typeof(List), typeof(List), typeof(List)); + return (tuple[0] as List, tuple[1] as List, tuple[2] as List, tuple[3] as List, tuple[4] as List, tuple[5] as List, tuple[6] as List, tuple[7] as List); + } + + /// + /// 将 DataSet 转 特定类型 + /// + /// DataSet + /// 特定类型集合 + /// List{object} + public static List ToList(this DataSet dataSet, params Type[] returnTypes) + { + if (returnTypes == null || returnTypes.Length == 0) return default; + + // 处理元组类型 + if (returnTypes.Length == 1 && returnTypes[0].IsValueType) + { + returnTypes = returnTypes[0].GenericTypeArguments; + } + + // 获取所有的 DataTable + var dataTables = dataSet.Tables; + + // 处理 8 个结果集 + if (returnTypes.Length >= 8) + { + return new List + { + dataTables[0].ToList(returnTypes[0]), + dataTables[1].ToList(returnTypes[1]), + dataTables[2].ToList(returnTypes[2]), + dataTables[3].ToList(returnTypes[3]), + dataTables[4].ToList(returnTypes[4]), + dataTables[5].ToList(returnTypes[5]), + dataTables[6].ToList(returnTypes[6]), + dataTables[7].ToList(returnTypes[7]) + }; + } + // 处理 7 个结果集 + else if (returnTypes.Length == 7) + { + return new List + { + dataTables[0].ToList(returnTypes[0]), + dataTables[1].ToList(returnTypes[1]), + dataTables[2].ToList(returnTypes[2]), + dataTables[3].ToList(returnTypes[3]), + dataTables[4].ToList(returnTypes[4]), + dataTables[5].ToList(returnTypes[5]), + dataTables[6].ToList(returnTypes[6]) + }; + } + // 处理 6 个结果集 + else if (returnTypes.Length == 6) + { + return new List + { + dataTables[0].ToList(returnTypes[0]), + dataTables[1].ToList(returnTypes[1]), + dataTables[2].ToList(returnTypes[2]), + dataTables[3].ToList(returnTypes[3]), + dataTables[4].ToList(returnTypes[4]), + dataTables[5].ToList(returnTypes[5]) + }; + } + // 处理 5 个结果集 + else if (returnTypes.Length == 5) + { + return new List + { + dataTables[0].ToList(returnTypes[0]), + dataTables[1].ToList(returnTypes[1]), + dataTables[2].ToList(returnTypes[2]), + dataTables[3].ToList(returnTypes[3]), + dataTables[4].ToList(returnTypes[4]) + }; + } + // 处理 4 个结果集 + else if (returnTypes.Length == 4) + { + return new List + { + dataTables[0].ToList(returnTypes[0]), + dataTables[1].ToList(returnTypes[1]), + dataTables[2].ToList(returnTypes[2]), + dataTables[3].ToList(returnTypes[3]) + }; + } + // 处理 3 个结果集 + else if (returnTypes.Length == 3) + { + return new List + { + dataTables[0].ToList(returnTypes[0]), + dataTables[1].ToList(returnTypes[1]), + dataTables[2].ToList(returnTypes[2]) + }; + } + // 处理 2 个结果集 + else if (returnTypes.Length == 2) + { + return new List + { + dataTables[0].ToList(returnTypes[0]), + dataTables[1].ToList(returnTypes[1]) + }; + } + // 处理 1 个结果集 + else + { + return new List + { + dataTables[0].ToList(returnTypes[0]) + }; + } + } + + /// + /// 将 DataSet 转 特定类型 + /// + /// DataSet + /// 特定类型集合 + /// object + public static Task> ToListAsync(this DataSet dataSet, params Type[] returnTypes) + { + return Task.FromResult(dataSet.ToList(returnTypes)); + } + + /// + /// 将 DataTable 转 特定类型 + /// + /// DataTable + /// 返回值类型 + /// object + public static object ToList(this DataTable dataTable, Type returnType) + { + var isGenericType = returnType.IsGenericType; + // 获取类型真实返回类型 + var underlyingType = isGenericType ? returnType.GenericTypeArguments.First() : returnType; + + var resultType = typeof(List<>).MakeGenericType(underlyingType); + var list = Activator.CreateInstance(resultType); + var addMethod = resultType.GetMethod("Add"); + + // 将 DataTable 转为行集合 + var dataRows = dataTable.AsEnumerable(); + + // 如果是基元类型 + if (underlyingType.IsRichPrimitive()) + { + // 遍历所有行 + foreach (var dataRow in dataRows) + { + // 只取第一列数据 + var firstColumnValue = dataRow[0]; + // 转换成目标类型数据 + var destValue = firstColumnValue?.ChangeType(underlyingType); + // 添加到集合中 + _ = addMethod.Invoke(list, new[] { destValue }); + } + } + // 处理Object类型 + else if (underlyingType == typeof(object)) + { + // 获取所有列名 + var columns = dataTable.Columns; + + // 遍历所有行 + foreach (var dataRow in dataRows) + { + var dic = new Dictionary(); + foreach (DataColumn column in columns) + { + dic.Add(column.ColumnName, dataRow[column]); + } + _ = addMethod.Invoke(list, new[] { dic }); + } + } + else + { + // 获取所有的数据列和类公开实例属性 + var dataColumns = dataTable.Columns; + var properties = underlyingType.GetProperties(BindingFlags.Public | BindingFlags.Instance); + //.Where(p => !p.IsDefined(typeof(NotMappedAttribute), true)); // sql 数据转换无需判断 [NotMapperd] 特性 + + // 遍历所有行 + foreach (var dataRow in dataRows) + { + var model = Activator.CreateInstance(underlyingType); + + // 遍历所有属性并一一赋值 + foreach (var property in properties) + { + // 获取属性对应的真实列名 + var columnName = property.Name; + if (property.IsDefined(typeof(ColumnAttribute), true)) + { + var columnAttribute = property.GetCustomAttribute(true); + if (!string.IsNullOrWhiteSpace(columnAttribute.Name)) columnName = columnAttribute.Name; + } + + // 如果 DataTable 不包含该列名,则跳过 + if (!dataColumns.Contains(columnName)) continue; + + // 获取列值 + var columnValue = dataRow[columnName]; + // 如果列值未空,则跳过 + if (columnValue == DBNull.Value) continue; + + // 转换成目标类型数据 + var destValue = columnValue?.ChangeType(property.PropertyType); + property.SetValue(model, destValue); + } + + // 添加到集合中 + _ = addMethod.Invoke(list, new[] { model }); + } + } + + return list; + } + + /// + /// 将 DataTable 转 特定类型 + /// + /// DataTable + /// 返回值类型 + /// object + public static Task ToListAsync(this DataTable dataTable, Type returnType) + { + return Task.FromResult(dataTable.ToList(returnType)); + } + + /// + /// 处理元组类型返回值 + /// + /// 数据集 + /// 返回值类型 + /// + internal static object ToValueTuple(this DataSet dataSet, Type tupleType) + { + // 获取元组最底层类型 + var underlyingTypes = tupleType.GetGenericArguments().Select(u => u.IsGenericType ? u.GetGenericArguments().First() : u); + + var toListMethod = typeof(DbDataConvertExtensions) + .GetMethods(BindingFlags.Public | BindingFlags.Static) + .First(u => u.Name == "ToList" && u.IsGenericMethod && u.GetGenericArguments().Length == tupleType.GetGenericArguments().Length) + .MakeGenericMethod(underlyingTypes.ToArray()); + + return toListMethod.Invoke(null, new[] { dataSet }); + } + } +} \ No newline at end of file diff --git a/Infrastructure/Define.cs b/Infrastructure/Define.cs new file mode 100644 index 0000000..b947574 --- /dev/null +++ b/Infrastructure/Define.cs @@ -0,0 +1,38 @@ +namespace Infrastructure +{ + public static class Define + { + public static string USERROLE = "UserRole"; //用户角色关联KEY + public const string ROLERESOURCE = "RoleResource"; //角色资源关联KEY + public const string USERORG = "UserOrg"; //用户机构关联KEY + public const string ROLEELEMENT = "RoleElement"; //角色菜单关联KEY + public const string ROLEMODULE = "RoleModule"; //角色模块关联KEY + public const string ROLEDATAPROPERTY = "RoleDataProperty"; //角色数据字段权限 + + + public const string DBTYPE_SQLSERVER = "SqlServer"; //sql server + public const string DBTYPE_MYSQL = "MySql"; //mysql + public const string DBTYPE_PostgreSQL = "PostgreSQL"; //PostgreSQL + public const string DBTYPE_ORACLE = "Oracle"; //oracle + + + public const int INVALID_TOKEN = 50014; //token无效 + + public const string TOKEN_NAME = "X-Token"; + public const string TENANT_ID = "tenantId"; + + + public const string SYSTEM_USERNAME = "System"; + public const string SYSTEM_USERPWD = "acdcd3db09e0dc32c222c1840144e31d"; + //public const string SYSTEM_USERPWD = "6ff33dd3478bae96663aced32f5262fa"; + public const string SYSTEM_SECRETKEY = "b6467189772775db"; + + public const string DATAPRIVILEGE_LOGINUSER = "{loginUser}"; //数据权限配置中,当前登录用户的key + public const string DATAPRIVILEGE_LOGINROLE = "{loginRole}"; //数据权限配置中,当前登录用户角色的key + public const string DATAPRIVILEGE_LOGINORG = "{loginOrg}"; //数据权限配置中,当前登录用户部门的key + + public const string JOBMAPKEY = "OpenJob"; + + public const string DEFAULT_FORM_INSTANCE_ID_NAME = "InstanceId"; + } +} diff --git a/Infrastructure/DynamicLinq.cs b/Infrastructure/DynamicLinq.cs new file mode 100644 index 0000000..d20a1f8 --- /dev/null +++ b/Infrastructure/DynamicLinq.cs @@ -0,0 +1,306 @@ +// *********************************************************************** +// Assembly : FairUtility +// Author : Yubao Li +// Created : 08-18-2015 +// +// Last Modified By : Yubao Li +// Last Modified On : 08-18-2015 +// *********************************************************************** +// +// Copyright (c) . All rights reserved. +// +// 动态linq +// *********************************************************************** + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; +using System.Reflection; + +namespace Infrastructure +{ + public static class DynamicLinq + { + public static ParameterExpression CreateLambdaParam(string name) + { + return Expression.Parameter(typeof(T), name); + } + + /// + /// 创建linq表达示的body部分 + /// + public static Expression GenerateBody(this ParameterExpression param, Filter filterObj) + { + PropertyInfo property = typeof(T).GetProperty(filterObj.Key); + + Expression left = null; //组装左边 + //组装右边 + Expression right = null; + + if (property != null) + { + left = Expression.Property(param, property); + if (property.PropertyType == typeof(int)) + { + right = Expression.Constant(int.Parse(filterObj.Value)); + } + else if (property.PropertyType == typeof(DateTime)) + { + right = Expression.Constant(DateTime.Parse(filterObj.Value)); + } + else if (property.PropertyType == typeof(string)) + { + right = Expression.Constant(filterObj.Value); + } + else if (property.PropertyType == typeof(decimal)) + { + right = Expression.Constant(decimal.Parse(filterObj.Value)); + } + else if (property.PropertyType == typeof(Guid)) + { + right = Expression.Constant(Guid.Parse(filterObj.Value)); + } + else if (property.PropertyType == typeof(bool)) + { + right = Expression.Constant(filterObj.Value.Equals("1")); + } + else if (property.PropertyType == typeof(Guid?)) + { + left = Expression.Property(left, "Value"); + right = Expression.Constant(Guid.Parse(filterObj.Value)); + } + else + { + throw new Exception("暂不能解析该Key的类型"); + } + } + else //如果左边不是属性,直接是值的情况 + { + left = Expression.Constant(filterObj.Key); + right = Expression.Constant(filterObj.Value); + } + + //c.XXX=="XXX" + Expression filter = Expression.Equal(left, right); + switch (filterObj.Contrast) + { + case "<=": + filter = Expression.LessThanOrEqual(left, right); + break; + + case "<": + filter = Expression.LessThan(left, right); + break; + + case ">": + filter = Expression.GreaterThan(left, right); + break; + + case ">=": + filter = Expression.GreaterThanOrEqual(left, right); + break; + case "!=": + filter = Expression.NotEqual(left, right); + break; + case "contains": + filter = Expression.Call(left, typeof(string).GetMethod("Contains", new Type[] {typeof(string)}), + Expression.Constant(filterObj.Value)); + break; + case "in": + var lExp = Expression.Constant(filterObj.Value.Split(',').ToList()); //数组 + var methodInfo = typeof(List).GetMethod("Contains", + new Type[] {typeof(string)}); //Contains语句 + filter = Expression.Call(lExp, methodInfo, left); + break; + case "not in": + var listExpression = Expression.Constant(filterObj.Value.Split(',').ToList()); //数组 + var method = typeof(List).GetMethod("Contains", new Type[] {typeof(string)}); //Contains语句 + filter = Expression.Not(Expression.Call(listExpression, method, left)); + break; + //交集,使用交集时左值必须时固定的值 + case "intersect": //交集 + if (property != null) + { + throw new Exception("交集模式下,表达式左边不能为变量,请调整数据规则,如:c=>\"A,B,C\" intersect \"B,D\""); + } + + var rightval = filterObj.Value.Split(',').ToList(); + var leftval = filterObj.Key.Split(',').ToList(); + var val = rightval.Intersect(leftval); + + filter = Expression.Constant(val.Count() > 0); + break; + } + + return filter; + } + + public static Expression> GenerateTypeBody(this ParameterExpression param, Filter filterObj) + { + return (Expression>) (param.GenerateBody(filterObj)); + } + + /// + /// 创建完整的lambda + /// + public static LambdaExpression GenerateLambda(this ParameterExpression param, Expression body) + { + //c=>c.XXX=="XXX" + return Expression.Lambda(body, param); + } + + public static Expression> GenerateTypeLambda(this ParameterExpression param, Expression body) + { + return (Expression>) (param.GenerateLambda(body)); + } + + public static Expression AndAlso(this Expression expression, Expression expressionRight) + { + return Expression.AndAlso(expression, expressionRight); + } + + public static Expression Or(this Expression expression, Expression expressionRight) + { + return Expression.Or(expression, expressionRight); + } + + public static Expression And(this Expression expression, Expression expressionRight) + { + return Expression.And(expression, expressionRight); + } + + public static IQueryable GenerateFilter(this IQueryable query, string parametername, string filterjson) + { + if (!string.IsNullOrEmpty(filterjson)) + { + var filterGroup = JsonHelper.Instance.Deserialize(filterjson); + query = GenerateFilter(query, parametername, filterGroup); + } + + return query; + } + + /// + /// 转换FilterGroup为Lambda表达式 + /// + /// + /// + /// + /// + /// + public static IQueryable GenerateFilter(this IQueryable query, string parametername, + FilterGroup filterGroup) + { + var param = CreateLambdaParam(parametername); + Expression result = ConvertGroup(filterGroup, param); + query = query.Where(param.GenerateTypeLambda(result)); + return query; + } + + /// + /// 转换filtergroup为表达式 + /// + /// + /// + /// + /// + public static Expression ConvertGroup(FilterGroup filterGroup, ParameterExpression param) + { + if (filterGroup == null) return null; + + if (filterGroup.Filters.Length == 1 &&(filterGroup.Children == null || !filterGroup.Children.Any())) //只有一个条件 + { + return param.GenerateBody(filterGroup.Filters[0]); + } + + Expression result = ConvertFilters(filterGroup.Filters, param, filterGroup.Operation); + Expression gresult = ConvertGroup(filterGroup.Children, param, filterGroup.Operation); + if (gresult == null) return result; + if (result == null) return gresult; + + if (filterGroup.Operation == "and") + { + return result.AndAlso(gresult); + } + else //or + { + return result.Or(gresult); + } + } + + /// + /// 转换FilterGroup[]为表达式,不管FilterGroup里面的Filters + /// + /// + /// + /// + /// + /// + private static Expression ConvertGroup(FilterGroup[] groups, ParameterExpression param, string operation) + { + if (groups == null || !groups.Any()) return null; + + Expression result = ConvertGroup(groups[0], param); + + if (groups.Length == 1) return result; + + if (operation == "and") + { + foreach (var filter in groups.Skip(1)) + { + result = result.AndAlso(ConvertGroup(filter, param)); + } + } + else + { + foreach (var filter in groups.Skip(1)) + { + result = result.Or(ConvertGroup(filter, param)); + } + } + + return result; + } + + /// + /// 转换Filter数组为表达式 + /// + /// + /// + /// + /// + /// + private static Expression ConvertFilters(Filter[] filters, ParameterExpression param, string operation) + { + if (filters == null || !filters.Any()) + { + return null; + } + + Expression result = param.GenerateBody(filters[0]); + + if (filters.Length == 1) + { + return result; + } + + if (operation == "and") + { + foreach (var filter in filters.Skip(1)) + { + result = result.AndAlso(param.GenerateBody(filter)); + } + } + else + { + foreach (var filter in filters.Skip(1)) + { + result = result.Or(param.GenerateBody(filter)); + } + } + + return result; + } + } +} \ No newline at end of file diff --git a/Infrastructure/DynamicQueryable.cs b/Infrastructure/DynamicQueryable.cs new file mode 100644 index 0000000..9fb59a9 --- /dev/null +++ b/Infrastructure/DynamicQueryable.cs @@ -0,0 +1,2079 @@ +//Copyright (C) Microsoft Corporation. All rights reserved. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; +using System.Reflection; +using System.Reflection.Emit; +using System.Text; +using System.Threading; + +namespace Infrastructure +{ + public static class DynamicQueryable + { + public static IQueryable Where(this IQueryable source, string predicate, params object[] values) { + return (IQueryable)Where((IQueryable)source, predicate, values); + } + + public static IQueryable Where(this IQueryable source, string predicate, params object[] values) { + if (source == null) throw new ArgumentNullException("source"); + if (predicate == null) throw new ArgumentNullException("predicate"); + LambdaExpression lambda = DynamicExpression.ParseLambda(source.ElementType, typeof(bool), predicate, values); + return source.Provider.CreateQuery( + Expression.Call( + typeof(Queryable), "Where", + new Type[] { source.ElementType }, + source.Expression, Expression.Quote(lambda))); + } + + public static IQueryable Select(this IQueryable source, string selector, params object[] values) { + if (source == null) throw new ArgumentNullException("source"); + if (selector == null) throw new ArgumentNullException("selector"); + LambdaExpression lambda = DynamicExpression.ParseLambda(source.ElementType, null, selector, values); + return source.Provider.CreateQuery( + Expression.Call( + typeof(Queryable), "Select", + new Type[] { source.ElementType, lambda.Body.Type }, + source.Expression, Expression.Quote(lambda))); + } + + public static IQueryable OrderBy(this IQueryable source, string ordering, params object[] values) { + return (IQueryable)OrderBy((IQueryable)source, ordering, values); + } + + public static IQueryable ThenBy(this IQueryable source, string ordering, params object[] values) + { + return (IQueryable)ThenBy((IQueryable)source, ordering, values); + } + + public static IQueryable ThenBy(this IQueryable source, string ordering, params object[] values) + { + if (source == null) throw new ArgumentNullException("source"); + if (ordering == null) throw new ArgumentNullException("ordering"); + ParameterExpression[] parameters = new ParameterExpression[] { + Expression.Parameter(source.ElementType, "") }; + ExpressionParser parser = new ExpressionParser(parameters, ordering, values); + IEnumerable orderings = parser.ParseOrdering(); + Expression queryExpr = source.Expression; + string methodAsc = "ThenBy"; + string methodDesc = "ThenByDescending"; + foreach (DynamicOrdering o in orderings) + { + queryExpr = Expression.Call( + typeof(Queryable), o.Ascending ? methodAsc : methodDesc, + new Type[] { source.ElementType, o.Selector.Type }, + queryExpr, Expression.Quote(Expression.Lambda(o.Selector, parameters))); + + } + return source.Provider.CreateQuery(queryExpr); + } + + public static IQueryable OrderBy(this IQueryable source, string ordering, params object[] values) { + if (source == null) throw new ArgumentNullException("source"); + if (ordering == null) throw new ArgumentNullException("ordering"); + ParameterExpression[] parameters = new ParameterExpression[] { + Expression.Parameter(source.ElementType, "") }; + ExpressionParser parser = new ExpressionParser(parameters, ordering, values); + IEnumerable orderings = parser.ParseOrdering(); + Expression queryExpr = source.Expression; + string methodAsc = "OrderBy"; + string methodDesc = "OrderByDescending"; + foreach (DynamicOrdering o in orderings) { + queryExpr = Expression.Call( + typeof(Queryable), o.Ascending ? methodAsc : methodDesc, + new Type[] { source.ElementType, o.Selector.Type }, + queryExpr, Expression.Quote(Expression.Lambda(o.Selector, parameters))); + methodAsc = "ThenBy"; + methodDesc = "ThenByDescending"; + } + return source.Provider.CreateQuery(queryExpr); + } + + public static IQueryable OrderBy(this IQueryable source, string propertyName, bool ascending) + where T : class + { + Type type = typeof (T); + + PropertyInfo property = type.GetProperty(propertyName); + if (property == null) + throw new ArgumentException("propertyName", "Not Exist"); + + ParameterExpression param = Expression.Parameter(type, "p"); + Expression propertyAccessExpression = Expression.MakeMemberAccess(param, property); + LambdaExpression orderByExpression = Expression.Lambda(propertyAccessExpression, param); + + string methodName = ascending ? "OrderBy" : "OrderByDescending"; + + MethodCallExpression resultExp = Expression.Call(typeof (Queryable), methodName, + new Type[] {type, property.PropertyType}, source.Expression, Expression.Quote(orderByExpression)); + + return source.Provider.CreateQuery(resultExp); + } + + public static IQueryable Take(this IQueryable source, int count) { + if (source == null) throw new ArgumentNullException("source"); + return source.Provider.CreateQuery( + Expression.Call( + typeof(Queryable), "Take", + new Type[] { source.ElementType }, + source.Expression, Expression.Constant(count))); + } + + public static IQueryable Skip(this IQueryable source, int count) { + if (source == null) throw new ArgumentNullException("source"); + return source.Provider.CreateQuery( + Expression.Call( + typeof(Queryable), "Skip", + new Type[] { source.ElementType }, + source.Expression, Expression.Constant(count))); + } + + public static IQueryable GroupBy(this IQueryable source, string keySelector, string elementSelector, params object[] values) { + if (source == null) throw new ArgumentNullException("source"); + if (keySelector == null) throw new ArgumentNullException("keySelector"); + if (elementSelector == null) throw new ArgumentNullException("elementSelector"); + LambdaExpression keyLambda = DynamicExpression.ParseLambda(source.ElementType, null, keySelector, values); + LambdaExpression elementLambda = DynamicExpression.ParseLambda(source.ElementType, null, elementSelector, values); + return source.Provider.CreateQuery( + Expression.Call( + typeof(Queryable), "GroupBy", + new Type[] { source.ElementType, keyLambda.Body.Type, elementLambda.Body.Type }, + source.Expression, Expression.Quote(keyLambda), Expression.Quote(elementLambda))); + } + + public static bool Any(this IQueryable source) { + if (source == null) throw new ArgumentNullException("source"); + return (bool)source.Provider.Execute( + Expression.Call( + typeof(Queryable), "Any", + new Type[] { source.ElementType }, source.Expression)); + } + + public static int Count(this IQueryable source) { + if (source == null) throw new ArgumentNullException("source"); + return (int)source.Provider.Execute( + Expression.Call( + typeof(Queryable), "Count", + new Type[] { source.ElementType }, source.Expression)); + } + } + + public abstract class DynamicClass + { + public override string ToString() { + PropertyInfo[] props = this.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public); + StringBuilder sb = new StringBuilder(); + sb.Append("{"); + for (int i = 0; i < props.Length; i++) { + if (i > 0) sb.Append(", "); + sb.Append(props[i].Name); + sb.Append("="); + sb.Append(props[i].GetValue(this, null)); + } + sb.Append("}"); + return sb.ToString(); + } + } + + public class DynamicProperty + { + string name; + Type type; + + public DynamicProperty(string name, Type type) { + if (name == null) throw new ArgumentNullException("name"); + if (type == null) throw new ArgumentNullException("type"); + this.name = name; + this.type = type; + } + + public string Name { + get { return name; } + } + + public Type Type { + get { return type; } + } + } + + public static class DynamicExpression + { + public static Expression Parse(Type resultType, string expression, params object[] values) { + ExpressionParser parser = new ExpressionParser(null, expression, values); + return parser.Parse(resultType); + } + + public static LambdaExpression ParseLambda(Type itType, Type resultType, string expression, params object[] values) { + return ParseLambda(new ParameterExpression[] { Expression.Parameter(itType, "") }, resultType, expression, values); + } + + public static LambdaExpression ParseLambda(ParameterExpression[] parameters, Type resultType, string expression, params object[] values) { + ExpressionParser parser = new ExpressionParser(parameters, expression, values); + return Expression.Lambda(parser.Parse(resultType), parameters); + } + + public static Expression> ParseLambda(string expression, params object[] values) { + return (Expression>)ParseLambda(typeof(T), typeof(S), expression, values); + } + + public static Type CreateClass(params DynamicProperty[] properties) { + return ClassFactory.Instance.GetDynamicClass(properties); + } + + public static Type CreateClass(IEnumerable properties) { + return ClassFactory.Instance.GetDynamicClass(properties); + } + } + + internal class DynamicOrdering + { + public Expression Selector; + public bool Ascending; + } + + internal class Signature : IEquatable + { + public DynamicProperty[] properties; + public int hashCode; + + public Signature(IEnumerable properties) { + this.properties = properties.ToArray(); + hashCode = 0; + foreach (DynamicProperty p in properties) { + hashCode ^= p.Name.GetHashCode() ^ p.Type.GetHashCode(); + } + } + + public override int GetHashCode() { + return hashCode; + } + + public override bool Equals(object obj) { + return obj is Signature ? Equals((Signature)obj) : false; + } + + public bool Equals(Signature other) { + if (properties.Length != other.properties.Length) return false; + for (int i = 0; i < properties.Length; i++) { + if (properties[i].Name != other.properties[i].Name || + properties[i].Type != other.properties[i].Type) return false; + } + return true; + } + } + + internal class ClassFactory + { + public static readonly ClassFactory Instance = new ClassFactory(); + + static ClassFactory() { } // Trigger lazy initialization of static fields + + ModuleBuilder module; + Dictionary classes; + int classCount; + ReaderWriterLock rwLock; + + private ClassFactory() { + AssemblyName name = new AssemblyName("DynamicClasses"); + AssemblyBuilder assembly = AssemblyBuilder.DefineDynamicAssembly(name, AssemblyBuilderAccess.Run); + // AssemblyBuilder assembly = AppDomain.CurrentDomain.DefineDynamicAssembly(name, AssemblyBuilderAccess.Run); +#if ENABLE_LINQ_PARTIAL_TRUST + new ReflectionPermission(PermissionState.Unrestricted).Assert(); +#endif + try { + module = assembly.DefineDynamicModule("Module"); + } + finally { +#if ENABLE_LINQ_PARTIAL_TRUST + PermissionSet.RevertAssert(); +#endif + } + classes = new Dictionary(); + rwLock = new ReaderWriterLock(); + } + + public Type GetDynamicClass(IEnumerable properties) { + rwLock.AcquireReaderLock(Timeout.Infinite); + try { + Signature signature = new Signature(properties); + Type type; + if (!classes.TryGetValue(signature, out type)) { + type = CreateDynamicClass(signature.properties); + //fixed by https://gitee.com/DUWENINK + if (!classes.ContainsKey(signature)){ + classes.Add(signature, type); + } + } + return type; + } + finally { + rwLock.ReleaseReaderLock(); + } + } + + Type CreateDynamicClass(DynamicProperty[] properties) { + LockCookie cookie = rwLock.UpgradeToWriterLock(Timeout.Infinite); + try { + string typeName = "DynamicClass" + (classCount + 1); +#if ENABLE_LINQ_PARTIAL_TRUST + new ReflectionPermission(PermissionState.Unrestricted).Assert(); +#endif + try { + TypeBuilder tb = this.module.DefineType(typeName, TypeAttributes.Class | + TypeAttributes.Public, typeof(DynamicClass)); + FieldInfo[] fields = GenerateProperties(tb, properties); + GenerateEquals(tb, fields); + GenerateGetHashCode(tb, fields); + Type result = tb.CreateType(); + classCount++; + return result; + } + finally { +#if ENABLE_LINQ_PARTIAL_TRUST + PermissionSet.RevertAssert(); +#endif + } + } + finally { + rwLock.DowngradeFromWriterLock(ref cookie); + } + } + + FieldInfo[] GenerateProperties(TypeBuilder tb, DynamicProperty[] properties) { + FieldInfo[] fields = new FieldBuilder[properties.Length]; + for (int i = 0; i < properties.Length; i++) { + DynamicProperty dp = properties[i]; + FieldBuilder fb = tb.DefineField("_" + dp.Name, dp.Type, FieldAttributes.Private); + PropertyBuilder pb = tb.DefineProperty(dp.Name, PropertyAttributes.HasDefault, dp.Type, null); + MethodBuilder mbGet = tb.DefineMethod("get_" + dp.Name, + MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig, + dp.Type, Type.EmptyTypes); + ILGenerator genGet = mbGet.GetILGenerator(); + genGet.Emit(OpCodes.Ldarg_0); + genGet.Emit(OpCodes.Ldfld, fb); + genGet.Emit(OpCodes.Ret); + MethodBuilder mbSet = tb.DefineMethod("set_" + dp.Name, + MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig, + null, new Type[] { dp.Type }); + ILGenerator genSet = mbSet.GetILGenerator(); + genSet.Emit(OpCodes.Ldarg_0); + genSet.Emit(OpCodes.Ldarg_1); + genSet.Emit(OpCodes.Stfld, fb); + genSet.Emit(OpCodes.Ret); + pb.SetGetMethod(mbGet); + pb.SetSetMethod(mbSet); + fields[i] = fb; + } + return fields; + } + + void GenerateEquals(TypeBuilder tb, FieldInfo[] fields) { + MethodBuilder mb = tb.DefineMethod("Equals", + MethodAttributes.Public | MethodAttributes.ReuseSlot | + MethodAttributes.Virtual | MethodAttributes.HideBySig, + typeof(bool), new Type[] { typeof(object) }); + ILGenerator gen = mb.GetILGenerator(); + LocalBuilder other = gen.DeclareLocal(tb); + Label next = gen.DefineLabel(); + gen.Emit(OpCodes.Ldarg_1); + gen.Emit(OpCodes.Isinst, tb); + gen.Emit(OpCodes.Stloc, other); + gen.Emit(OpCodes.Ldloc, other); + gen.Emit(OpCodes.Brtrue_S, next); + gen.Emit(OpCodes.Ldc_I4_0); + gen.Emit(OpCodes.Ret); + gen.MarkLabel(next); + foreach (FieldInfo field in fields) { + Type ft = field.FieldType; + Type ct = typeof(EqualityComparer<>).MakeGenericType(ft); + next = gen.DefineLabel(); + gen.EmitCall(OpCodes.Call, ct.GetMethod("get_Default"), null); + gen.Emit(OpCodes.Ldarg_0); + gen.Emit(OpCodes.Ldfld, field); + gen.Emit(OpCodes.Ldloc, other); + gen.Emit(OpCodes.Ldfld, field); + gen.EmitCall(OpCodes.Callvirt, ct.GetMethod("Equals", new Type[] { ft, ft }), null); + gen.Emit(OpCodes.Brtrue_S, next); + gen.Emit(OpCodes.Ldc_I4_0); + gen.Emit(OpCodes.Ret); + gen.MarkLabel(next); + } + gen.Emit(OpCodes.Ldc_I4_1); + gen.Emit(OpCodes.Ret); + } + + void GenerateGetHashCode(TypeBuilder tb, FieldInfo[] fields) { + MethodBuilder mb = tb.DefineMethod("GetHashCode", + MethodAttributes.Public | MethodAttributes.ReuseSlot | + MethodAttributes.Virtual | MethodAttributes.HideBySig, + typeof(int), Type.EmptyTypes); + ILGenerator gen = mb.GetILGenerator(); + gen.Emit(OpCodes.Ldc_I4_0); + foreach (FieldInfo field in fields) { + Type ft = field.FieldType; + Type ct = typeof(EqualityComparer<>).MakeGenericType(ft); + gen.EmitCall(OpCodes.Call, ct.GetMethod("get_Default"), null); + gen.Emit(OpCodes.Ldarg_0); + gen.Emit(OpCodes.Ldfld, field); + gen.EmitCall(OpCodes.Callvirt, ct.GetMethod("GetHashCode", new Type[] { ft }), null); + gen.Emit(OpCodes.Xor); + } + gen.Emit(OpCodes.Ret); + } + } + + public sealed class ParseException : Exception + { + int position; + + public ParseException(string message, int position) + : base(message) { + this.position = position; + } + + public int Position { + get { return position; } + } + + public override string ToString() { + return string.Format(Res.ParseExceptionFormat, Message, position); + } + } + + internal class ExpressionParser + { + struct Token + { + public TokenId id; + public string text; + public int pos; + } + + enum TokenId + { + Unknown, + End, + Identifier, + StringLiteral, + IntegerLiteral, + RealLiteral, + Exclamation, + Percent, + Amphersand, + OpenParen, + CloseParen, + Asterisk, + Plus, + Comma, + Minus, + Dot, + Slash, + Colon, + LessThan, + Equal, + GreaterThan, + Question, + OpenBracket, + CloseBracket, + Bar, + ExclamationEqual, + DoubleAmphersand, + LessThanEqual, + LessGreater, + DoubleEqual, + GreaterThanEqual, + DoubleBar + } + + interface ILogicalSignatures + { + void F(bool x, bool y); + void F(bool? x, bool? y); + } + + interface IArithmeticSignatures + { + void F(int x, int y); + void F(uint x, uint y); + void F(long x, long y); + void F(ulong x, ulong y); + void F(float x, float y); + void F(double x, double y); + void F(decimal x, decimal y); + void F(int? x, int? y); + void F(uint? x, uint? y); + void F(long? x, long? y); + void F(ulong? x, ulong? y); + void F(float? x, float? y); + void F(double? x, double? y); + void F(decimal? x, decimal? y); + } + + interface IRelationalSignatures : IArithmeticSignatures + { + void F(string x, string y); + void F(char x, char y); + void F(DateTime x, DateTime y); + void F(TimeSpan x, TimeSpan y); + void F(char? x, char? y); + void F(DateTime? x, DateTime? y); + void F(TimeSpan? x, TimeSpan? y); + } + + interface IEqualitySignatures : IRelationalSignatures + { + void F(bool x, bool y); + void F(bool? x, bool? y); + } + + interface IAddSignatures : IArithmeticSignatures + { + void F(DateTime x, TimeSpan y); + void F(TimeSpan x, TimeSpan y); + void F(DateTime? x, TimeSpan? y); + void F(TimeSpan? x, TimeSpan? y); + } + + interface ISubtractSignatures : IAddSignatures + { + void F(DateTime x, DateTime y); + void F(DateTime? x, DateTime? y); + } + + interface INegationSignatures + { + void F(int x); + void F(long x); + void F(float x); + void F(double x); + void F(decimal x); + void F(int? x); + void F(long? x); + void F(float? x); + void F(double? x); + void F(decimal? x); + } + + interface INotSignatures + { + void F(bool x); + void F(bool? x); + } + + interface IEnumerableSignatures + { + void Where(bool predicate); + void Any(); + void Any(bool predicate); + void All(bool predicate); + void Count(); + void Count(bool predicate); + void Min(object selector); + void Max(object selector); + void Sum(int selector); + void Sum(int? selector); + void Sum(long selector); + void Sum(long? selector); + void Sum(float selector); + void Sum(float? selector); + void Sum(double selector); + void Sum(double? selector); + void Sum(decimal selector); + void Sum(decimal? selector); + void Average(int selector); + void Average(int? selector); + void Average(long selector); + void Average(long? selector); + void Average(float selector); + void Average(float? selector); + void Average(double selector); + void Average(double? selector); + void Average(decimal selector); + void Average(decimal? selector); + } + + static readonly Type[] predefinedTypes = { + typeof(Object), + typeof(Boolean), + typeof(Char), + typeof(String), + typeof(SByte), + typeof(Byte), + typeof(Int16), + typeof(UInt16), + typeof(Int32), + typeof(UInt32), + typeof(Int64), + typeof(UInt64), + typeof(Single), + typeof(Double), + typeof(Decimal), + typeof(DateTime), + typeof(TimeSpan), + typeof(Guid), + typeof(Math), + typeof(Convert) + }; + + static readonly Expression trueLiteral = Expression.Constant(true); + static readonly Expression falseLiteral = Expression.Constant(false); + static readonly Expression nullLiteral = Expression.Constant(null); + + static readonly string keywordIt = "it"; + static readonly string keywordIif = "iif"; + static readonly string keywordNew = "new"; + + static Dictionary keywords; + + Dictionary symbols; + IDictionary externals; + Dictionary literals; + ParameterExpression it; + string text; + int textPos; + int textLen; + char ch; + Token token; + + public ExpressionParser(ParameterExpression[] parameters, string expression, object[] values) { + if (expression == null) throw new ArgumentNullException("expression"); + if (keywords == null) keywords = CreateKeywords(); + symbols = new Dictionary(StringComparer.OrdinalIgnoreCase); + literals = new Dictionary(); + if (parameters != null) ProcessParameters(parameters); + if (values != null) ProcessValues(values); + text = expression; + textLen = text.Length; + SetTextPos(0); + NextToken(); + } + + void ProcessParameters(ParameterExpression[] parameters) { + foreach (ParameterExpression pe in parameters) + if (!String.IsNullOrEmpty(pe.Name)) + AddSymbol(pe.Name, pe); + if (parameters.Length == 1 && String.IsNullOrEmpty(parameters[0].Name)) + it = parameters[0]; + } + + void ProcessValues(object[] values) { + for (int i = 0; i < values.Length; i++) { + object value = values[i]; + if (i == values.Length - 1 && value is IDictionary) { + externals = (IDictionary)value; + } + else { + AddSymbol("@" + i.ToString(System.Globalization.CultureInfo.InvariantCulture), value); + } + } + } + + void AddSymbol(string name, object value) { + if (symbols.ContainsKey(name)) + throw ParseError(Res.DuplicateIdentifier, name); + symbols.Add(name, value); + } + + public Expression Parse(Type resultType) { + int exprPos = token.pos; + Expression expr = ParseExpression(); + if (resultType != null) + if ((expr = PromoteExpression(expr, resultType, true)) == null) + throw ParseError(exprPos, Res.ExpressionTypeMismatch, GetTypeName(resultType)); + ValidateToken(TokenId.End, Res.SyntaxError); + return expr; + } + +#pragma warning disable 0219 + public IEnumerable ParseOrdering() { + List orderings = new List(); + while (true) { + Expression expr = ParseExpression(); + bool ascending = true; + if (TokenIdentifierIs("asc") || TokenIdentifierIs("ascending")) { + NextToken(); + } + else if (TokenIdentifierIs("desc") || TokenIdentifierIs("descending")) { + NextToken(); + ascending = false; + } + orderings.Add(new DynamicOrdering { Selector = expr, Ascending = ascending }); + if (token.id != TokenId.Comma) break; + NextToken(); + } + ValidateToken(TokenId.End, Res.SyntaxError); + return orderings; + } +#pragma warning restore 0219 + + // ?: operator + Expression ParseExpression() { + int errorPos = token.pos; + Expression expr = ParseLogicalOr(); + if (token.id == TokenId.Question) { + NextToken(); + Expression expr1 = ParseExpression(); + ValidateToken(TokenId.Colon, Res.ColonExpected); + NextToken(); + Expression expr2 = ParseExpression(); + expr = GenerateConditional(expr, expr1, expr2, errorPos); + } + return expr; + } + + // ||, or operator + Expression ParseLogicalOr() { + Expression left = ParseLogicalAnd(); + while (token.id == TokenId.DoubleBar || TokenIdentifierIs("or")) { + Token op = token; + NextToken(); + Expression right = ParseLogicalAnd(); + CheckAndPromoteOperands(typeof(ILogicalSignatures), op.text, ref left, ref right, op.pos); + left = Expression.OrElse(left, right); + } + return left; + } + + // &&, and operator + Expression ParseLogicalAnd() { + Expression left = ParseComparison(); + while (token.id == TokenId.DoubleAmphersand || TokenIdentifierIs("and")) { + Token op = token; + NextToken(); + Expression right = ParseComparison(); + CheckAndPromoteOperands(typeof(ILogicalSignatures), op.text, ref left, ref right, op.pos); + left = Expression.AndAlso(left, right); + } + return left; + } + + // =, ==, !=, <>, >, >=, <, <= operators + Expression ParseComparison() { + Expression left = ParseAdditive(); + while (token.id == TokenId.Equal || token.id == TokenId.DoubleEqual || + token.id == TokenId.ExclamationEqual || token.id == TokenId.LessGreater || + token.id == TokenId.GreaterThan || token.id == TokenId.GreaterThanEqual || + token.id == TokenId.LessThan || token.id == TokenId.LessThanEqual) { + Token op = token; + NextToken(); + Expression right = ParseAdditive(); + bool isEquality = op.id == TokenId.Equal || op.id == TokenId.DoubleEqual || + op.id == TokenId.ExclamationEqual || op.id == TokenId.LessGreater; + if (isEquality && !left.Type.IsValueType && !right.Type.IsValueType) { + if (left.Type != right.Type) { + if (left.Type.IsAssignableFrom(right.Type)) { + right = Expression.Convert(right, left.Type); + } + else if (right.Type.IsAssignableFrom(left.Type)) { + left = Expression.Convert(left, right.Type); + } + else { + throw IncompatibleOperandsError(op.text, left, right, op.pos); + } + } + } + else if (IsEnumType(left.Type) || IsEnumType(right.Type)) { + if (left.Type != right.Type) { + Expression e; + if ((e = PromoteExpression(right, left.Type, true)) != null) { + right = e; + } + else if ((e = PromoteExpression(left, right.Type, true)) != null) { + left = e; + } + else { + throw IncompatibleOperandsError(op.text, left, right, op.pos); + } + } + } + else { + CheckAndPromoteOperands(isEquality ? typeof(IEqualitySignatures) : typeof(IRelationalSignatures), + op.text, ref left, ref right, op.pos); + } + switch (op.id) { + case TokenId.Equal: + case TokenId.DoubleEqual: + left = GenerateEqual(left, right); + break; + case TokenId.ExclamationEqual: + case TokenId.LessGreater: + left = GenerateNotEqual(left, right); + break; + case TokenId.GreaterThan: + left = GenerateGreaterThan(left, right); + break; + case TokenId.GreaterThanEqual: + left = GenerateGreaterThanEqual(left, right); + break; + case TokenId.LessThan: + left = GenerateLessThan(left, right); + break; + case TokenId.LessThanEqual: + left = GenerateLessThanEqual(left, right); + break; + } + } + return left; + } + + // +, -, & operators + Expression ParseAdditive() { + Expression left = ParseMultiplicative(); + while (token.id == TokenId.Plus || token.id == TokenId.Minus || + token.id == TokenId.Amphersand) { + Token op = token; + NextToken(); + Expression right = ParseMultiplicative(); + switch (op.id) { + case TokenId.Plus: + if (left.Type == typeof(string) || right.Type == typeof(string)) + goto case TokenId.Amphersand; + CheckAndPromoteOperands(typeof(IAddSignatures), op.text, ref left, ref right, op.pos); + left = GenerateAdd(left, right); + break; + case TokenId.Minus: + CheckAndPromoteOperands(typeof(ISubtractSignatures), op.text, ref left, ref right, op.pos); + left = GenerateSubtract(left, right); + break; + case TokenId.Amphersand: + left = GenerateStringConcat(left, right); + break; + } + } + return left; + } + + // *, /, %, mod operators + Expression ParseMultiplicative() { + Expression left = ParseUnary(); + while (token.id == TokenId.Asterisk || token.id == TokenId.Slash || + token.id == TokenId.Percent || TokenIdentifierIs("mod")) { + Token op = token; + NextToken(); + Expression right = ParseUnary(); + CheckAndPromoteOperands(typeof(IArithmeticSignatures), op.text, ref left, ref right, op.pos); + switch (op.id) { + case TokenId.Asterisk: + left = Expression.Multiply(left, right); + break; + case TokenId.Slash: + left = Expression.Divide(left, right); + break; + case TokenId.Percent: + case TokenId.Identifier: + left = Expression.Modulo(left, right); + break; + } + } + return left; + } + + // -, !, not unary operators + Expression ParseUnary() { + if (token.id == TokenId.Minus || token.id == TokenId.Exclamation || + TokenIdentifierIs("not")) { + Token op = token; + NextToken(); + if (op.id == TokenId.Minus && (token.id == TokenId.IntegerLiteral || + token.id == TokenId.RealLiteral)) { + token.text = "-" + token.text; + token.pos = op.pos; + return ParsePrimary(); + } + Expression expr = ParseUnary(); + if (op.id == TokenId.Minus) { + CheckAndPromoteOperand(typeof(INegationSignatures), op.text, ref expr, op.pos); + expr = Expression.Negate(expr); + } + else { + CheckAndPromoteOperand(typeof(INotSignatures), op.text, ref expr, op.pos); + expr = Expression.Not(expr); + } + return expr; + } + return ParsePrimary(); + } + + Expression ParsePrimary() { + Expression expr = ParsePrimaryStart(); + while (true) { + if (token.id == TokenId.Dot) { + NextToken(); + expr = ParseMemberAccess(null, expr); + } + else if (token.id == TokenId.OpenBracket) { + expr = ParseElementAccess(expr); + } + else { + break; + } + } + return expr; + } + + Expression ParsePrimaryStart() { + switch (token.id) { + case TokenId.Identifier: + return ParseIdentifier(); + case TokenId.StringLiteral: + return ParseStringLiteral(); + case TokenId.IntegerLiteral: + return ParseIntegerLiteral(); + case TokenId.RealLiteral: + return ParseRealLiteral(); + case TokenId.OpenParen: + return ParseParenExpression(); + default: + throw ParseError(Res.ExpressionExpected); + } + } + + Expression ParseStringLiteral() { + ValidateToken(TokenId.StringLiteral); + char quote = token.text[0]; + string s = token.text.Substring(1, token.text.Length - 2); + int start = 0; + while (true) { + int i = s.IndexOf(quote, start); + if (i < 0) break; + s = s.Remove(i, 1); + start = i + 1; + } + //if (quote == '\'') { + // if (s.Length != 1) + // throw ParseError(Res.InvalidCharacterLiteral); + // NextToken(); + // return CreateLiteral(s[0], s); + //} + NextToken(); + return CreateLiteral(s, s); + } + + Expression ParseIntegerLiteral() { + ValidateToken(TokenId.IntegerLiteral); + string text = token.text; + if (text[0] != '-') { + ulong value; + if (!UInt64.TryParse(text, out value)) + throw ParseError(Res.InvalidIntegerLiteral, text); + NextToken(); + if (value <= (ulong)Int32.MaxValue) return CreateLiteral((int)value, text); + if (value <= (ulong)UInt32.MaxValue) return CreateLiteral((uint)value, text); + if (value <= (ulong)Int64.MaxValue) return CreateLiteral((long)value, text); + return CreateLiteral(value, text); + } + else { + long value; + if (!Int64.TryParse(text, out value)) + throw ParseError(Res.InvalidIntegerLiteral, text); + NextToken(); + if (value >= Int32.MinValue && value <= Int32.MaxValue) + return CreateLiteral((int)value, text); + return CreateLiteral(value, text); + } + } + + Expression ParseRealLiteral() { + ValidateToken(TokenId.RealLiteral); + string text = token.text; + object value = null; + char last = text[text.Length - 1]; + if (last == 'F' || last == 'f') { + float f; + if (Single.TryParse(text.Substring(0, text.Length - 1), out f)) value = f; + } + else { + double d; + if (Double.TryParse(text, out d)) value = d; + } + if (value == null) throw ParseError(Res.InvalidRealLiteral, text); + NextToken(); + return CreateLiteral(value, text); + } + + Expression CreateLiteral(object value, string text) { + ConstantExpression expr = Expression.Constant(value); + literals.Add(expr, text); + return expr; + } + + Expression ParseParenExpression() { + ValidateToken(TokenId.OpenParen, Res.OpenParenExpected); + NextToken(); + Expression e = ParseExpression(); + ValidateToken(TokenId.CloseParen, Res.CloseParenOrOperatorExpected); + NextToken(); + return e; + } + + Expression ParseIdentifier() { + ValidateToken(TokenId.Identifier); + object value; + if (keywords.TryGetValue(token.text, out value)) { + if (value is Type) return ParseTypeAccess((Type)value); + if (value == (object)keywordIt) return ParseIt(); + if (value == (object)keywordIif) return ParseIif(); + if (value == (object)keywordNew) return ParseNew(); + NextToken(); + return (Expression)value; + } + if (symbols.TryGetValue(token.text, out value) || + externals != null && externals.TryGetValue(token.text, out value)) { + Expression expr = value as Expression; + if (expr == null) { + expr = Expression.Constant(value); + } + else { + LambdaExpression lambda = expr as LambdaExpression; + if (lambda != null) return ParseLambdaInvocation(lambda); + } + NextToken(); + return expr; + } + if (it != null) return ParseMemberAccess(null, it); + throw ParseError(Res.UnknownIdentifier, token.text); + } + + Expression ParseIt() { + if (it == null) + throw ParseError(Res.NoItInScope); + NextToken(); + return it; + } + + Expression ParseIif() { + int errorPos = token.pos; + NextToken(); + Expression[] args = ParseArgumentList(); + if (args.Length != 3) + throw ParseError(errorPos, Res.IifRequiresThreeArgs); + return GenerateConditional(args[0], args[1], args[2], errorPos); + } + + Expression GenerateConditional(Expression test, Expression expr1, Expression expr2, int errorPos) { + if (test.Type != typeof(bool)) + throw ParseError(errorPos, Res.FirstExprMustBeBool); + if (expr1.Type != expr2.Type) { + Expression expr1as2 = expr2 != nullLiteral ? PromoteExpression(expr1, expr2.Type, true) : null; + Expression expr2as1 = expr1 != nullLiteral ? PromoteExpression(expr2, expr1.Type, true) : null; + if (expr1as2 != null && expr2as1 == null) { + expr1 = expr1as2; + } + else if (expr2as1 != null && expr1as2 == null) { + expr2 = expr2as1; + } + else { + string type1 = expr1 != nullLiteral ? expr1.Type.Name : "null"; + string type2 = expr2 != nullLiteral ? expr2.Type.Name : "null"; + if (expr1as2 != null && expr2as1 != null) + throw ParseError(errorPos, Res.BothTypesConvertToOther, type1, type2); + throw ParseError(errorPos, Res.NeitherTypeConvertsToOther, type1, type2); + } + } + return Expression.Condition(test, expr1, expr2); + } + + Expression ParseNew() { + NextToken(); + ValidateToken(TokenId.OpenParen, Res.OpenParenExpected); + NextToken(); + List properties = new List(); + List expressions = new List(); + while (true) { + int exprPos = token.pos; + Expression expr = ParseExpression(); + string propName; + if (TokenIdentifierIs("as")) { + NextToken(); + propName = GetIdentifier(); + NextToken(); + } + else { + MemberExpression me = expr as MemberExpression; + if (me == null) throw ParseError(exprPos, Res.MissingAsClause); + propName = me.Member.Name; + } + expressions.Add(expr); + properties.Add(new DynamicProperty(propName, expr.Type)); + if (token.id != TokenId.Comma) break; + NextToken(); + } + ValidateToken(TokenId.CloseParen, Res.CloseParenOrCommaExpected); + NextToken(); + Type type = DynamicExpression.CreateClass(properties); + MemberBinding[] bindings = new MemberBinding[properties.Count]; + for (int i = 0; i < bindings.Length; i++) + bindings[i] = Expression.Bind(type.GetProperty(properties[i].Name), expressions[i]); + return Expression.MemberInit(Expression.New(type), bindings); + } + + Expression ParseLambdaInvocation(LambdaExpression lambda) { + int errorPos = token.pos; + NextToken(); + Expression[] args = ParseArgumentList(); + MethodBase method; + if (FindMethod(lambda.Type, "Invoke", false, args, out method) != 1) + throw ParseError(errorPos, Res.ArgsIncompatibleWithLambda); + return Expression.Invoke(lambda, args); + } + + Expression ParseTypeAccess(Type type) { + int errorPos = token.pos; + NextToken(); + if (token.id == TokenId.Question) { + if (!type.IsValueType || IsNullableType(type)) + throw ParseError(errorPos, Res.TypeHasNoNullableForm, GetTypeName(type)); + type = typeof(Nullable<>).MakeGenericType(type); + NextToken(); + } + if (token.id == TokenId.OpenParen) { + Expression[] args = ParseArgumentList(); + MethodBase method; + switch (FindBestMethod(type.GetConstructors(), args, out method)) { + case 0: + if (args.Length == 1) + return GenerateConversion(args[0], type, errorPos); + throw ParseError(errorPos, Res.NoMatchingConstructor, GetTypeName(type)); + case 1: + return Expression.New((ConstructorInfo)method, args); + default: + throw ParseError(errorPos, Res.AmbiguousConstructorInvocation, GetTypeName(type)); + } + } + ValidateToken(TokenId.Dot, Res.DotOrOpenParenExpected); + NextToken(); + return ParseMemberAccess(type, null); + } + + Expression GenerateConversion(Expression expr, Type type, int errorPos) { + Type exprType = expr.Type; + if (exprType == type) return expr; + if (exprType.IsValueType && type.IsValueType) { + if ((IsNullableType(exprType) || IsNullableType(type)) && + GetNonNullableType(exprType) == GetNonNullableType(type)) + return Expression.Convert(expr, type); + if ((IsNumericType(exprType) || IsEnumType(exprType)) && + (IsNumericType(type)) || IsEnumType(type)) + return Expression.ConvertChecked(expr, type); + } + if (exprType.IsAssignableFrom(type) || type.IsAssignableFrom(exprType) || + exprType.IsInterface || type.IsInterface) + return Expression.Convert(expr, type); + throw ParseError(errorPos, Res.CannotConvertValue, + GetTypeName(exprType), GetTypeName(type)); + } + + Expression ParseMemberAccess(Type type, Expression instance) { + if (instance != null) type = instance.Type; + int errorPos = token.pos; + string id = GetIdentifier(); + NextToken(); + if (token.id == TokenId.OpenParen) { + if (instance != null && type != typeof(string)) { + Type enumerableType = FindGenericType(typeof(IEnumerable<>), type); + if (enumerableType != null) { + Type elementType = enumerableType.GetGenericArguments()[0]; + return ParseAggregate(instance, elementType, id, errorPos); + } + } + Expression[] args = ParseArgumentList(); + MethodBase mb; + switch (FindMethod(type, id, instance == null, args, out mb)) { + case 0: + throw ParseError(errorPos, Res.NoApplicableMethod, + id, GetTypeName(type)); + case 1: + MethodInfo method = (MethodInfo)mb; + if (!IsPredefinedType(method.DeclaringType)) + throw ParseError(errorPos, Res.MethodsAreInaccessible, GetTypeName(method.DeclaringType)); + if (method.ReturnType == typeof(void)) + throw ParseError(errorPos, Res.MethodIsVoid, + id, GetTypeName(method.DeclaringType)); + return Expression.Call(instance, (MethodInfo)method, args); + default: + throw ParseError(errorPos, Res.AmbiguousMethodInvocation, + id, GetTypeName(type)); + } + } + else { + MemberInfo member = FindPropertyOrField(type, id, instance == null); + if (member == null) + throw ParseError(errorPos, Res.UnknownPropertyOrField, + id, GetTypeName(type)); + return member is PropertyInfo ? + Expression.Property(instance, (PropertyInfo)member) : + Expression.Field(instance, (FieldInfo)member); + } + } + + static Type FindGenericType(Type generic, Type type) { + while (type != null && type != typeof(object)) { + if (type.IsGenericType && type.GetGenericTypeDefinition() == generic) return type; + if (generic.IsInterface) { + foreach (Type intfType in type.GetInterfaces()) { + Type found = FindGenericType(generic, intfType); + if (found != null) return found; + } + } + type = type.BaseType; + } + return null; + } + + Expression ParseAggregate(Expression instance, Type elementType, string methodName, int errorPos) { + ParameterExpression outerIt = it; + ParameterExpression innerIt = Expression.Parameter(elementType, ""); + it = innerIt; + Expression[] args = ParseArgumentList(); + it = outerIt; + MethodBase signature; + if (FindMethod(typeof(IEnumerableSignatures), methodName, false, args, out signature) != 1) + throw ParseError(errorPos, Res.NoApplicableAggregate, methodName); + Type[] typeArgs; + if (signature.Name == "Min" || signature.Name == "Max") { + typeArgs = new Type[] { elementType, args[0].Type }; + } + else { + typeArgs = new Type[] { elementType }; + } + if (args.Length == 0) { + args = new Expression[] { instance }; + } + else { + args = new Expression[] { instance, Expression.Lambda(args[0], innerIt) }; + } + return Expression.Call(typeof(Enumerable), signature.Name, typeArgs, args); + } + + Expression[] ParseArgumentList() { + ValidateToken(TokenId.OpenParen, Res.OpenParenExpected); + NextToken(); + Expression[] args = token.id != TokenId.CloseParen ? ParseArguments() : new Expression[0]; + ValidateToken(TokenId.CloseParen, Res.CloseParenOrCommaExpected); + NextToken(); + return args; + } + + Expression[] ParseArguments() { + List argList = new List(); + while (true) { + argList.Add(ParseExpression()); + if (token.id != TokenId.Comma) break; + NextToken(); + } + return argList.ToArray(); + } + + Expression ParseElementAccess(Expression expr) { + int errorPos = token.pos; + ValidateToken(TokenId.OpenBracket, Res.OpenParenExpected); + NextToken(); + Expression[] args = ParseArguments(); + ValidateToken(TokenId.CloseBracket, Res.CloseBracketOrCommaExpected); + NextToken(); + if (expr.Type.IsArray) { + if (expr.Type.GetArrayRank() != 1 || args.Length != 1) + throw ParseError(errorPos, Res.CannotIndexMultiDimArray); + Expression index = PromoteExpression(args[0], typeof(int), true); + if (index == null) + throw ParseError(errorPos, Res.InvalidIndex); + return Expression.ArrayIndex(expr, index); + } + else { + MethodBase mb; + switch (FindIndexer(expr.Type, args, out mb)) { + case 0: + throw ParseError(errorPos, Res.NoApplicableIndexer, + GetTypeName(expr.Type)); + case 1: + return Expression.Call(expr, (MethodInfo)mb, args); + default: + throw ParseError(errorPos, Res.AmbiguousIndexerInvocation, + GetTypeName(expr.Type)); + } + } + } + + static bool IsPredefinedType(Type type) { + foreach (Type t in predefinedTypes) if (t == type) return true; + return false; + } + + static bool IsNullableType(Type type) { + return type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>); + } + + static Type GetNonNullableType(Type type) { + return IsNullableType(type) ? type.GetGenericArguments()[0] : type; + } + + static string GetTypeName(Type type) { + Type baseType = GetNonNullableType(type); + string s = baseType.Name; + if (type != baseType) s += '?'; + return s; + } + + static bool IsNumericType(Type type) { + return GetNumericTypeKind(type) != 0; + } + + static bool IsSignedIntegralType(Type type) { + return GetNumericTypeKind(type) == 2; + } + + static bool IsUnsignedIntegralType(Type type) { + return GetNumericTypeKind(type) == 3; + } + + static int GetNumericTypeKind(Type type) { + type = GetNonNullableType(type); + if (type.IsEnum) return 0; + switch (Type.GetTypeCode(type)) { + case TypeCode.Char: + case TypeCode.Single: + case TypeCode.Double: + case TypeCode.Decimal: + return 1; + case TypeCode.SByte: + case TypeCode.Int16: + case TypeCode.Int32: + case TypeCode.Int64: + return 2; + case TypeCode.Byte: + case TypeCode.UInt16: + case TypeCode.UInt32: + case TypeCode.UInt64: + return 3; + default: + return 0; + } + } + + static bool IsEnumType(Type type) { + return GetNonNullableType(type).IsEnum; + } + + void CheckAndPromoteOperand(Type signatures, string opName, ref Expression expr, int errorPos) { + Expression[] args = new Expression[] { expr }; + MethodBase method; + if (FindMethod(signatures, "F", false, args, out method) != 1) + throw ParseError(errorPos, Res.IncompatibleOperand, + opName, GetTypeName(args[0].Type)); + expr = args[0]; + } + + void CheckAndPromoteOperands(Type signatures, string opName, ref Expression left, ref Expression right, int errorPos) { + Expression[] args = new Expression[] { left, right }; + MethodBase method; + if (FindMethod(signatures, "F", false, args, out method) != 1) + throw IncompatibleOperandsError(opName, left, right, errorPos); + left = args[0]; + right = args[1]; + } + + Exception IncompatibleOperandsError(string opName, Expression left, Expression right, int pos) { + return ParseError(pos, Res.IncompatibleOperands, + opName, GetTypeName(left.Type), GetTypeName(right.Type)); + } + + MemberInfo FindPropertyOrField(Type type, string memberName, bool staticAccess) { + BindingFlags flags = BindingFlags.Public | BindingFlags.DeclaredOnly | + (staticAccess ? BindingFlags.Static : BindingFlags.Instance); + foreach (Type t in SelfAndBaseTypes(type)) { + MemberInfo[] members = t.FindMembers(MemberTypes.Property | MemberTypes.Field, + flags, Type.FilterNameIgnoreCase, memberName); + if (members.Length != 0) return members[0]; + } + return null; + } + + int FindMethod(Type type, string methodName, bool staticAccess, Expression[] args, out MethodBase method) { + BindingFlags flags = BindingFlags.Public | BindingFlags.DeclaredOnly | + (staticAccess ? BindingFlags.Static : BindingFlags.Instance); + foreach (Type t in SelfAndBaseTypes(type)) { + MemberInfo[] members = t.FindMembers(MemberTypes.Method, + flags, Type.FilterNameIgnoreCase, methodName); + int count = FindBestMethod(members.Cast(), args, out method); + if (count != 0) return count; + } + method = null; + return 0; + } + + int FindIndexer(Type type, Expression[] args, out MethodBase method) { + foreach (Type t in SelfAndBaseTypes(type)) { + MemberInfo[] members = t.GetDefaultMembers(); + if (members.Length != 0) { + IEnumerable methods = members. + OfType(). + Select(p => (MethodBase)p.GetGetMethod()). + Where(m => m != null); + int count = FindBestMethod(methods, args, out method); + if (count != 0) return count; + } + } + method = null; + return 0; + } + + static IEnumerable SelfAndBaseTypes(Type type) { + if (type.IsInterface) { + List types = new List(); + AddInterface(types, type); + return types; + } + return SelfAndBaseClasses(type); + } + + static IEnumerable SelfAndBaseClasses(Type type) { + while (type != null) { + yield return type; + type = type.BaseType; + } + } + + static void AddInterface(List types, Type type) { + if (!types.Contains(type)) { + types.Add(type); + foreach (Type t in type.GetInterfaces()) AddInterface(types, t); + } + } + + class MethodData + { + public MethodBase MethodBase; + public ParameterInfo[] Parameters; + public Expression[] Args; + } + + int FindBestMethod(IEnumerable methods, Expression[] args, out MethodBase method) { + MethodData[] applicable = methods. + Select(m => new MethodData { MethodBase = m, Parameters = m.GetParameters() }). + Where(m => IsApplicable(m, args)). + ToArray(); + if (applicable.Length > 1) { + applicable = applicable. + Where(m => applicable.All(n => m == n || IsBetterThan(args, m, n))). + ToArray(); + } + if (applicable.Length == 1) { + MethodData md = applicable[0]; + for (int i = 0; i < args.Length; i++) args[i] = md.Args[i]; + method = md.MethodBase; + } + else { + method = null; + } + return applicable.Length; + } + + bool IsApplicable(MethodData method, Expression[] args) { + if (method.Parameters.Length != args.Length) return false; + Expression[] promotedArgs = new Expression[args.Length]; + for (int i = 0; i < args.Length; i++) { + ParameterInfo pi = method.Parameters[i]; + if (pi.IsOut) return false; + Expression promoted = PromoteExpression(args[i], pi.ParameterType, false); + if (promoted == null) return false; + promotedArgs[i] = promoted; + } + method.Args = promotedArgs; + return true; + } + + Expression PromoteExpression(Expression expr, Type type, bool exact) { + if (expr.Type == type) return expr; + if (expr is ConstantExpression) { + ConstantExpression ce = (ConstantExpression)expr; + if (ce == nullLiteral) { + if (!type.IsValueType || IsNullableType(type)) + return Expression.Constant(null, type); + } + else { + string text; + if (literals.TryGetValue(ce, out text)) { + Type target = GetNonNullableType(type); + Object value = null; + switch (Type.GetTypeCode(ce.Type)) { + case TypeCode.Int32: + case TypeCode.UInt32: + case TypeCode.Int64: + case TypeCode.UInt64: + value = ParseNumber(text, target); + break; + case TypeCode.Double: + if (target == typeof(decimal)) value = ParseNumber(text, target); + break; + case TypeCode.String: + value = ParseEnum(text, target); + break; + } + if (value != null) + return Expression.Constant(value, type); + } + } + } + if (IsCompatibleWith(expr.Type, type)) { + if (type.IsValueType || exact) return Expression.Convert(expr, type); + return expr; + } + return null; + } + + static object ParseNumber(string text, Type type) { + switch (Type.GetTypeCode(GetNonNullableType(type))) { + case TypeCode.SByte: + sbyte sb; + if (sbyte.TryParse(text, out sb)) return sb; + break; + case TypeCode.Byte: + byte b; + if (byte.TryParse(text, out b)) return b; + break; + case TypeCode.Int16: + short s; + if (short.TryParse(text, out s)) return s; + break; + case TypeCode.UInt16: + ushort us; + if (ushort.TryParse(text, out us)) return us; + break; + case TypeCode.Int32: + int i; + if (int.TryParse(text, out i)) return i; + break; + case TypeCode.UInt32: + uint ui; + if (uint.TryParse(text, out ui)) return ui; + break; + case TypeCode.Int64: + long l; + if (long.TryParse(text, out l)) return l; + break; + case TypeCode.UInt64: + ulong ul; + if (ulong.TryParse(text, out ul)) return ul; + break; + case TypeCode.Single: + float f; + if (float.TryParse(text, out f)) return f; + break; + case TypeCode.Double: + double d; + if (double.TryParse(text, out d)) return d; + break; + case TypeCode.Decimal: + decimal e; + if (decimal.TryParse(text, out e)) return e; + break; + } + return null; + } + + static object ParseEnum(string name, Type type) { + if (type.IsEnum) { + MemberInfo[] memberInfos = type.FindMembers(MemberTypes.Field, + BindingFlags.Public | BindingFlags.DeclaredOnly | BindingFlags.Static, + Type.FilterNameIgnoreCase, name); + if (memberInfos.Length != 0) return ((FieldInfo)memberInfos[0]).GetValue(null); + } + return null; + } + + static bool IsCompatibleWith(Type source, Type target) { + if (source == target) return true; + if (!target.IsValueType) return target.IsAssignableFrom(source); + Type st = GetNonNullableType(source); + Type tt = GetNonNullableType(target); + if (st != source && tt == target) return false; + TypeCode sc = st.IsEnum ? TypeCode.Object : Type.GetTypeCode(st); + TypeCode tc = tt.IsEnum ? TypeCode.Object : Type.GetTypeCode(tt); + switch (sc) { + case TypeCode.SByte: + switch (tc) { + case TypeCode.SByte: + case TypeCode.Int16: + case TypeCode.Int32: + case TypeCode.Int64: + case TypeCode.Single: + case TypeCode.Double: + case TypeCode.Decimal: + return true; + } + break; + case TypeCode.Byte: + switch (tc) { + case TypeCode.Byte: + case TypeCode.Int16: + case TypeCode.UInt16: + case TypeCode.Int32: + case TypeCode.UInt32: + case TypeCode.Int64: + case TypeCode.UInt64: + case TypeCode.Single: + case TypeCode.Double: + case TypeCode.Decimal: + return true; + } + break; + case TypeCode.Int16: + switch (tc) { + case TypeCode.Int16: + case TypeCode.Int32: + case TypeCode.Int64: + case TypeCode.Single: + case TypeCode.Double: + case TypeCode.Decimal: + return true; + } + break; + case TypeCode.UInt16: + switch (tc) { + case TypeCode.UInt16: + case TypeCode.Int32: + case TypeCode.UInt32: + case TypeCode.Int64: + case TypeCode.UInt64: + case TypeCode.Single: + case TypeCode.Double: + case TypeCode.Decimal: + return true; + } + break; + case TypeCode.Int32: + switch (tc) { + case TypeCode.Int32: + case TypeCode.Int64: + case TypeCode.Single: + case TypeCode.Double: + case TypeCode.Decimal: + return true; + } + break; + case TypeCode.UInt32: + switch (tc) { + case TypeCode.UInt32: + case TypeCode.Int64: + case TypeCode.UInt64: + case TypeCode.Single: + case TypeCode.Double: + case TypeCode.Decimal: + return true; + } + break; + case TypeCode.Int64: + switch (tc) { + case TypeCode.Int64: + case TypeCode.Single: + case TypeCode.Double: + case TypeCode.Decimal: + return true; + } + break; + case TypeCode.UInt64: + switch (tc) { + case TypeCode.UInt64: + case TypeCode.Single: + case TypeCode.Double: + case TypeCode.Decimal: + return true; + } + break; + case TypeCode.Single: + switch (tc) { + case TypeCode.Single: + case TypeCode.Double: + return true; + } + break; + default: + if (st == tt) return true; + break; + } + return false; + } + + static bool IsBetterThan(Expression[] args, MethodData m1, MethodData m2) { + bool better = false; + for (int i = 0; i < args.Length; i++) { + int c = CompareConversions(args[i].Type, + m1.Parameters[i].ParameterType, + m2.Parameters[i].ParameterType); + if (c < 0) return false; + if (c > 0) better = true; + } + return better; + } + + // Return 1 if s -> t1 is a better conversion than s -> t2 + // Return -1 if s -> t2 is a better conversion than s -> t1 + // Return 0 if neither conversion is better + static int CompareConversions(Type s, Type t1, Type t2) { + if (t1 == t2) return 0; + if (s == t1) return 1; + if (s == t2) return -1; + bool t1t2 = IsCompatibleWith(t1, t2); + bool t2t1 = IsCompatibleWith(t2, t1); + if (t1t2 && !t2t1) return 1; + if (t2t1 && !t1t2) return -1; + if (IsSignedIntegralType(t1) && IsUnsignedIntegralType(t2)) return 1; + if (IsSignedIntegralType(t2) && IsUnsignedIntegralType(t1)) return -1; + return 0; + } + + Expression GenerateEqual(Expression left, Expression right) { + return Expression.Equal(left, right); + } + + Expression GenerateNotEqual(Expression left, Expression right) { + return Expression.NotEqual(left, right); + } + + Expression GenerateGreaterThan(Expression left, Expression right) { + if (left.Type == typeof(string)) { + return Expression.GreaterThan( + GenerateStaticMethodCall("Compare", left, right), + Expression.Constant(0) + ); + } + return Expression.GreaterThan(left, right); + } + + Expression GenerateGreaterThanEqual(Expression left, Expression right) { + if (left.Type == typeof(string)) { + return Expression.GreaterThanOrEqual( + GenerateStaticMethodCall("Compare", left, right), + Expression.Constant(0) + ); + } + return Expression.GreaterThanOrEqual(left, right); + } + + Expression GenerateLessThan(Expression left, Expression right) { + if (left.Type == typeof(string)) { + return Expression.LessThan( + GenerateStaticMethodCall("Compare", left, right), + Expression.Constant(0) + ); + } + return Expression.LessThan(left, right); + } + + Expression GenerateLessThanEqual(Expression left, Expression right) { + if (left.Type == typeof(string)) { + return Expression.LessThanOrEqual( + GenerateStaticMethodCall("Compare", left, right), + Expression.Constant(0) + ); + } + return Expression.LessThanOrEqual(left, right); + } + + Expression GenerateAdd(Expression left, Expression right) { + if (left.Type == typeof(string) && right.Type == typeof(string)) { + return GenerateStaticMethodCall("Concat", left, right); + } + return Expression.Add(left, right); + } + + Expression GenerateSubtract(Expression left, Expression right) { + return Expression.Subtract(left, right); + } + + Expression GenerateStringConcat(Expression left, Expression right) { + return Expression.Call( + null, + typeof(string).GetMethod("Concat", new[] { typeof(object), typeof(object) }), + new[] { left, right }); + } + + MethodInfo GetStaticMethod(string methodName, Expression left, Expression right) { + return left.Type.GetMethod(methodName, new[] { left.Type, right.Type }); + } + + Expression GenerateStaticMethodCall(string methodName, Expression left, Expression right) { + return Expression.Call(null, GetStaticMethod(methodName, left, right), new[] { left, right }); + } + + void SetTextPos(int pos) { + textPos = pos; + ch = textPos < textLen ? text[textPos] : '\0'; + } + + void NextChar() { + if (textPos < textLen) textPos++; + ch = textPos < textLen ? text[textPos] : '\0'; + } + + void NextToken() { + while (Char.IsWhiteSpace(ch)) NextChar(); + TokenId t; + int tokenPos = textPos; + switch (ch) { + case '!': + NextChar(); + if (ch == '=') { + NextChar(); + t = TokenId.ExclamationEqual; + } + else { + t = TokenId.Exclamation; + } + break; + case '%': + NextChar(); + t = TokenId.Percent; + break; + case '&': + NextChar(); + if (ch == '&') { + NextChar(); + t = TokenId.DoubleAmphersand; + } + else { + t = TokenId.Amphersand; + } + break; + case '(': + NextChar(); + t = TokenId.OpenParen; + break; + case ')': + NextChar(); + t = TokenId.CloseParen; + break; + case '*': + NextChar(); + t = TokenId.Asterisk; + break; + case '+': + NextChar(); + t = TokenId.Plus; + break; + case ',': + NextChar(); + t = TokenId.Comma; + break; + case '-': + NextChar(); + t = TokenId.Minus; + break; + case '.': + NextChar(); + t = TokenId.Dot; + break; + case '/': + NextChar(); + t = TokenId.Slash; + break; + case ':': + NextChar(); + t = TokenId.Colon; + break; + case '<': + NextChar(); + if (ch == '=') { + NextChar(); + t = TokenId.LessThanEqual; + } + else if (ch == '>') { + NextChar(); + t = TokenId.LessGreater; + } + else { + t = TokenId.LessThan; + } + break; + case '=': + NextChar(); + if (ch == '=') { + NextChar(); + t = TokenId.DoubleEqual; + } + else { + t = TokenId.Equal; + } + break; + case '>': + NextChar(); + if (ch == '=') { + NextChar(); + t = TokenId.GreaterThanEqual; + } + else { + t = TokenId.GreaterThan; + } + break; + case '?': + NextChar(); + t = TokenId.Question; + break; + case '[': + NextChar(); + t = TokenId.OpenBracket; + break; + case ']': + NextChar(); + t = TokenId.CloseBracket; + break; + case '|': + NextChar(); + if (ch == '|') { + NextChar(); + t = TokenId.DoubleBar; + } + else { + t = TokenId.Bar; + } + break; + case '"': + case '\'': + char quote = ch; + do { + NextChar(); + while (textPos < textLen && ch != quote) NextChar(); + if (textPos == textLen) + throw ParseError(textPos, Res.UnterminatedStringLiteral); + NextChar(); + } while (ch == quote); + t = TokenId.StringLiteral; + break; + default: + if (Char.IsLetter(ch) || ch == '@' || ch == '_') { + do { + NextChar(); + } while (Char.IsLetterOrDigit(ch) || ch == '_'); + t = TokenId.Identifier; + break; + } + if (Char.IsDigit(ch)) { + t = TokenId.IntegerLiteral; + do { + NextChar(); + } while (Char.IsDigit(ch)); + if (ch == '.') { + t = TokenId.RealLiteral; + NextChar(); + ValidateDigit(); + do { + NextChar(); + } while (Char.IsDigit(ch)); + } + if (ch == 'E' || ch == 'e') { + t = TokenId.RealLiteral; + NextChar(); + if (ch == '+' || ch == '-') NextChar(); + ValidateDigit(); + do { + NextChar(); + } while (Char.IsDigit(ch)); + } + if (ch == 'F' || ch == 'f') NextChar(); + break; + } + if (textPos == textLen) { + t = TokenId.End; + break; + } + throw ParseError(textPos, Res.InvalidCharacter, ch); + } + token.id = t; + token.text = text.Substring(tokenPos, textPos - tokenPos); + token.pos = tokenPos; + } + + bool TokenIdentifierIs(string id) { + return token.id == TokenId.Identifier && String.Equals(id, token.text, StringComparison.OrdinalIgnoreCase); + } + + string GetIdentifier() { + ValidateToken(TokenId.Identifier, Res.IdentifierExpected); + string id = token.text; + if (id.Length > 1 && id[0] == '@') id = id.Substring(1); + return id; + } + + void ValidateDigit() { + if (!Char.IsDigit(ch)) throw ParseError(textPos, Res.DigitExpected); + } + + void ValidateToken(TokenId t, string errorMessage) { + if (token.id != t) throw ParseError(errorMessage); + } + + void ValidateToken(TokenId t) { + if (token.id != t) throw ParseError(Res.SyntaxError); + } + + Exception ParseError(string format, params object[] args) { + return ParseError(token.pos, format, args); + } + + Exception ParseError(int pos, string format, params object[] args) { + return new ParseException(string.Format(System.Globalization.CultureInfo.CurrentCulture, format, args), pos); + } + + static Dictionary CreateKeywords() { + Dictionary d = new Dictionary(StringComparer.OrdinalIgnoreCase); + d.Add("true", trueLiteral); + d.Add("false", falseLiteral); + d.Add("null", nullLiteral); + d.Add(keywordIt, keywordIt); + d.Add(keywordIif, keywordIif); + d.Add(keywordNew, keywordNew); + foreach (Type type in predefinedTypes) d.Add(type.Name, type); + return d; + } + } + + static class Res + { + public const string DuplicateIdentifier = "The identifier '{0}' was defined more than once"; + public const string ExpressionTypeMismatch = "Expression of type '{0}' expected"; + public const string ExpressionExpected = "Expression expected"; + public const string InvalidCharacterLiteral = "Character literal must contain exactly one character"; + public const string InvalidIntegerLiteral = "Invalid integer literal '{0}'"; + public const string InvalidRealLiteral = "Invalid real literal '{0}'"; + public const string UnknownIdentifier = "Unknown identifier '{0}'"; + public const string NoItInScope = "No 'it' is in scope"; + public const string IifRequiresThreeArgs = "The 'iif' function requires three arguments"; + public const string FirstExprMustBeBool = "The first expression must be of type 'Boolean'"; + public const string BothTypesConvertToOther = "Both of the types '{0}' and '{1}' convert to the other"; + public const string NeitherTypeConvertsToOther = "Neither of the types '{0}' and '{1}' converts to the other"; + public const string MissingAsClause = "Expression is missing an 'as' clause"; + public const string ArgsIncompatibleWithLambda = "Argument list incompatible with lambda expression"; + public const string TypeHasNoNullableForm = "Type '{0}' has no nullable form"; + public const string NoMatchingConstructor = "No matching constructor in type '{0}'"; + public const string AmbiguousConstructorInvocation = "Ambiguous invocation of '{0}' constructor"; + public const string CannotConvertValue = "A value of type '{0}' cannot be converted to type '{1}'"; + public const string NoApplicableMethod = "No applicable method '{0}' exists in type '{1}'"; + public const string MethodsAreInaccessible = "Methods on type '{0}' are not accessible"; + public const string MethodIsVoid = "Method '{0}' in type '{1}' does not return a value"; + public const string AmbiguousMethodInvocation = "Ambiguous invocation of method '{0}' in type '{1}'"; + public const string UnknownPropertyOrField = "No property or field '{0}' exists in type '{1}'"; + public const string NoApplicableAggregate = "No applicable aggregate method '{0}' exists"; + public const string CannotIndexMultiDimArray = "Indexing of multi-dimensional arrays is not supported"; + public const string InvalidIndex = "Array index must be an integer expression"; + public const string NoApplicableIndexer = "No applicable indexer exists in type '{0}'"; + public const string AmbiguousIndexerInvocation = "Ambiguous invocation of indexer in type '{0}'"; + public const string IncompatibleOperand = "Operator '{0}' incompatible with operand type '{1}'"; + public const string IncompatibleOperands = "Operator '{0}' incompatible with operand types '{1}' and '{2}'"; + public const string UnterminatedStringLiteral = "Unterminated string literal"; + public const string InvalidCharacter = "Syntax error '{0}'"; + public const string DigitExpected = "Digit expected"; + public const string SyntaxError = "Syntax error"; + public const string TokenExpected = "{0} expected"; + public const string ParseExceptionFormat = "{0} (at index {1})"; + public const string ColonExpected = "':' expected"; + public const string OpenParenExpected = "'(' expected"; + public const string CloseParenOrOperatorExpected = "')' or operator expected"; + public const string CloseParenOrCommaExpected = "')' or ',' expected"; + public const string DotOrOpenParenExpected = "'.' or '(' expected"; + public const string OpenBracketExpected = "'[' expected"; + public const string CloseBracketOrCommaExpected = "']' or ',' expected"; + public const string IdentifierExpected = "Identifier expected"; + } +} diff --git a/Infrastructure/Encryption.cs b/Infrastructure/Encryption.cs new file mode 100644 index 0000000..20f4483 --- /dev/null +++ b/Infrastructure/Encryption.cs @@ -0,0 +1,59 @@ +using System; +using System.Security.Cryptography; +using System.Text; + +namespace Infrastructure +{ + public class Encryption + { + private static string encryptKey = "4h!@w$rng,i#$@x1%)5^3(7*5P31/Ee0"; + + //默认密钥向量 + private static byte[] Keys = { 0x41, 0x72, 0x65, 0x79, 0x6F, 0x75, 0x6D, 0x79, 0x53, 0x6E, 0x6F, 0x77, 0x6D, 0x61, 0x6E, 0x3F }; + /// + /// 加密 + /// + /// + /// + public static string Encrypt(string encryptString) + { + if (string.IsNullOrEmpty(encryptString)) + return string.Empty; + RijndaelManaged rijndaelProvider = new RijndaelManaged(); + rijndaelProvider.Key = Encoding.UTF8.GetBytes(encryptKey.Substring(0, 32)); + rijndaelProvider.IV = Keys; + ICryptoTransform rijndaelEncrypt = rijndaelProvider.CreateEncryptor(); + + byte[] inputData = Encoding.UTF8.GetBytes(encryptString); + byte[] encryptedData = rijndaelEncrypt.TransformFinalBlock(inputData, 0, inputData.Length); + + return Convert.ToBase64String(encryptedData); + } + /// + /// 解密 + /// + /// + /// + public static string Decrypt(string decryptString) + { + if (string.IsNullOrEmpty(decryptString)) + return string.Empty; + try + { + RijndaelManaged rijndaelProvider = new RijndaelManaged(); + rijndaelProvider.Key = Encoding.UTF8.GetBytes(encryptKey.Substring(0, 32)); + rijndaelProvider.IV = Keys; + ICryptoTransform rijndaelDecrypt = rijndaelProvider.CreateDecryptor(); + + byte[] inputData = Convert.FromBase64String(decryptString); + byte[] decryptedData = rijndaelDecrypt.TransformFinalBlock(inputData, 0, inputData.Length); + + return Encoding.UTF8.GetString(decryptedData); + } + catch + { + return ""; + } + } + } +} diff --git a/Infrastructure/Extensions/AutofacManager/AutofacContainerModule.cs b/Infrastructure/Extensions/AutofacManager/AutofacContainerModule.cs new file mode 100644 index 0000000..8585aaa --- /dev/null +++ b/Infrastructure/Extensions/AutofacManager/AutofacContainerModule.cs @@ -0,0 +1,22 @@ +using System; + +namespace Infrastructure.Extensions.AutofacManager +{ + /// + /// 提供全局静态获取服务的能力。 + /// 例:AutofacContainerModule.GetService<IPathProvider>() + /// + public class AutofacContainerModule + { + static private IServiceProvider _provider; + public static void ConfigServiceProvider(IServiceProvider serviceProvider) + { + _provider = serviceProvider; + } + public static TService GetService() where TService:class + { + Type typeParameterType = typeof(TService); + return (TService)_provider.GetService(typeParameterType); + } + } +} diff --git a/Infrastructure/Extensions/AutofacManager/IDependency.cs b/Infrastructure/Extensions/AutofacManager/IDependency.cs new file mode 100644 index 0000000..638e254 --- /dev/null +++ b/Infrastructure/Extensions/AutofacManager/IDependency.cs @@ -0,0 +1,9 @@ +namespace Infrastructure.Extensions.AutofacManager +{ + /// + /// 所有AutoFac注入的基类 + /// + public interface IDependency + { + } +} diff --git a/Infrastructure/Extensions/ConvertJsonExtension.cs b/Infrastructure/Extensions/ConvertJsonExtension.cs new file mode 100644 index 0000000..3cc0e0a --- /dev/null +++ b/Infrastructure/Extensions/ConvertJsonExtension.cs @@ -0,0 +1,377 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Data; +using System.Reflection; +using System.Text; +using Newtonsoft.Json; + +namespace Infrastructure.Extensions +{ + public static class ConvertJsonExtension + { + #region 私有方法 + /// + /// 过滤特殊字符 + /// + /// 字符串 + /// json字符串 + private static string String2Json(String s) + { + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < s.Length; i++) + { + char c = s.ToCharArray()[i]; + switch (c) + { + case '\"': + sb.Append("\\\""); break; + case '\\': + sb.Append("\\\\"); break; + case '/': + sb.Append("\\/"); break; + case '\b': + sb.Append("\\b"); break; + case '\f': + sb.Append("\\f"); break; + case '\n': + sb.Append("\\n"); break; + case '\r': + sb.Append("\\r"); break; + case '\t': + sb.Append("\\t"); break; + default: + sb.Append(c); break; + } + } + return sb.ToString(); + } + /// + /// 格式化字符型、日期型、布尔型 + /// + /// + /// + /// + private static string StringFormat(string str, Type type) + { + if (type == typeof(string)) + { + str = String2Json(str); + str = "\"" + str + "\""; + } + else if (type == typeof(DateTime)) + { + str = "\"" + str + "\""; + } + else if (type == typeof(bool)) + { + str = str.ToLower(); + } + else if (type != typeof(string) && string.IsNullOrEmpty(str)) + { + str = "\"" + str + "\""; + } + return str; + } + + #endregion + + #region list转换成JSON + /// + /// list转换为Json + /// + /// + /// + /// + public static string ListToJson(this IList list) + { + object obj = list[0]; + return ListToJson(list, obj.GetType().Name); + } + /// + /// list转换为json + /// + /// + /// + /// + /// + private static string ListToJson(this IList list, string JsonName) + { + if (list.Count == 0) + { + return ""; + } + StringBuilder Json = new StringBuilder(); + if (string.IsNullOrEmpty(JsonName)) + JsonName = list[0].GetType().Name; + Json.Append("{\"" + JsonName + "\":["); + + for (int i = 0; i < list.Count; i++) + { + T obj = Activator.CreateInstance(); + PropertyInfo[] pi = obj.GetType().GetProperties(); + Json.Append("{"); + for (int j = 0; j < pi.Length; j++) + { + Type type = pi[j].GetValue(list[i], null).GetType(); + Json.Append("\"" + pi[j].Name.ToString() + "\":" + StringFormat(pi[j].GetValue(list[i], null).ToString(), type)); + if (j < pi.Length - 1) + { + Json.Append(","); + } + } + Json.Append("}"); + if (i < list.Count - 1) + { + Json.Append(","); + } + } + Json.Append("]}"); + return Json.ToString(); + } + #endregion + + #region 对象转换为Json + /// + /// 对象转换为json + /// + /// json对象 + /// json字符串 + public static string ToJson(this object jsonObject) + { + string jsonString = "{"; + PropertyInfo[] propertyInfo = jsonObject.GetType().GetProperties(); + for (int i = 0; i < propertyInfo.Length; i++) + { + object objectValue = propertyInfo[i].GetGetMethod().Invoke(jsonObject, null); + string value = string.Empty; + if (objectValue is DateTime || objectValue is Guid || objectValue is TimeSpan) + { + value = "'" + objectValue.ToString() + "'"; + } + else if (objectValue is string) + { + value = "'" + ToJson(objectValue.ToString()) + "'"; + } + else if (objectValue is IEnumerable) + { + value = ToJson((IEnumerable)objectValue); + } + else + { + value = ToJson(objectValue.ToString()); + } + jsonString += "\"" + ToJson(propertyInfo[i].Name) + "\":" + value + ","; + } + jsonString.Remove(jsonString.Length - 1, jsonString.Length); + return jsonString + "}"; + } + + #endregion + + #region 对象集合转换为json + /// + /// 对象集合转换为json + /// + /// 对象集合 + /// json字符串 + public static string ToJson(this IEnumerable array) + { + string jsonString = "{"; + foreach (object item in array) + { + jsonString += ToJson(item) + ","; + } + jsonString.Remove(jsonString.Length - 1, jsonString.Length); + return jsonString + "]"; + } + #endregion + + #region 普通集合转换Json + /// + /// 普通集合转换Json + /// + /// 集合对象 + /// Json字符串 + public static string ToArrayString(this IEnumerable array) + { + string jsonString = "["; + foreach (object item in array) + { + jsonString = ToJson(item.ToString()) + ","; + } + jsonString.Remove(jsonString.Length - 1, jsonString.Length); + return jsonString + "]"; + } + #endregion + + #region DataSet转换为Json + /// + /// DataSet转换为Json + /// + /// DataSet对象 + /// Json字符串 + public static string ToJson(this DataSet dataSet) + { + string jsonString = "{"; + foreach (DataTable table in dataSet.Tables) + { + jsonString += "\"" + table.TableName + "\":" + ToJson(table) + ","; + } + jsonString = jsonString.TrimEnd(','); + return jsonString + "}"; + } + #endregion + + #region Datatable转换为Json + /// + /// Datatable转换为Json + /// + /// Datatable对象 + /// Json字符串 + public static string ToJson(this DataTable dt) + { + StringBuilder jsonString = new StringBuilder(); + jsonString.Append("["); + DataRowCollection drc = dt.Rows; + for (int i = 0; i < drc.Count; i++) + { + jsonString.Append("{"); + for (int j = 0; j < dt.Columns.Count; j++) + { + string strKey = dt.Columns[j].ColumnName; + string strValue = drc[i][j].ToString(); + Type type = dt.Columns[j].DataType; + jsonString.Append("\"" + strKey + "\":"); + strValue = StringFormat(strValue, type); + if (j < dt.Columns.Count - 1) + { + jsonString.Append(strValue + ","); + } + else + { + jsonString.Append(strValue); + } + } + jsonString.Append("},"); + } + jsonString.Remove(jsonString.Length - 1, 1); + jsonString.Append("]"); + return jsonString.ToString(); + } + /// + /// DataTable转换为Json + /// + public static string ToJson(this DataTable dt, string jsonName) + { + StringBuilder Json = new StringBuilder(); + if (string.IsNullOrEmpty(jsonName)) + jsonName = dt.TableName; + Json.Append("{\"" + jsonName + "\":["); + if (dt.Rows.Count > 0) + { + for (int i = 0; i < dt.Rows.Count; i++) + { + Json.Append("{"); + for (int j = 0; j < dt.Columns.Count; j++) + { + Type type = dt.Rows[i][j].GetType(); + Json.Append("\"" + dt.Columns[j].ColumnName.ToString() + "\":" + StringFormat(dt.Rows[i][j].ToString(), type)); + if (j < dt.Columns.Count - 1) + { + Json.Append(","); + } + } + Json.Append("}"); + if (i < dt.Rows.Count - 1) + { + Json.Append(","); + } + } + } + Json.Append("]}"); + return Json.ToString(); + } + + #endregion + + #region DataReader转换为Json + /// + /// DataReader转换为Json + /// + /// DataReader对象 + /// Json字符串 + public static string ReaderJson(this IDataReader dataReader) + { + StringBuilder jsonString = new StringBuilder(); + Dictionary ModelField = new Dictionary(); + for (int i = 0; i < dataReader.FieldCount; i++) + { + ModelField.Add(dataReader.GetName(i), dataReader.GetFieldType(i)); + } + jsonString.Append("["); + while (dataReader.Read()) + { + jsonString.Append("{"); + foreach (KeyValuePair keyVal in ModelField) + { + Type type = keyVal.Value; + string strKey = keyVal.Key; + string strValue = dataReader[strKey].ToString(); + jsonString.Append("\"" + strKey + "\":"); + strValue = StringFormat(strValue, type); + jsonString.Append(strValue + ","); + } + jsonString.Remove(jsonString.Length - 1, 1); + jsonString.Append("},"); + } + dataReader.Close(); + jsonString.Remove(jsonString.Length - 1, 1); + jsonString.Append("]"); + return jsonString.ToString(); + } + #endregion + + + public static T DeserializeObject(this string entityString) + { + if (string.IsNullOrEmpty(entityString)) + { + return default(T); + } + if (entityString == "{}") + { + entityString = "[]"; + } + return JsonConvert.DeserializeObject(entityString); + } + + public static string Serialize(this object obj, JsonSerializerSettings formatDate = null) + { + if (obj == null) return null; + formatDate = formatDate ?? new JsonSerializerSettings + { + DateFormatString = "yyyy-MM-dd HH:mm:ss" + }; + return JsonConvert.SerializeObject(obj, formatDate); + } + + // + // 摘要: + // 字串反序列化成指定对象实体(列表) + // + // 参数: + // Json: + // 字串 + // + // 类型参数: + // T: + // 实体类型 + public static List ToList(this string Json) + { + return (Json == null) ? null : JsonConvert.DeserializeObject>(Json); + } + + } +} + diff --git a/Infrastructure/Extensions/DateTimeExtension.cs b/Infrastructure/Extensions/DateTimeExtension.cs new file mode 100644 index 0000000..83838a2 --- /dev/null +++ b/Infrastructure/Extensions/DateTimeExtension.cs @@ -0,0 +1,52 @@ +using System; + +namespace Infrastructure.Extensions +{ + public static class DateTimeExtension + { + /// + /// 实现由C# 的时间到 Javascript 的时间的转换 + /// returns the number of milliseconds since Jan 1, 1970 (useful for converting C# dates to JS dates) + /// + /// + /// + public static double UnixTicks(this DateTime dt) + { + DateTime d1 = new DateTime(1970, 1, 1); + DateTime d2 = dt.AddHours(8).ToUniversalTime(); + TimeSpan ts = new TimeSpan(d2.Ticks - d1.Ticks); + return ts.TotalMilliseconds; + } + + /// + /// 将毫秒值转成 C# DateTime 类型 + /// + /// + /// + public static DateTime ConvertTime(this long time) + { + DateTime timeStamp = new DateTime(1970, 1, 1); //得到1970年的时间戳 + long t = (time + 8 * 60 * 60) * 10000000 + timeStamp.Ticks; + DateTime dt = new DateTime(t); + return dt; + } + /// + /// 获取格式化字符串,不带时分秒,格式:"yyyy-MM-dd" + /// + /// + /// + public static string ToDateString(this DateTime? dateTime) + { + if (!dateTime.HasValue) + { + return string.Empty; + } + + return dateTime.Value.ToDateString(); + } + public static string ToDateString(this DateTime dateTime) + { + return dateTime.ToString("yyyy-MM-dd"); + } + } +} diff --git a/Infrastructure/Extensions/EntityProperties.cs b/Infrastructure/Extensions/EntityProperties.cs new file mode 100644 index 0000000..b300850 --- /dev/null +++ b/Infrastructure/Extensions/EntityProperties.cs @@ -0,0 +1,1349 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +using System.Data; +using System.Linq; +using System.Linq.Expressions; +using System.Reflection; +using System.Text; +using Infrastructure.Const; +using Infrastructure.Utilities; + +namespace Infrastructure.Extensions +{ + public static class EntityProperties + { + public static string GetExpressionPropertyFirst(this Expression> properties) + { + string[] arr = properties.GetExpressionProperty(); + if (arr.Length > 0) + return arr[0]; + return ""; + } + + /// + /// 获取对象里指定成员名称 + /// + /// + /// > exp = x => new { x.字段1, x.字段2 };或x=>x.Name]]> + /// + public static string[] GetExpressionProperty(this Expression> properties) + { + if (properties == null) + return new string[] { }; + if (properties.Body is NewExpression) + return ((NewExpression) properties.Body).Members.Select(x => x.Name).ToArray(); + if (properties.Body is MemberExpression) + return new string[] {((MemberExpression) properties.Body).Member.Name}; + if (properties.Body is UnaryExpression) + return new string[] {((properties.Body as UnaryExpression).Operand as MemberExpression).Member.Name}; + throw new Exception("未实现的表达式"); + } + + public static string ValidateHashInEntity(this Type typeinfo, Dictionary dic) + { + return typeinfo.ValidateDicInEntity(dic, false); + } + + public static void RemoveNotExistColumns(this Type typeinfo, List cols) + { + } + + /// + /// 获取所有字段的名称 + /// + /// + /// + public static List GetAtrrNames(this Type typeinfo) + { + return typeinfo.GetProperties().Select(c => c.Name).ToList(); + } + + public static void IsExistColumns(this Type typeinfo) + { + } + + public static Dictionary GetColumType(this PropertyInfo[] properties) + { + return properties.GetColumType(false); + } + + public static Dictionary GetColumType(this PropertyInfo[] properties, bool containsKey) + { + Dictionary dictionary = new Dictionary(); + foreach (PropertyInfo property in properties) + { + if (!containsKey && property.IsKey()) + { + continue; + } + + var keyVal = GetColumnType(property, true); + dictionary.Add(keyVal.Key, keyVal.Value); + } + + return dictionary; + } + + private static readonly Dictionary entityMapDbColumnType = new Dictionary() + { + {typeof(int), SqlDbTypeName.Int}, + {typeof(int?), SqlDbTypeName.Int}, + {typeof(long), SqlDbTypeName.BigInt}, + {typeof(long?), SqlDbTypeName.BigInt}, + {typeof(decimal), "decimal(18, 5)"}, + {typeof(decimal?), "decimal(18, 5)"}, + {typeof(double), "decimal(18, 5)"}, + {typeof(double?), "decimal(18, 5)"}, + {typeof(float), "decimal(18, 5)"}, + {typeof(float?), "decimal(18, 5)"}, + {typeof(Guid), "UniqueIdentifier"}, + {typeof(Guid?), "UniqueIdentifier"}, + {typeof(byte), "tinyint"}, + {typeof(byte?), "tinyint"}, + {typeof(string), "nvarchar"} + }; + + /// + /// 返回属性的字段及数据库类型 + /// + /// + /// 是否包括后字段具体长度:nvarchar(100) + /// + public static KeyValuePair GetColumnType(this PropertyInfo property, bool lenght = false) + { + string colType = ""; + object objAtrr = property.GetTypeCustomAttributes(typeof(ColumnAttribute), out bool asType); + if (asType) + { + colType = ((ColumnAttribute) objAtrr).TypeName.ToLower(); + if (!string.IsNullOrEmpty(colType)) + { + //不需要具体长度直接返回 + if (!lenght) + { + return new KeyValuePair(property.Name, colType); + } + + if (colType == "decimal" || colType == "double" || colType == "float") + { + objAtrr = property.GetTypeCustomAttributes(typeof(DisplayFormatAttribute), out asType); + colType += "(" + (asType ? ((DisplayFormatAttribute) objAtrr).DataFormatString : "18,5") + ")"; + } + + //如果是string,根据 varchar或nvarchar判断最大长度 + if (property.PropertyType.ToString() == "System.String") + { + colType = colType.Split("(")[0]; + objAtrr = property.GetTypeCustomAttributes(typeof(MaxLengthAttribute), out asType); + if (asType) + { + int length = ((MaxLengthAttribute) objAtrr).Length; + colType += "(" + (length < 1 || length > (colType.StartsWith("n") ? 8000 : 4000) + ? "max" + : length.ToString()) + ")"; + } + else + { + colType += "(max)"; + } + } + + return new KeyValuePair(property.Name, colType); + } + } + + if (entityMapDbColumnType.TryGetValue(property.PropertyType, out string value)) + { + colType = value; + } + else + { + colType = SqlDbTypeName.NVarChar; + } + + if (lenght && colType == SqlDbTypeName.NVarChar) + { + colType = "nvarchar(max)"; + } + + return new KeyValuePair(property.Name, colType); + } + + /// + /// + /// + /// 将数组转换成sql语句 + /// 指定FieldType数据库字段类型 + /// + /// + public static string GetArraySql(this object[] array, FieldType fieldType) + { + if (array == null || array.Count() == 0) + { + return string.Empty; + } + + string columnType = string.Empty; + List arrrayEntityList = array.Select(x => new ArrayEntity {column1 = x.ToString()}).ToList(); + return arrrayEntityList.GetEntitySql(false, null, null, null, fieldType); + } + + /// + ///要执行的sql语句如:通过EntityToSqlTempName.Temp_Insert0.ToString()字符串占位,生成的的sql语句会把EntityToSqlTempName.Temp_Insert0.ToString()替换成生成的sql临时表数据 + /// string sql = " ;DELETE FROM " + typeEntity.Name + " where " + typeEntity.GetKeyName() + + /// " in (select * from " + EntityToSqlTempName.Temp_Insert0.ToString() + ")"; + /// + /// + /// + /// 指定生成的数组值的类型 + /// + public static string GetArraySql(this object[] array, FieldType fieldType, string sql) + { + if (array == null || array.Count() == 0) + { + return string.Empty; + } + + string columnType = string.Empty; + List arrrayEntityList = array.Select(x => new ArrayEntity {column1 = x.ToString()}).ToList(); + return arrrayEntityList.GetEntitySql(false, sql, null, null, fieldType); + } + + public static string GetArraySql(this object[] array, string sql) + { + return array.GetArraySql(typeof(T).GetFieldType(), sql); + } + + /// + /// 根据实体获取key的类型,用于update或del操作 + /// + /// + public static FieldType GetFieldType(this Type typeEntity) + { + FieldType fieldType; + string columnType = typeEntity.GetProperties().Where(x => x.Name == typeEntity.GetKeyName()).ToList()[0] + .GetColumnType(false).Value; + switch (columnType) + { + case SqlDbTypeName.Int: + fieldType = FieldType.Int; + break; + case SqlDbTypeName.BigInt: + fieldType = FieldType.BigInt; + break; + case SqlDbTypeName.VarChar: + fieldType = FieldType.VarChar; + break; + case SqlDbTypeName.UniqueIdentifier: + fieldType = FieldType.UniqueIdentifier; + break; + default: + fieldType = FieldType.NvarChar; + break; + } + + return fieldType; + } + + public static string GetEntitySql(this IEnumerable entityList, + bool containsKey = false, + string sql = null, + Expression> ignoreFileds = null, + Expression> fixedColumns = null, + FieldType? fieldType = null + ) + { + if (entityList == null || entityList.Count() == 0) return ""; + PropertyInfo[] propertyInfo = typeof(T).GetProperties().ToArray(); + if (propertyInfo.Count() == 0) + { + propertyInfo = entityList.ToArray()[0].GetType().GetGenericProperties().ToArray(); + } + + propertyInfo = propertyInfo.GetGenericProperties().ToArray(); + + string[] arr = null; + if (fixedColumns != null) + { + arr = fixedColumns.GetExpressionToArray(); + PropertyInfo keyProperty = typeof(T).GetKeyProperty(); + propertyInfo = propertyInfo + .Where(x => (containsKey && x.Name == keyProperty.Name) || arr.Contains(x.Name)).ToArray(); + } + + if (ignoreFileds != null) + { + arr = ignoreFileds.GetExpressionToArray(); + propertyInfo = propertyInfo.Where(x => !arr.Contains(x.Name)).ToArray(); + } + + Dictionary dictProperties = propertyInfo.GetColumType(containsKey); + if (fieldType != null) + { + string realType = fieldType.ToString(); + if ((int) fieldType == 0 || (int) fieldType == 1) + { + realType += "(max)"; + } + + dictProperties = new Dictionary + {{dictProperties.Select(x => x.Key).ToList()[0], realType}}; + } + + if (dictProperties.Keys.Count * entityList.Count() > 50 * 3000) + { + throw new Exception("写入数据太多,请分开写入。"); + } + + string cols = string.Join(",", dictProperties.Select(c => "[" + c.Key + "]" + " " + c.Value)); + StringBuilder declareTable = new StringBuilder(); + + string tempTablbe = "#" + EntityToSqlTempName.TempInsert.ToString(); + + declareTable.Append("CREATE TABLE " + tempTablbe + " (" + cols + ")"); + declareTable.Append("\r\n"); + + //参数总数量 + int parCount = (dictProperties.Count) * (entityList.Count()); + int takeCount = 0; + int maxParsCount = 2050; + if (parCount > maxParsCount) + { + //如果参数总数量超过2100,设置每次分批循环写入表的大小 + takeCount = maxParsCount / dictProperties.Count; + } + + int count = 0; + StringBuilder stringLeft = new StringBuilder(); + StringBuilder stringCenter = new StringBuilder(); + StringBuilder stringRight = new StringBuilder(); + + int index = 0; + foreach (T entity in entityList) + { + //每1000行需要分批写入(数据库限制每批至多写入1000行数据) + if (index == 0 || index >= 1000 || takeCount - index == 0) + { + if (stringLeft.Length > 0) + { + declareTable.AppendLine( + stringLeft.Remove(stringLeft.Length - 2, 2).Append("',").ToString() + + stringCenter.Remove(stringCenter.Length - 1, 1).Append("',").ToString() + + stringRight.Remove(stringRight.Length - 1, 1).ToString()); + + stringLeft.Clear(); + stringCenter.Clear(); + stringRight.Clear(); + } + + stringLeft.AppendLine("exec sp_executesql N'SET NOCOUNT ON;"); + stringCenter.Append("N'"); + + index = 0; + count = 0; + } + + stringLeft.Append(index == 0 ? "; INSERT INTO " + tempTablbe + " values (" : " "); + index++; + foreach (PropertyInfo property in propertyInfo) + { + if (!containsKey && property.IsKey()) + { + continue; + } + + string par = "@v" + count; + stringLeft.Append(par + ","); + stringCenter.Append(par + " " + dictProperties[property.Name] + ","); + object val = property.GetValue(entity); + if (val == null) + { + stringRight.Append(par + "=NUll,"); + } + else + { + stringRight.Append(par + "='" + val.ToString().Replace("'", "''''") + "',"); + } + + count++; + } + + stringLeft.Remove(stringLeft.Length - 1, 1); + stringLeft.Append("),("); + } + + if (stringLeft.Length > 0) + { + declareTable.AppendLine( + stringLeft.Remove(stringLeft.Length - 2, 2).Append("',").ToString() + + stringCenter.Remove(stringCenter.Length - 1, 1).Append("',").ToString() + + stringRight.Remove(stringRight.Length - 1, 1).ToString()); + + stringLeft.Clear(); + stringCenter.Clear(); + stringRight.Clear(); + } + + if (!string.IsNullOrEmpty(sql)) + { + sql = sql.Replace(EntityToSqlTempName.TempInsert.ToString(), tempTablbe); + declareTable.AppendLine(sql); + } + else + { + declareTable.AppendLine(" SELECT " + + (string.Join(",", fixedColumns?.GetExpressionToArray() ?? new string[] {"*"})) + + " FROM " + tempTablbe); + } + + + if (tempTablbe.Substring(0, 1) == "#") + { + declareTable.AppendLine("; drop table " + tempTablbe); + } + + return declareTable.ToString(); + } + + + /// + ///此方法适用于数据量少,只有几列数据,不超过1W行,或几十列数据不超过1000行的情况下使用 + /// 大批量的数据考虑其他方式 + /// 將datatable生成sql語句,替換datatable作為參數傳入存儲過程 + /// + /// + /// + public static string GetDataTableSql(this DataTable table) + { + Dictionary dictCloumn = new Dictionary(); + for (int i = 0; i < table.Columns.Count; i++) + { + dictCloumn.Add(table.Columns[i].ColumnName, " nvarchar(max)"); + } + + + //参数总数量 + int parCount = (dictCloumn.Count) * (table.Rows.Count); + int takeCount = 0; + int maxParsCount = 2050; + if (parCount > maxParsCount) + { + //如果参数总数量超过2100,设置每次分批循环写入表的大小 + takeCount = maxParsCount / dictCloumn.Count; + } + + if (dictCloumn.Keys.Count * table.Rows.Count > 50 * 3000) + { + throw new Exception("写入数据太多,请分开写入。"); + } + + string cols = string.Join(",", dictCloumn.Select(c => "[" + c.Key + "]" + " " + c.Value)); + StringBuilder declareTable = new StringBuilder(); + + string tempTablbe = "#Temp_Insert0"; + declareTable.Append("CREATE TABLE " + tempTablbe + " (" + cols + ")"); + declareTable.Append("\r\n"); + int count = 0; + StringBuilder stringLeft = new StringBuilder(); + StringBuilder stringCenter = new StringBuilder(); + StringBuilder stringRight = new StringBuilder(); + + int index = 0; + + foreach (DataRow row in table.Rows) + { + //每1000行需要分批写入(数据库限制每批至多写入1000行数据) + if (index == 0 || index >= 1000 || takeCount - index == 0) + { + if (stringLeft.Length > 0) + { + declareTable.AppendLine( + stringLeft.Remove(stringLeft.Length - 2, 2).Append("',").ToString() + + stringCenter.Remove(stringCenter.Length - 1, 1).Append("',").ToString() + + stringRight.Remove(stringRight.Length - 1, 1).ToString()); + + stringLeft.Clear(); + stringCenter.Clear(); + stringRight.Clear(); + } + + // sbLeft.AppendLine(" INSERT INTO @toInsert0"); + stringLeft.AppendLine("exec sp_executesql N'SET NOCOUNT ON;"); + stringCenter.Append("N'"); + + index = 0; + count = 0; + } + + stringLeft.Append(index == 0 ? "; INSERT INTO " + tempTablbe + " values (" : " "); + index++; + foreach (KeyValuePair keyValue in dictCloumn) + { + string par = "@v" + count; + stringLeft.Append(par + ","); + stringCenter.Append(par + " " + keyValue.Value + ","); + object val = row[keyValue.Key]; + if (val == null) + { + stringRight.Append(par + "=NUll,"); + } + else + { + stringRight.Append(par + "='" + val.ToString().Replace("'", "''''") + "',"); + } + + count++; + } + + stringLeft.Remove(stringLeft.Length - 1, 1); + stringLeft.Append("),("); + } + + + if (stringLeft.Length > 0) + { + declareTable.AppendLine( + stringLeft.Remove(stringLeft.Length - 2, 2).Append("',").ToString() + + stringCenter.Remove(stringCenter.Length - 1, 1).Append("',").ToString() + + stringRight.Remove(stringRight.Length - 1, 1).ToString()); + + stringLeft.Clear(); + stringCenter.Clear(); + stringRight.Clear(); + } + + declareTable.AppendLine(" SELECT * FROM " + tempTablbe); + if (tempTablbe.Substring(0, 1) == "#") + { + declareTable.AppendLine("; drop table " + tempTablbe); + } + + return declareTable.ToString(); + } + + + public static string GetKeyName(this Type typeinfo) + { + return typeinfo.GetProperties().GetKeyName(); + } + + public static string GetKeyType(this Type typeinfo) + { + string keyType = typeinfo.GetProperties().GetKeyName(true); + if (keyType == "varchar") + { + return "varchar(max)"; + } + else if (keyType != "nvarchar") + { + return keyType; + } + else + { + return "nvarchar(max)"; + } + } + + public static string GetKeyName(this PropertyInfo[] properties) + { + return properties.GetKeyName(false); + } + + /// + /// 获取key列名 + /// + /// + /// true获取key对应类型,false返回对象Key的名称 + /// + public static string GetKeyName(this PropertyInfo[] properties, bool keyType) + { + string keyName = string.Empty; + foreach (PropertyInfo propertyInfo in properties) + { + if (!propertyInfo.IsKey()) + continue; + if (!keyType) + return propertyInfo.Name; + var attributes = propertyInfo.GetCustomAttributes(typeof(ColumnAttribute), false); + //如果没有ColumnAttribute的需要单独再验证,下面只验证有属性的 + if (attributes.Length > 0) + return ((ColumnAttribute) attributes[0]).TypeName.ToLower(); + else + return GetColumType(new PropertyInfo[] {propertyInfo}, true)[propertyInfo.Name]; + } + + return keyName; + } + + /// + /// 获取主键字段 + /// + /// + /// + public static PropertyInfo GetKeyProperty(this Type entity) + { + return entity.GetProperties().GetKeyProperty(); + } + + public static PropertyInfo GetKeyProperty(this PropertyInfo[] properties) + { + return properties.Where(c => c.IsKey()).FirstOrDefault(); + } + + public static bool IsKey(this PropertyInfo propertyInfo) + { + object[] keyAttributes = propertyInfo.GetCustomAttributes(typeof(KeyAttribute), false); + if (keyAttributes.Length > 0) + return true; + return false; + } + + + private static string[] _userEditFields { get; set; } + + + /// + /// 判断是否包含某个属性 + /// public string MO {get; set; }包含Editable + /// + public static bool ContainsCustomAttributes(this PropertyInfo propertyInfo, Type type) + { + propertyInfo.GetTypeCustomAttributes(type, out bool contains); + return contains; + } + + public static List ContainsCustomAttributes(this Type obj, Type containType) + { + List proList = new List(); + foreach (PropertyInfo pro in obj.GetProperties()) + { + if (pro.GetTypeCustomAttributes(containType) != null) + { + proList.Add(pro); + } + } + + return proList; + } + + /// + /// 获取PropertyInfo指定属性 + /// + /// + /// + /// + public static object GetTypeCustomAttributes(this PropertyInfo propertyInfo, Type type, out bool asType) + { + object[] attributes = propertyInfo.GetCustomAttributes(type, false); + if (attributes.Length == 0) + { + asType = false; + return new string[0]; + } + + asType = true; + return attributes[0]; + } + + /// + /// 验证集合的属性 + /// + /// + /// + /// + /// + public static void ValidationEntityList(this List entityList, + Expression> expression = null) + { + WebResponseContent responseData = new WebResponseContent(); + foreach (T entity in entityList) + { + entity.ValidationEntity(expression); + } + } + + /// + /// 指定需要验证的字段 + /// + /// + /// + /// 对指定属性进行验证x=>{x.Name,x.Size} + /// + public static void ValidationEntity(this T entity, Expression> expression = null, + Expression> validateProperties = null) + { + ValidationEntity(entity, expression?.GetExpressionProperty(), + validateProperties?.GetExpressionProperty()); + } + + /// + /// specificProperties=null并且validateProperties=null,对所有属性验证,只验证其是否合法,不验证是否为空(除属性标识指定了不能为空外) + /// specificProperties!=null,对指定属性校验,并且都必须有值 + /// null并且validateProperties!=null,对指定属性校验,不判断是否有值 + /// + /// + /// + /// 验证指定的属性,并且非空判断 + /// 验证指定属性,只对字段合法性判断,不验证是否为空 + /// + public static void ValidationEntity(this T entity, string[] specificProperties, + string[] validateProperties = null) + { + if (entity == null) + { + throw new Exception("对象不能为null"); + } + + PropertyInfo[] propertyArray = typeof(T).GetProperties(); + //若T为object取不到属性 + if (propertyArray.Length == 0) + { + propertyArray = entity.GetType().GetProperties(); + } + + List compareProper = new List(); + + //只验证数据合法性,验证非空 + if (specificProperties != null && specificProperties.Length > 0) + { + compareProper.AddRange(propertyArray.Where(x => specificProperties.Contains(x.Name))); + } + + //只验证数据合法性,不验证非空 + if (validateProperties != null && validateProperties.Length > 0) + { + compareProper.AddRange(propertyArray.Where(x => validateProperties.Contains(x.Name))); + } + + if (compareProper.Count() > 0) + { + propertyArray = compareProper.ToArray(); + } + + foreach (PropertyInfo propertyInfo in propertyArray) + { + object value = propertyInfo.GetValue(entity); + //设置默认状态的值 + if (propertyInfo.Name == "Enable") + { + if (value == null) + { + propertyInfo.SetValue(entity, 0); + continue; + } + } + + //若存在specificProperties并且属性为数组specificProperties中的值,校验时就需要判断是否为空 + var reslut = propertyInfo.ValidationProperty(value, + specificProperties != null && specificProperties.Contains(propertyInfo.Name) ? true : false + ); + if (!reslut.Item1) + { + throw new Exception(reslut.Item2); + } + } + } + + /// + /// 获取数据库类型,不带长度,如varchar(100),只返回的varchar + /// + /// + /// + public static string GetSqlDbType(this PropertyInfo propertyInfo) + { + string dbType = propertyInfo.GetTypeCustomValue(x => new {x.TypeName}); + + if (string.IsNullOrEmpty(dbType)) + { + return dbType; + } + + dbType = dbType.ToLower(); + if (dbType.Contains(SqlDbTypeName.NVarChar)) + { + dbType = SqlDbTypeName.NVarChar; + } + else if (dbType.Contains(SqlDbTypeName.VarChar)) + { + dbType = SqlDbTypeName.VarChar; + } + else if (dbType.Contains(SqlDbTypeName.NChar)) + { + dbType = SqlDbTypeName.NChar; + } + else if (dbType.Contains(SqlDbTypeName.Char)) + { + dbType = SqlDbTypeName.Char; + } + + return dbType; + } + + /// + /// 验证数据库字段类型与值是否正确, + /// + /// propertyInfo为当字段,当前字段必须有ColumnAttribute属性, + /// 如字段:标识为数据库int类型[Column(TypeName="int")] public int Id { get; set; } + /// 如果是小数float或Decimal必须对propertyInfo字段加DisplayFormatAttribute属性 + /// + /// + /// IEnumerable<(bool, string, object)> bool成否校验成功,string校验失败信息,object,当前校验的值 + public static IEnumerable<(bool, string, object)> ValidationValueForDbType(this PropertyInfo propertyInfo, + params object[] values) + { + string dbTypeName = propertyInfo.GetTypeCustomValue(c => c.TypeName); + foreach (object value in values) + { + yield return dbTypeName.ValidationVal(value, propertyInfo); + } + } + + public static bool ValidationRquiredValueForDbType(this PropertyInfo propertyInfo, object value, + out string message) + { + if (value == null || value?.ToString()?.Trim() == "") + { + message = $"{propertyInfo.GetDisplayName()}不能为空"; + return false; + } + + var result = propertyInfo.GetProperWithDbType().ValidationVal(value, propertyInfo); + message = result.Item2; + return result.Item1; + } + + private static readonly Dictionary ProperWithDbType = new Dictionary() + { + {typeof(string), SqlDbTypeName.NVarChar}, + {typeof(DateTime), SqlDbTypeName.DateTime}, + {typeof(long), SqlDbTypeName.BigInt}, + {typeof(int), SqlDbTypeName.Int}, + {typeof(decimal), SqlDbTypeName.Decimal}, + {typeof(float), SqlDbTypeName.Float}, + {typeof(double), SqlDbTypeName.Double}, + {typeof(byte), SqlDbTypeName.Int}, //类型待完 + {typeof(Guid), SqlDbTypeName.UniqueIdentifier} + }; + + public static string GetProperWithDbType(this PropertyInfo propertyInfo) + { + bool result = ProperWithDbType.TryGetValue(propertyInfo.PropertyType, out string value); + if (result) + { + return value; + } + + return SqlDbTypeName.NVarChar; + } + + /// + /// 验证数据库字段类型与值是否正确, + /// + /// 数据库字段类型(如varchar,nvarchar,decimal,不要带后面长度如:varchar(50)) + /// 值 + /// 要验证的类的属性,若不为null,则会判断字符串的长度是否正确 + /// (bool, string, object)bool成否校验成功,string校验失败信息,object,当前校验的值 + public static (bool, string, object) ValidationVal(this string dbType, object value, + PropertyInfo propertyInfo = null) + { + if (string.IsNullOrEmpty(dbType)) + { + dbType = propertyInfo != null ? propertyInfo.GetProperWithDbType() : SqlDbTypeName.NVarChar; + } + + dbType = dbType.ToLower(); + string val = value?.ToString(); + //验证长度 + string reslutMsg = string.Empty; + if (dbType == SqlDbTypeName.Int || dbType == SqlDbTypeName.BigInt) + { + if (!StringExtension.IsInt(value)) + reslutMsg = "只能为有效整数"; + } + else if (dbType == SqlDbTypeName.DateTime + || dbType == SqlDbTypeName.Date + || dbType == SqlDbTypeName.SmallDateTime + || dbType == SqlDbTypeName.SmallDate + ) + { + if (!StringExtension.IsDate(value)) + reslutMsg = "必须为日期格式"; + } + else if (dbType == SqlDbTypeName.Float || dbType == SqlDbTypeName.Decimal || dbType == SqlDbTypeName.Double) + { + string formatString = string.Empty; + if (propertyInfo != null) + formatString = propertyInfo.GetTypeCustomValue(x => x.DataFormatString); + //if (string.IsNullOrEmpty(formatString)) + // throw new Exception("请对字段" + propertyInfo?.Name + "添加DisplayFormat属性标识"); + + if (!StringExtension.IsNumber(val, formatString)) + { + string[] arr = (formatString ?? "10,0").Split(','); + reslutMsg = $"整数{arr[0]}最多位,小数最多{arr[1]}位"; + } + } + else if (dbType == SqlDbTypeName.UniqueIdentifier) + { + if (!StringExtension.IsGuid(val)) + { + reslutMsg = propertyInfo.Name + "Guid不正确"; + } + } + else if (propertyInfo != null + && (dbType == SqlDbTypeName.VarChar + || dbType == SqlDbTypeName.NVarChar + || dbType == SqlDbTypeName.NChar + || dbType == SqlDbTypeName.Char + || dbType == SqlDbTypeName.Text)) + { + //默认nvarchar(max) 、text 长度不能超过20000 + if (val.Length > 20000) + { + reslutMsg = $"字符长度最多【20000】"; + } + else + { + int length = + StringExtension.GetInt( + propertyInfo.GetTypeCustomValue(x => new {x.Length})); + if (length == 0) + { + return (true, null, null); + } + //判断双字节与单字段 + else if (length < 8000 && + ((dbType.Substring(0, 1) != "n" + && Encoding.UTF8.GetBytes(val.ToCharArray()).Length > length) + || val.Length > length) + ) + { + reslutMsg = $"最多只能【{length}】个字符。"; + } + } + } + + if (!string.IsNullOrEmpty(reslutMsg) && propertyInfo != null) + { + reslutMsg = propertyInfo.GetDisplayName() + reslutMsg; + } + + return (reslutMsg == "" ? true : false, reslutMsg, value); + } + + public static string GetDisplayName(this PropertyInfo property) + { + string displayName = property.GetTypeCustomValue(x => new {x.Name}); + if (string.IsNullOrEmpty(displayName)) + { + return property.Name; + } + + return displayName; + } + + /// + /// 验证每个属性的值是否正确 + /// + /// + /// 属性的值 + /// 是否指定当前属性必须有值 + /// + public static (bool, string, object) ValidationProperty(this PropertyInfo propertyInfo, object objectVal, + bool required) + { + if (propertyInfo.IsKey()) + { + return (true, null, objectVal); + } + + string val = objectVal == null ? "" : objectVal.ToString().Trim(); + + string requiredMsg = string.Empty; + if (required) + { + var reuireVal = + propertyInfo.GetTypeCustomValues(x => new {x.AllowEmptyStrings, x.ErrorMessage}); + if (reuireVal != null && !Convert.ToBoolean(reuireVal["AllowEmptyStrings"])) + { + required = true; + requiredMsg = reuireVal["ErrorMessage"]; + } + } + + //如果不要求为必填项并且值为空,直接返回 + if (!required && string.IsNullOrEmpty(val)) + return (true, null, objectVal); + + if ((required && val == string.Empty)) + { + if (requiredMsg != "") return (false, requiredMsg, objectVal); + string propertyName = propertyInfo.GetTypeCustomValue(x => new {x.Name}); + return (false, + requiredMsg + (string.IsNullOrEmpty(propertyName) ? propertyInfo.Name : propertyName) + "不能为空", + objectVal); + } + + //列名 + string typeName = propertyInfo.GetSqlDbType(); + + //如果没有ColumnAttribute的需要单独再验证,下面只验证有属性的 + if (typeName == null) + { + return (true, null, objectVal); + } + + //验证长度 + return typeName.ValidationVal(val, propertyInfo); + } + + /// + /// 获取属性的指定属性 + /// + /// + /// + /// + public static object GetTypeCustomAttributes(this MemberInfo member, Type type) + { + object[] obj = member.GetCustomAttributes(type, false); + if (obj.Length == 0) return null; + return obj[0]; + } + + /// + /// 获取类的指定属性 + /// + /// + /// + /// + public static object GetTypeCustomAttributes(this Type entity, Type type) + { + object[] obj = entity.GetCustomAttributes(type, false); + if (obj.Length == 0) return null; + return obj[0]; + } + + /// + /// 获取类的多个指定属性的值 + /// + /// 当前类 + /// 指定的类 + /// 指定属性的值 格式 Expression<Func<entityt, object>> exp = x => new { x.字段1, x.字段2 }; + /// 返回的是字段+value + public static Dictionary GetTypeCustomValues(this MemberInfo member, + Expression> expression) + { + var attr = member.GetTypeCustomAttributes(typeof(TEntity)); + if (attr == null) + { + return null; + } + + string[] propertyName = expression.GetExpressionProperty(); + Dictionary propertyKeyValues = new Dictionary(); + + foreach (PropertyInfo property in attr.GetType().GetProperties()) + { + if (propertyName.Contains(property.Name)) + { + propertyKeyValues[property.Name] = (property.GetValue(attr) ?? string.Empty).ToString(); + } + } + + return propertyKeyValues; + } + + /// + /// 获取类的单个指定属性的值(只会返回第一个属性的值) + /// + /// 当前类 + /// 指定的类 + /// 指定属性的值 格式 Expression<Func<entityt, object>> exp = x => new { x.字段1, x.字段2 }; + /// + public static string GetTypeCustomValue(this MemberInfo member, + Expression> expression) + { + var propertyKeyValues = member.GetTypeCustomValues(expression); + if (propertyKeyValues == null || propertyKeyValues.Count == 0) + { + return null; + } + + return propertyKeyValues.First().Value ?? ""; + } + + /// + /// 判断hash的列是否为对应的实体,并且值是否有效 + /// + /// + /// + /// 移除不存在字段 + /// + public static string ValidateDicInEntity(this Type typeinfo, Dictionary dic, + bool removeNotContains, string[] ignoreFields = null) + { + return typeinfo.ValidateDicInEntity(dic, removeNotContains, true, ignoreFields); + } + + public static string ValidateDicInEntity(this Type type, List> dicList, + bool removeNotContains, bool removerKey, string[] ignoreFields = null) + { + PropertyInfo[] propertyInfo = type.GetProperties(); + string reslutMsg = string.Empty; + foreach (Dictionary dic in dicList) + { + reslutMsg = type.ValidateDicInEntity(dic, propertyInfo, removeNotContains, removerKey, ignoreFields); + if (!string.IsNullOrEmpty(reslutMsg)) + return reslutMsg; + } + + return reslutMsg; + } + + public static string ValidateDicInEntity(this Type type, Dictionary dic, bool removeNotContains, + bool removerKey, string[] ignoreFields = null) + { + return type.ValidateDicInEntity(dic, null, removeNotContains, removerKey, ignoreFields); + } + + /// + /// 判断hash的列是否为对应的实体,并且值是否有效 + /// + /// + /// + /// 移除不存在字段 + /// 移除主键 + /// + private static string ValidateDicInEntity(this Type typeinfo, Dictionary dic, + PropertyInfo[] propertyInfo, bool removeNotContains, bool removerKey, string[] ignoreFields = null) + { + if (dic == null || dic.Count == 0) + { + return "参数无效"; + } + + if (propertyInfo == null) + propertyInfo = typeinfo.GetProperties().Where(x => x.PropertyType.Name != "List`1").ToArray(); + + // 不存在的字段直接移除 + dic.Where(x => !propertyInfo.Any(p => p.Name == x.Key)).Select(s => s.Key).ToList().ForEach(f => + { + dic.Remove(f); + }); + string keyName = typeinfo.GetKeyName(); + //移除主键 + if (removerKey) + { + dic.Remove(keyName); + } + + foreach (PropertyInfo property in propertyInfo) + { + //忽略与主键的字段不做验证 + if (property.Name == keyName || (ignoreFields != null && ignoreFields.Contains(property.Name))) + continue; + + //不在编辑中的列,是否也要必填 + if (!dic.ContainsKey(property.Name)) + { + //移除主键默认为新增数据,将不在编辑列中的有默认值的数据设置为默认值 + //如果为true默认为添加功能,添加操作所有不能为空的列也必须要提交 + if (property.GetCustomAttributes(typeof(RequiredAttribute)).Count() > 0 + && property.PropertyType != typeof(int) + && property.PropertyType != typeof(long) + && property.PropertyType != typeof(byte) + && property.PropertyType != typeof(decimal) + ) + { + return property.GetTypeCustomValue(x => x.Name) + "为必须提交项"; + } + + continue; + } + + bool isEdit = property.ContainsCustomAttributes(typeof(EditableAttribute)); + //不是编辑列的直接移除,并且不是主键 + //removerKey=true,不保留主键,直接移除 + //removerKey=false,保留主键,属性与主键不同的直接移除 + // if (!isEdit && (removerKey || (!removerKey && property.Name != keyName))) + if (!isEdit) + { + if (property.GetCustomAttributes(typeof(RequiredAttribute)).Count() > 0) + { + return property.GetTypeCustomValue(x => x.Name) + "没有配置好Model为编辑列"; + } + + dic.Remove(property.Name); + continue; + } + + ////移除忽略的不保存的数据 + //if (property.ContainsCustomAttributes(typeof(JsonIgnoreAttribute))) + //{ + // hash.Remove(property.Name); + // continue; + //} + //验证数据类型,不验证是否为空 + var result = property.ValidationProperty(dic[property.Name], false); + if (!result.Item1) + return result.Item2; + + //将所有空值设置为null + if (dic[property.Name] != null && dic[property.Name].ToString() == string.Empty) + dic[property.Name] = null; + } + + return string.Empty; + } + + + private static object MapToInstance(this Type reslutType, object sourceEntity, PropertyInfo[] sourcePro, + PropertyInfo[] reslutPro, string[] sourceFilterField, string[] reslutFilterField, string mapType = null) + { + mapType = mapType ?? GetMapType(reslutType); + if (sourcePro == null) + { + sourcePro = sourceEntity.GetType().GetProperties(); + } + + if (reslutPro == null) + { + reslutPro = reslutType.GetProperties(); + ; + } + + object newObj = Activator.CreateInstance(reslutType); + + if (mapType == "Dictionary") + { + if (sourceFilterField != null && sourceFilterField.Length > 0) + { + sourcePro = sourcePro.Where(x => sourceFilterField.Contains(x.Name)).ToArray(); + } + + foreach (var property in sourcePro) + { + (newObj as System.Collections.IDictionary).Add(property.Name, property.GetValue(sourceEntity)); + } + + return newObj; + } + + if (reslutFilterField != null && reslutFilterField.Count() > 0) + { + reslutPro.Where(x => reslutFilterField.Contains(x.Name)); + } + + foreach (var property in reslutPro) + { + PropertyInfo info = sourcePro.Where(x => x.Name == property.Name).FirstOrDefault(); + if (!(info != null && info.PropertyType == property.PropertyType)) + continue; + property.SetValue(newObj, info.GetValue(sourceEntity)); + } + + return newObj; + } + + private static string GetMapType(Type type) + { + return typeof(Dictionary<,>) == type ? "Dictionary" : "entity"; + } + + /// + /// 将数据源映射到新的数据中,目前只支持List<TSource>映射到List<TResult>或TSource映射到TResult + /// 目前只支持Dictionary或实体类型 + /// + /// + /// + /// + /// 只映射返回对象的指定字段,若为null则默认为全部字段 + /// 只映射数据源对象的指定字段,若为null则默认为全部字段 + /// + public static TResult MapToObject(this TSource source, + Expression> resultExpression, + Expression> sourceExpression = null + ) where TResult : class + { + if (source == null) + return null; + string[] sourceFilterField = sourceExpression == null + ? typeof(TSource).GetProperties().Select(x => x.Name).ToArray() + : sourceExpression.GetExpressionProperty(); + string[] reslutFilterField = resultExpression?.GetExpressionProperty(); + if (!(source is System.Collections.IList)) + return MapToInstance(typeof(TResult), source, null, null, sourceFilterField, reslutFilterField) as + TResult; + + Type sourceType = null; + Type resultType = null; + System.Collections.IList sourceList = source as System.Collections.IList; + sourceType = sourceList[0].GetType(); + resultType = (typeof(TResult)).GenericTypeArguments[0]; + + System.Collections.IList reslutList = Activator.CreateInstance(typeof(TResult)) as System.Collections.IList; + PropertyInfo[] sourcePro = sourceType.GetProperties(); + PropertyInfo[] resultPro = resultType.GetProperties(); + + string mapType = GetMapType(resultType); + for (int i = 0; i < sourceList.Count; i++) + { + var reslutobj = MapToInstance(resultType, sourceList[i], sourcePro, resultPro, sourceFilterField, + reslutFilterField, mapType); + reslutList.Add(reslutobj); + } + + return reslutList as TResult; + } + + /// + /// 将一个实体的赋到另一个实体上,应用场景: + /// 两个实体,a a1= new a();b b1= new b(); a1.P=b1.P; a1.Name=b1.Name; + /// + /// + /// + /// + /// + /// 指定对需要的字段赋值,格式x=>new {x.Name,x.P},返回的结果只会对Name与P赋值 + public static void MapValueToEntity(this TSource source, TResult result, + Expression> expression = null) where TResult : class + { + if (source == null) + return; + string[] fields = expression?.GetExpressionToArray(); + PropertyInfo[] reslutPro = fields == null + ? result.GetType().GetProperties() + : result.GetType().GetProperties().Where(x => fields.Contains(x.Name)).ToArray(); + PropertyInfo[] sourcePro = source.GetType().GetProperties(); + foreach (var property in reslutPro) + { + PropertyInfo info = sourcePro.Where(x => x.Name == property.Name).FirstOrDefault(); + if (info != null && info.PropertyType == property.PropertyType) + { + property.SetValue(result, info.GetValue(source)); + } + } + } + } + + public class ArrayEntity + { + public string column1 { get; set; } + } + + public enum FieldType + { + VarChar = 0, + NvarChar, + Int, + BigInt, + UniqueIdentifier + } + + public enum EntityToSqlTempName + { + TempInsert = 0 + } +} \ No newline at end of file diff --git a/Infrastructure/Extensions/EnumTypeExtention.cs b/Infrastructure/Extensions/EnumTypeExtention.cs new file mode 100644 index 0000000..c618530 --- /dev/null +++ b/Infrastructure/Extensions/EnumTypeExtention.cs @@ -0,0 +1,65 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.ComponentModel.DataAnnotations; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Infrastructure.Extensions +{ + public static class EnumTypeExtention + { + + public static T GetEnumByName(this string name) + { + foreach (var memberInfo in typeof(T).GetMembers()) + { + foreach (var attr in memberInfo.GetCustomAttributes(true)) + { + var test = attr as DisplayAttribute; + + if (test == null) continue; + + if (test.Name == name) + { + var result = (T)Enum.Parse(typeof(T), memberInfo.Name); + + return result; + } + } + } + + return default(T); + } + + public static string GetEnumName(this T type, Enum enm) where T : Type + { + foreach (var memberInfo in type.GetMembers()) + { + foreach (var attr in memberInfo.GetCustomAttributes(true)) + { + var test = attr as DisplayAttribute; + + if (test == null) continue; + + if (memberInfo.Name == enm.ToString()) + { + return test.Name; + } + } + } + + return null; + } + + public static string GetDescription(this Enum val) + { + var field = val.GetType().GetField(val.ToString()); + var customAttribute = Attribute.GetCustomAttribute(field, typeof(DescriptionAttribute)); + if (customAttribute == null) { return val.ToString(); } + else { return ((DescriptionAttribute)customAttribute).Description; } + } + + } +} diff --git a/Infrastructure/Extensions/ExtensionsJson.cs b/Infrastructure/Extensions/ExtensionsJson.cs new file mode 100644 index 0000000..0d2901b --- /dev/null +++ b/Infrastructure/Extensions/ExtensionsJson.cs @@ -0,0 +1,22 @@ +using Newtonsoft.Json.Converters; +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Infrastructure.Extensions +{ + public static class ExtensionsJson + { + //public static string ToJson(this object obj) + //{ + // IsoDateTimeConverter isoDateTimeConverter = new IsoDateTimeConverter + // { + // DateTimeFormat = "yyyy-MM-dd HH:mm:ss" + // }; + // return JsonConvert.SerializeObject(obj, isoDateTimeConverter); + //} + } +} diff --git a/Infrastructure/Extensions/GenericExtension.cs b/Infrastructure/Extensions/GenericExtension.cs new file mode 100644 index 0000000..8b9f20a --- /dev/null +++ b/Infrastructure/Extensions/GenericExtension.cs @@ -0,0 +1,105 @@ +using System; +using System.Collections.Generic; +using System.Data; +using System.Linq; +using System.Linq.Expressions; +using System.Reflection; + +namespace Infrastructure.Extensions +{ + /// + /// 泛型扩展 + /// + public static class GenericExtension + { + public static bool Equal(this T x, T y) + { + return ((IComparable)(x)).CompareTo(y) == 0; + } + + #region ToDictionary + /// + /// 将实体指定的字段写入字典 + /// + /// + /// + /// + /// + + public static Dictionary ToDictionary(this T t, Expression> expression) where T : class + { + Dictionary dic = new Dictionary(); + string[] fields = expression.GetExpressionToArray(); + PropertyInfo[] properties = expression == null ? t.GetType().GetProperties() : t.GetType().GetProperties().Where(x => fields.Contains(x.Name)).ToArray(); + + foreach (var property in properties) + { + var value = property.GetValue(t, null); + dic.Add(property.Name, value != null ? value.ToString() : ""); + } + return dic; + } + + public static Dictionary ToDictionary(this TInterface t, Dictionary dic = null) where T : class, TInterface + { + if (dic == null) + dic = new Dictionary(); + var properties = typeof(T).GetProperties(); + foreach (var property in properties) + { + var value = property.GetValue(t, null); + if (value == null) continue; + dic.Add(property.Name, value != null ? value.ToString() : ""); + } + return dic; + } + + #endregion + + + public static DataTable ToDataTable(this IEnumerable source, Expression> columns = null, bool contianKey = true) + { + DataTable dtReturn = new DataTable(); + if (source == null) return dtReturn; + + PropertyInfo[] oProps = typeof(T).GetProperties() + .Where(x => x.PropertyType.Name != "List`1").ToArray(); + if (columns != null) + { + string[] columnArray = columns.GetExpressionToArray(); + oProps = oProps.Where(x => columnArray.Contains(x.Name)).ToArray(); + } + //移除自增主键 + PropertyInfo keyType = oProps.GetKeyProperty();// oProps.GetKeyProperty()?.PropertyType; + if (!contianKey && keyType != null && (keyType.PropertyType == typeof(int) || keyType.PropertyType == typeof(long))) + { + oProps = oProps.Where(x => x.Name != keyType.Name).ToArray(); + } + + foreach (var pi in oProps) + { + var colType = pi.PropertyType; + + if ((colType.IsGenericType) && (colType.GetGenericTypeDefinition() == typeof(Nullable<>))) + { + colType = colType.GetGenericArguments()[0]; + } + + dtReturn.Columns.Add(new DataColumn(pi.Name, colType)); + } + foreach (var rec in source) + { + var dr = dtReturn.NewRow(); + foreach (var pi in oProps) + { + dr[pi.Name] = pi.GetValue(rec, null) == null + ? DBNull.Value + : pi.GetValue + (rec, null); + } + dtReturn.Rows.Add(dr); + } + return dtReturn; + } + } +} diff --git a/Infrastructure/Extensions/LambdaExtensions.cs b/Infrastructure/Extensions/LambdaExtensions.cs new file mode 100644 index 0000000..5de78e6 --- /dev/null +++ b/Infrastructure/Extensions/LambdaExtensions.cs @@ -0,0 +1,504 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; +using System.Reflection; + using Infrastructure.Const; + + namespace Infrastructure.Extensions +{ + public static class LambdaExtensions + { + /// + /// 分页查询 + /// + /// + /// + /// + /// + /// + public static IQueryable TakePage(this IQueryable queryable, int page, int size = 15) + { + return queryable.TakeOrderByPage(page, size); + } + /// + /// 分页查询 + /// + /// + /// + /// + /// + /// + /// + public static IQueryable TakeOrderByPage(this IQueryable queryable, int page, int size = 15, Expression>> orderBy = null) + { + if (page <= 0) + { + page = 1; + } + return Queryable.Take(Queryable.Skip(queryable.GetIQueryableOrderBy(orderBy.GetExpressionToDic()), (page - 1) * size), size); + } + + /// + /// 创建lambda表达式:p=>true + /// + /// + /// + public static Expression> True() + { + return p => true; + } + + /// + /// 创建lambda表达式:p=>false + /// + /// + /// + public static Expression> False() + { + + return p => false; + } + + public static ParameterExpression GetExpressionParameter(this Type type) + { + + return Expression.Parameter(type, "p"); + } + /// + /// 创建lambda表达式:p=>p.propertyName + /// + /// + /// + /// + /// + public static Expression> GetExpression(this string propertyName) + { + return propertyName.GetExpression(typeof(T).GetExpressionParameter()); + } + /// + /// 创建委托有返回值的表达式:p=>p.propertyName + /// + /// + /// + /// + /// + public static Func GetFun(this string propertyName) + { + return propertyName.GetExpression(typeof(T).GetExpressionParameter()).Compile(); + } + + /// + /// 创建lambda表达式:p=>false + /// 在已知TKey字段类型时,如动态排序OrderBy(x=>x.ID)会用到此功能,返回的就是x=>x.ID + /// Expression> expression = x => x.CreateDate;指定了类型 + /// + /// + /// + public static Expression> GetExpression(this string propertyName, ParameterExpression parameter) + { + if (typeof(TKey).Name == "Object") + return Expression.Lambda>(Expression.Convert(Expression.Property(parameter, propertyName), typeof(object)), parameter); + return Expression.Lambda>(Expression.Property(parameter, propertyName), parameter); + } + /// + /// 创建lambda表达式:p=>false + /// object不能确认字段类型(datetime,int,string),如动态排序OrderBy(x=>x.ID)会用到此功能,返回的就是x=>x.ID + /// Expression> expression = x => x.CreateDate;任意类型的字段 + /// + /// + /// + public static Expression> GetExpression(this string propertyName) + { + return propertyName.GetExpression(typeof(T).GetExpressionParameter()); + } + + public static Expression> GetExpression(this string propertyName, ParameterExpression parameter) + { + return Expression.Lambda>(Expression.Convert(Expression.Property(parameter, propertyName), typeof(object)), parameter); + } + + + + /// + /// + /// + /// + /// 字段名 + /// 表达式的值 + /// 创建表达式的类型,如:p=>p.propertyName != propertyValue + /// p=>p.propertyName.Contains(propertyValue) + /// + public static Expression> CreateExpression(this string propertyName, object propertyValue, LinqExpressionType expressionType) + { + return propertyName.CreateExpression(propertyValue, null, expressionType); + } + + /// + /// + /// + /// + /// 字段名 + /// 表达式的值 + /// 创建表达式的类型,如:p=>p.propertyName != propertyValue + /// p=>p.propertyName.Contains(propertyValue) + /// + private static Expression> CreateExpression( + this string propertyName, + object propertyValue, + ParameterExpression parameter, + LinqExpressionType expressionType) + { + Type proType = typeof(T).GetProperty(propertyName).PropertyType; + //创建节点变量如p=>的节点p + // parameter ??= Expression.Parameter(typeof(T), "p");//创建参数p + parameter = parameter ?? Expression.Parameter(typeof(T), "p"); + + //创建节点的属性p=>p.name 属性name + MemberExpression memberProperty = Expression.PropertyOrField(parameter, propertyName); + if (expressionType == LinqExpressionType.In) + { + if (!(propertyValue is System.Collections.IList list) || list.Count == 0) throw new Exception("属性值类型不正确"); + + bool isStringValue = true; + List objList = new List(); + + if (proType.ToString() != "System.String") + { + isStringValue = false; + foreach (var value in list) + { + objList.Add(value.ToString().ChangeType(proType)); + } + list = objList; + } + + if (isStringValue) + { + //string 类型的字段,如果值带有'单引号,EF会默认变成''两个单引号 + MethodInfo method = typeof(System.Collections.IList).GetMethod("Contains"); + //创建集合常量并设置为常量的值 + ConstantExpression constantCollection = Expression.Constant(list); + //创建一个表示调用带参数的方法的:new string[]{"1","a"}.Contains("a"); + MethodCallExpression methodCall = Expression.Call(constantCollection, method, memberProperty); + return Expression.Lambda>(methodCall, parameter); + } + //非string字段,按上面方式处理报异常Null TypeMapping in Sql Tree + BinaryExpression body = null; + foreach (var value in list) + { + ConstantExpression constantExpression = Expression.Constant(value); + UnaryExpression unaryExpression = Expression.Convert(memberProperty, constantExpression.Type); + + body = body == null + ? Expression.Equal(unaryExpression, constantExpression) + : Expression.OrElse(body, Expression.Equal(unaryExpression, constantExpression)); + } + return Expression.Lambda>(body, parameter); + } + + // object value = propertyValue; + ConstantExpression constant = proType.ToString() == "System.String" + ? Expression.Constant(propertyValue) : Expression.Constant(propertyValue.ToString().ChangeType(proType)); + + UnaryExpression member = Expression.Convert(memberProperty, constant.Type); + Expression> expression; + switch (expressionType) + { + //p=>p.propertyName == propertyValue + case LinqExpressionType.Equal: + expression = Expression.Lambda>(Expression.Equal(member, constant), parameter); + break; + //p=>p.propertyName != propertyValue + case LinqExpressionType.NotEqual: + expression = Expression.Lambda>(Expression.NotEqual(member, constant), parameter); + break; + // p => p.propertyName > propertyValue + case LinqExpressionType.GreaterThan: + expression = Expression.Lambda>(Expression.GreaterThan(member, constant), parameter); + break; + // p => p.propertyName < propertyValue + case LinqExpressionType.LessThan: + expression = Expression.Lambda>(Expression.LessThan(member, constant), parameter); + break; + // p => p.propertyName >= propertyValue + case LinqExpressionType.ThanOrEqual: + expression = Expression.Lambda>(Expression.GreaterThanOrEqual(member, constant), parameter); + break; + // p => p.propertyName <= propertyValue + case LinqExpressionType.LessThanOrEqual: + expression = Expression.Lambda>(Expression.LessThanOrEqual(member, constant), parameter); + break; + // p => p.propertyName.Contains(propertyValue) + // p => !p.propertyName.Contains(propertyValue) + case LinqExpressionType.Contains: + case LinqExpressionType.NotContains: + MethodInfo method = typeof(string).GetMethod("Contains", new[] { typeof(string) }); + constant = Expression.Constant(propertyValue, typeof(string)); + if (expressionType == LinqExpressionType.Contains) + { + expression = Expression.Lambda>(Expression.Call(member, method, constant), parameter); + } + else + { + expression = Expression.Lambda>(Expression.Not(Expression.Call(member, method, constant)), parameter); + } + break; + default: + // p => p.false + expression = False(); + break; + } + return expression; + } + + /// + /// 表达式转换成KeyValList(主要用于多字段排序,并且多个字段的排序规则不一样) + /// 如有多个字段进行排序,参数格式为 + /// Expression>> orderBy = x => new Dictionary() { + /// { x.ID, true }, + /// { x.DestWarehouseName, true } + /// }; + /// 返回的是new Dictionary(){{}}key为排序字段,bool为升降序 + /// + /// + /// + /// + public static IEnumerable> GetExpressionToPair(this Expression>> expression) + { + + foreach (var exp in ((ListInitExpression)expression.Body).Initializers) + { + yield return new KeyValuePair( + exp.Arguments[0] is MemberExpression ? + (exp.Arguments[0] as MemberExpression).Member.Name.ToString() + : ((exp.Arguments[0] as UnaryExpression).Operand as MemberExpression).Member.Name, + (QueryOrderBy)((exp.Arguments[1] as ConstantExpression).Value)); + } + } + + + /// + /// 表达式转换成KeyValList(主要用于多字段排序,并且多个字段的排序规则不一样) + /// 如有多个字段进行排序,参数格式为 + /// Expression>> orderBy = x => new Dictionary() { + /// { x.ID, QueryOrderBy.Desc }, + /// { x.DestWarehouseName, QueryOrderBy.Asc } + /// }; + /// 返回的是new Dictionary(){{}}key为排序字段,QueryOrderBy为排序方式 + /// + /// + /// + /// + public static Dictionary GetExpressionToDic(this Expression>> expression) + { + return expression.GetExpressionToPair().Reverse().ToList().ToDictionary(x => x.Key, x => x.Value); + } + /// + /// 解析多字段排序 + /// + /// + /// + /// string=排序的字段,bool=true降序/false升序 + /// + public static IQueryable GetIQueryableOrderBy(this IQueryable queryable, Dictionary orderBySelector) + { + string[] orderByKeys = orderBySelector.Select(x => x.Key).ToArray(); + if (orderByKeys == null || orderByKeys.Length == 0) return queryable; + + IOrderedQueryable queryableOrderBy = null; + // string orderByKey = orderByKeys[^1]; + string orderByKey = orderByKeys[orderByKeys.Length-1]; + queryableOrderBy = orderBySelector[orderByKey] == QueryOrderBy.Desc + ? queryableOrderBy = queryable.OrderByDescending(orderByKey.GetExpression()) + : queryable.OrderBy(orderByKey.GetExpression()); + + for (int i = orderByKeys.Length - 2; i >= 0; i--) + { + queryableOrderBy = orderBySelector[orderByKeys[i]] == QueryOrderBy.Desc + ? queryableOrderBy.ThenByDescending(orderByKeys[i].GetExpression()) + : queryableOrderBy.ThenBy(orderByKeys[i].GetExpression()); + } + return queryableOrderBy; + } + + /// + /// 获取对象表达式指定属性的值 + /// 如获取:Out_Scheduling对象的ID或基他字段 + /// + /// + /// 格式 Expression>sch=x=>new {x.v1,x.v2} or x=>x.v1 解析里面的值返回为数组 + /// + public static string[] GetExpressionToArray(this Expression> expression) + { + string[] propertyNames = null; + if (expression.Body is MemberExpression) + { + propertyNames = new string[] { ((MemberExpression)expression.Body).Member.Name }; + } + else + { + propertyNames = expression.GetExpressionProperty().Distinct().ToArray(); + } + return propertyNames; + } + + /// + /// 与下面and生成方式有所不同,如果直接用表达式1.2进行合并产会提示数据源不同的异常,只能使用下面的的and合并 + /// 此种合并是在使用的同一个数据源(变量),生成的sql语句同样有性能问题(本身可以索引扫描的,生成的sql语句的case when变成索引查找) + /// + /// 通过字段动态生成where and /or表达 + /// 如:有多个where条件,当条件成立时where 1=1 and/or 2=2,依次往后拼接 + /// + /// + /// + /// ExpressionParameters + /// 1、Field生成的字段 + /// 2、ExpressionType 表达式类型大于、小于、于大=、小于=、contains + /// 3、Value表达式的值 + /// + /// + public static Expression> And(List listExpress) + { + return listExpress.Compose(Expression.And); + } + /// + /// 同上面and用法相同 + /// + /// + /// + /// + public static Expression> Or(this List listExpress) + { + return listExpress.Compose(Expression.Or); + } + private static Expression> Compose(this List listExpress, Func merge) + { + ParameterExpression parameter = Expression.Parameter(typeof(T), "p"); + Expression> expression = null; + foreach (ExpressionParameters exp in listExpress) + { + if (expression == null) + { + expression = exp.Field.GetExpression(parameter); + } + else + { + expression = expression.Compose(exp.Field.GetExpression(parameter), merge); + } + } + return expression; + } + /// + /// https://blogs.msdn.microsoft.com/meek/2008/05/02/linq-to-entities-combining-predicates/ + /// 表达式合并(合并生产的sql语句有性能问题) + /// 合并两个where条件,如:多个查询条件时,判断条件成立才where + /// + /// + /// + /// + /// + public static Expression> And(this Expression> first, Expression> second) + { + return first.Compose(second, Expression.And); + } + public static Expression> Or(this Expression> first, Expression> second) + { + return first.Compose(second, Expression.Or); + } + public static Expression Compose(this Expression first, Expression second, Func merge) + { + // build parameter map (from parameters of second to parameters of first) + var map = first.Parameters.Select((f, i) => new { f, s = second.Parameters[i] }).ToDictionary(p => p.s, p => p.f); + // replace parameters in the second lambda expression with parameters from the first + var secondBody = ParameterRebinder.ReplaceParameters(map, second.Body); + // apply composition of lambda expression bodies to parameters from the first expression + return Expression.Lambda(merge(first.Body, secondBody), first.Parameters); + } + + public static IQueryable GetQueryableSelect(this IQueryable queryable) + { + Expression> expression = CreateMemberInitExpression(); + return queryable.Select(expression); + } + + /// + /// 动态创建表达式Expression> expression = CreateMemberInitExpression(); + ///结果为Expression> expression1 = x => new User() { Age = x.Age, Species = x.Species }; + ///参照文档https://docs.microsoft.com/zh-cn/dotnet/api/system.linq.expressions.memberinitexpression?redirectedfrom=MSDN&view=netframework-4.8 + /// + /// + /// + /// + public static Expression> CreateMemberInitExpression(Type resultType = null) + { + resultType = resultType ?? typeof(Result); + ParameterExpression left = Expression.Parameter(typeof(Source), "p"); + NewExpression newExpression = Expression.New(resultType); + PropertyInfo[] propertyInfos = resultType.GetProperties(); + List memberBindings = new List(); + foreach (PropertyInfo propertyInfo in propertyInfos) + { + MemberExpression member = Expression.Property(left, propertyInfo.Name); + MemberBinding speciesMemberBinding = Expression.Bind(resultType.GetMember(propertyInfo.Name)[0], member); + memberBindings.Add(speciesMemberBinding); + } + MemberInitExpression memberInitExpression = Expression.MemberInit(newExpression, memberBindings); + Expression> expression = Expression.Lambda>(memberInitExpression, new ParameterExpression[] { left }); + return expression; + } + public static Expression> CreateMemberInitExpression(Type resultType) + { + return CreateMemberInitExpression(resultType); + } + /// + /// 属性判断待完 + /// + /// + /// + public static IEnumerable GetGenericProperties(this Type type) + { + return type.GetProperties().GetGenericProperties(); + } + /// + /// 属性判断待完 + /// + /// + /// + public static IEnumerable GetGenericProperties(this IEnumerable properties) + { + return properties.Where(x => !x.PropertyType.IsGenericType && x.PropertyType.GetInterface("IList") == null || x.PropertyType.GetInterface("IEnumerable", false) == null); + } + } + + public class ExpressionParameters + { + public string Field { get; set; } + public LinqExpressionType ExpressionType { get; set; } + public object Value { get; set; } + // public + } + public class ParameterRebinder : ExpressionVisitor + { + + private readonly Dictionary map; + public ParameterRebinder(Dictionary map) + { + this.map = map ?? new Dictionary(); + } + + public static Expression ReplaceParameters(Dictionary map, Expression exp) + { + return new ParameterRebinder(map).Visit(exp); + } + protected override Expression VisitParameter(ParameterExpression p) + { + ParameterExpression replacement; + if (map.TryGetValue(p, out replacement)) + { + p = replacement; + } + return base.VisitParameter(p); + } + } +} diff --git a/Infrastructure/Extensions/ObjectExtension.cs b/Infrastructure/Extensions/ObjectExtension.cs new file mode 100644 index 0000000..6f55cfe --- /dev/null +++ b/Infrastructure/Extensions/ObjectExtension.cs @@ -0,0 +1,1490 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.ComponentModel; +using System.Data; +using System.IO; +using System.Linq; +using System.Linq.Expressions; +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.Serialization.Formatters.Binary; +using System.Text; +using System.Text.RegularExpressions; +using System.Threading; +using System.Threading.Tasks; +using System.Web; +using System.Xml; +using System.Xml.Linq; + +namespace Infrastructure.Extensions +{ + + public static class ObjectExtension + { + + public static bool DicKeyIsNullOrEmpty(this Dictionary dic, string key) + { + if (dic == null) + return true; + if (!dic.ContainsKey(key)) return true; + object value = dic[key]; + if (value == null || value.ToString() == "") + { + return true; + } + return false; + } + public static Dictionary ReaderToDictionary(this IDataReader Reader) + { + List> rowList = Reader.ReaderToDictionaryList(); + return rowList.Count() > 0 ? rowList[0] : null; + } + /// + /// IDataReader转换成DictionaryList + /// + /// + /// + public static List> ReaderToDictionaryList(this IDataReader Reader) + { + List> rowList = new List>(); + try + { + while (Reader.Read()) + { + Dictionary row = new Dictionary(StringComparer.OrdinalIgnoreCase); + for (var fieldCount = 0; fieldCount < Reader.FieldCount; fieldCount++) + { + row.Add(Reader.GetName(fieldCount), Reader[fieldCount]); + } + rowList.Add(row); + } + } + catch (Exception ex) { throw ex; } + finally + { + Reader.Close(); + Reader.Dispose(); + } + return rowList; + } + + public static T DicToEntity(this Dictionary dic) + { + return new List>() { dic }.DicToList().ToList()[0]; + } + public static List DicToList(this List> dicList) + { + return dicList.DicToIEnumerable().ToList(); + } + public static object DicToList(this List> dicList, Type type) + { + return typeof(ObjectExtension).GetMethod("DicToList") + .MakeGenericMethod(new Type[] { type }) + .Invoke(typeof(ObjectExtension), new object[] { dicList }); + } + + public static IEnumerable DicToIEnumerable(this List> dicList) + { + foreach (Dictionary dic in dicList) + { + T model = Activator.CreateInstance(); + foreach (PropertyInfo property in model.GetType() + .GetProperties(BindingFlags.GetProperty | BindingFlags.Public | BindingFlags.Instance)) + { + if (!dic.TryGetValue(property.Name, out object value)) continue; + property.SetValue(model, value?.ToString().ChangeType(property.PropertyType), null); + } + yield return model; + } + } + /// + /// IDataReader转换成List + /// + /// + /// + /// + public static List ReaderToList(this IDataReader Reader) + { + List objectField = new List(Reader.FieldCount); + for (int i = 0; i < Reader.FieldCount; i++) + { + objectField.Add(Reader.GetName(i).ToLower()); + } + List objectList = new List(); + try + { + while (Reader.Read()) + { + T model = Activator.CreateInstance(); + foreach (PropertyInfo property in model.GetType() + .GetProperties(BindingFlags.GetProperty + | BindingFlags.Public + | BindingFlags.Instance)) + { + if (!objectField.Contains(property.Name.ToLower())) { continue; } + if (StringExtension.IsNullOrEmpty(Reader[property.Name])) { continue; } + property.SetValue(model, Reader[property.Name].ToString().ChangeType(property.PropertyType), null); + } + objectList.Add(model); + } + } + catch (Exception ex) + { + + throw ex; + } + finally + { + Reader.Close(); + Reader.Dispose(); + } + return objectList; + } + + // + // public static object ChangeType(this object convertibleValue, Type type) + // { + // if (null == convertibleValue) return null; + // + // try + // { + // if (type == typeof(Guid) || type == typeof(Guid?)) + // { + // string value = convertibleValue.ToString(); + // if (value == "") return null; + // return Guid.Parse(value); + // } + // + // if (!type.IsGenericType) return Convert.ChangeType(convertibleValue, type); + // if (type.ToString() == "System.Nullable`1[System.Boolean]" || type.ToString() == "System.Boolean") + // { + // if (convertibleValue.ToString() == "0") + // return false; + // return true; + // } + // Type genericTypeDefinition = type.GetGenericTypeDefinition(); + // if (genericTypeDefinition == typeof(Nullable<>)) + // { + // return Convert.ChangeType(convertibleValue, Nullable.GetUnderlyingType(type)); + // } + // } + // catch + // { + // return null; + // } + // return null; + // } + + + /// + /// 将集合转换为数据集。 + /// + /// 转换的元素类型。 + /// 集合。 + /// 是否生成泛型数据集。 + /// 数据集。 + public static DataSet ToDataSet(this IEnumerable list, bool generic = true) + { + return ListToDataSet(list, generic); + } + + /// + /// 将集合转换为数据集。 + /// + /// 集合。 + /// 是否生成泛型数据集。 + /// 数据集。 + public static DataSet ToDataSet(this IEnumerable list, bool generic = true) + { + return ListToDataSet(list, generic); + } + + /// + /// 将集合转换为数据集。 + /// + /// 转换的元素类型。 + /// 集合。 + /// 是否生成泛型数据集。 + /// 数据集。 + public static DataSet ToDataSet(this IEnumerable list, bool generic = true) + { + return ListToDataSet(list, typeof(T), generic); + } + + /// + /// 将实例转换为集合数据集。 + /// + /// 实例类型。 + /// 实例。 + /// 是否生成泛型数据集。 + /// 数据集。 + public static DataSet ToListSet(this T o, bool generic = true) + { + if (o is IEnumerable) + { + return ListToDataSet(o as IEnumerable, generic); + } + else + { + return ListToDataSet(new T[] { o }, generic); + } + } + + /// + /// 将可序列化实例转换为XmlDocument。 + /// + /// 实例类型。 + /// 实例。 + /// XmlDocument。 + public static XmlDocument ToXmlDocument(this T o) + { + XmlDocument xmlDocument = new XmlDocument + { + InnerXml = o.ToListSet().GetXml() + }; + return xmlDocument; + } + + /// + /// 将集合转换为数据集。 + /// + /// 集合。 + /// 转换的元素类型。 + /// 是否生成泛型数据集。 + /// 转换后的数据集。 + private static DataSet ListToDataSet(IEnumerable list, Type t, bool generic) + { + DataSet ds = new DataSet("Data"); + if (t == null) + { + if (list != null) + { + foreach (var i in list) + { + if (i == null) + { + continue; + } + t = i.GetType(); + break; + } + } + if (t == null) + { + return ds; + } + } + ds.Tables.Add(t.Name); + //如果集合中元素为DataSet扩展涉及到的基本类型时,进行特殊转换。 + if (t.IsValueType || t == typeof(string)) + { + ds.Tables[0].TableName = "Info"; + ds.Tables[0].Columns.Add(t.Name); + if (list != null) + { + foreach (var i in list) + { + DataRow addRow = ds.Tables[0].NewRow(); + addRow[t.Name] = i; + ds.Tables[0].Rows.Add(addRow); + } + } + return ds; + } + //处理模型的字段和属性。 + var fields = t.GetFields(); + var properties = t.GetProperties(); + foreach (var j in fields) + { + if (!ds.Tables[0].Columns.Contains(j.Name)) + { + if (generic) + { + ds.Tables[0].Columns.Add(j.Name, j.FieldType); + } + else + { + ds.Tables[0].Columns.Add(j.Name); + } + } + } + foreach (var j in properties) + { + if (!ds.Tables[0].Columns.Contains(j.Name)) + { + if (generic) + { + ds.Tables[0].Columns.Add(j.Name, j.PropertyType); + } + else + { + ds.Tables[0].Columns.Add(j.Name); + } + } + } + if (list == null) + { + return ds; + } + //读取list中元素的值。 + foreach (var i in list) + { + if (i == null) + { + continue; + } + DataRow addRow = ds.Tables[0].NewRow(); + foreach (var j in fields) + { + MemberExpression field = Expression.Field(Expression.Constant(i), j.Name); + LambdaExpression lambda = Expression.Lambda(field, new ParameterExpression[] { }); + Delegate func = lambda.Compile(); + object value = func.DynamicInvoke(); + addRow[j.Name] = value; + } + foreach (var j in properties) + { + MemberExpression property = Expression.Property(Expression.Constant(i), j); + LambdaExpression lambda = Expression.Lambda(property, new ParameterExpression[] { }); + Delegate func = lambda.Compile(); + object value = func.DynamicInvoke(); + addRow[j.Name] = value; + } + ds.Tables[0].Rows.Add(addRow); + } + return ds; + } + + /// + /// 将集合转换为数据集。 + /// + /// 转换的元素类型。 + /// 集合。 + /// 是否生成泛型数据集。 + /// 数据集。 + private static DataSet ListToDataSet(IEnumerable list, bool generic) + { + return ListToDataSet(list, typeof(T), generic); + } + + /// + /// 将集合转换为数据集。 + /// + /// 集合。 + /// 是否转换为字符串形式。 + /// 转换后的数据集。 + private static DataSet ListToDataSet(IEnumerable list, bool generic) + { + return ListToDataSet(list, null, generic); + } + + /// + /// 获取DataSet第一表,第一行,第一列的值。 + /// + /// DataSet数据集。 + /// 值。 + public static object GetData(this DataSet ds) + { + if ( + ds == null + || ds.Tables.Count == 0 + ) + { + return string.Empty; + } + else + { + return ds.Tables[0].GetData(); + } + } + + /// + /// 获取DataTable第一行,第一列的值。 + /// + /// DataTable数据集表。 + /// 值。 + public static object GetData(this DataTable dt) + { + if ( + dt.Columns.Count == 0 + || dt.Rows.Count == 0 + ) + { + return string.Empty; + } + else + { + return dt.Rows[0][0]; + } + } + + /// + /// 获取DataSet第一个匹配columnName的值。 + /// + /// 数据集。 + /// 列名。 + /// 值。 + public static object GetData(this DataSet ds, string columnName) + { + if ( + ds == null + || ds.Tables.Count == 0 + ) + { + return string.Empty; + } + foreach (DataTable dt in ds.Tables) + { + object o = dt.GetData(columnName); + if (!string.IsNullOrEmpty(o.ToString())) + { + return o; + } + } + return string.Empty; + } + + /// + /// 获取DataTable第一个匹配columnName的值。 + /// + /// 数据表。 + /// 列名。 + /// 值。 + public static object GetData(this DataTable dt, string columnName) + { + if (string.IsNullOrEmpty(columnName)) + { + return GetData(dt); + } + if ( + dt.Columns.Count == 0 + || dt.Columns.IndexOf(columnName) == -1 + || dt.Rows.Count == 0 + ) + { + return string.Empty; + } + return dt.Rows[0][columnName]; + } + + /// + /// 将object转换为string类型信息。 + /// + /// object。 + /// 默认值。 + /// string。 + public static string ToString(this object o, string t) + { + string info = string.Empty; + if (o == null) + { + info = t; + } + else + { + info = o.ToString(); + } + return info; + } + + /// + /// 将DateTime?转换为string类型信息。 + /// + /// DateTime?。 + /// 标准或自定义日期和时间格式的字符串。 + /// 默认值。 + /// string。 + public static string ToString(this DateTime? o, string format, string t) + { + string info = string.Empty; + if (o == null) + { + info = t; + } + else + { + info = o.Value.ToString(format); + } + return info; + } + + /// + /// 将TimeSpan?转换为string类型信息。 + /// + /// TimeSpan?。 + /// 标准或自定义时间格式的字符串。 + /// 默认值。 + /// string。 + public static string ToString(this TimeSpan? o, string format, string t) + { + string info = string.Empty; + if (o == null) + { + info = t; + } + else + { + info = o.Value.ToString(format); + } + return info; + } + + + + /// + /// 将object转换为byte类型信息。 + /// + /// object。 + /// 默认值。 + /// byte。 + public static byte ToByte(this object o, byte t = default(byte)) + { + if (!byte.TryParse(o.ToString(string.Empty), out byte info)) + { + info = t; + } + return info; + } + + public static byte[] ToBytes(this object obj) + { + if (obj == null) + return null; + var bf = new BinaryFormatter(); + using (var ms = new MemoryStream()) + { + bf.Serialize(ms, obj); + return ms.ToArray(); + } + } + + public static object ToObject(this byte[] source) + { + using (var memStream = new MemoryStream()) + { + var bf = new BinaryFormatter(); + memStream.Write(source, 0, source.Length); + memStream.Seek(0, SeekOrigin.Begin); + var obj = bf.Deserialize(memStream); + return obj; + } + } + + /// + /// 将object转换为char类型信息。 + /// + /// object。 + /// 默认值。 + /// char。 + public static char ToChar(this object o, char t = default(char)) + { + if (!char.TryParse(o.ToString(string.Empty), out char info)) + { + info = t; + } + return info; + } + + /// + /// 将object转换为int类型信息。 + /// + /// object。 + /// 默认值。 + /// int。 + public static int ToInt(this object o, int t = default(int)) + { + if (!int.TryParse(o.ToString(string.Empty), out int info)) + { + info = t; + } + return info; + } + + /// + /// 将object转换为double类型信息。 + /// + /// object。 + /// 默认值。 + /// double。 + public static double ToDouble(this object o, double t = default(double)) + { + double info; + if (!double.TryParse(o.ToString(string.Empty), out info)) + { + info = t; + } + return info; + } + + /// + /// 将object转换为decimal类型信息。 + /// + /// object。 + /// 默认值。 + /// decimal。 + public static decimal ToDecimal(this object o, decimal t = default(decimal)) + { + decimal info; + if (!decimal.TryParse(o.ToString(string.Empty), out info)) + { + info = t; + } + return info; + } + + /// + /// 将object转换为float类型信息。 + /// + /// object。 + /// 默认值。 + /// float。 + public static float ToFloat(this object o, float t = default(float)) + { + if (!float.TryParse(o.ToString(string.Empty), out float info)) + { + info = t; + } + return info; + } + + /// + /// 将object转换为long类型信息。 + /// + /// object。 + /// 默认值。 + /// long。 + public static long ToLong(this object o, long t = default(long)) + { + long info; + if (!long.TryParse(o.ToString(string.Empty), out info)) + { + info = t; + } + return info; + } + + /// + /// 将object转换为bool类型信息。 + /// + /// object。 + /// 默认值。 + /// bool。 + public static bool ToBool(this object o, bool t = default(bool)) + { + bool info; + if (!bool.TryParse(o.ToString(string.Empty), out info)) + { + info = t; + } + return info; + } + + /// + /// 将object转换为sbyte类型信息。 + /// + /// object。 + /// 默认值。 + /// sbyte。 + public static sbyte ToSbyte(this object o, sbyte t = default(sbyte)) + { + sbyte info; + if (!sbyte.TryParse(o.ToString(string.Empty), out info)) + { + info = t; + } + return info; + } + + /// + /// 将object转换为short类型信息。 + /// + /// object。 + /// 默认值。 + /// short。 + public static short ToShort(this object o, short t = default(short)) + { + short info; + if (!short.TryParse(o.ToString(string.Empty), out info)) + { + info = t; + } + return info; + } + + /// + /// 将object转换为ushort类型信息。 + /// + /// object。 + /// 默认值。 + /// ushort。 + public static ushort ToUShort(this object o, ushort t = default(ushort)) + { + ushort info; + if (!ushort.TryParse(o.ToString(string.Empty), out info)) + { + info = t; + } + return info; + } + + /// + /// 将object转换为ulong类型信息。 + /// + /// object。 + /// 默认值。 + /// ulong。 + public static ulong ToULong(this object o, ulong t = default(ulong)) + { + ulong info; + if (!ulong.TryParse(o.ToString(string.Empty), out info)) + { + info = t; + } + return info; + } + + /// + /// 将object转换为Enum[T]类型信息。 + /// + /// object。 + /// 默认值。 + /// Enum[T]。 + public static T ToEnum(this object o, T t = default(T)) + where T : struct + { + if (!System.Enum.TryParse(o.ToString(string.Empty), out T info)) + { + info = t; + } + return info; + } + + /// + /// 将object转换为DateTime类型信息。 + /// + /// object。 + /// 默认值。 + /// DateTime。 + public static DateTime ToDateTime(this object o, DateTime t = default(DateTime)) + { + if (t == default(DateTime)) + { + t = new DateTime(1753, 1, 1); + } + DateTime info; + if (!DateTime.TryParse(o.ToString(string.Empty), out info)) + { + info = t; + } + return info; + } + + /// + /// 将object转换为TimeSpan类型信息。 + /// + /// object。 + /// 默认值。 + /// TimeSpan。 + public static TimeSpan ToTimeSpan(this object o, TimeSpan t = default(TimeSpan)) + { + if (t == default(TimeSpan)) + { + t = new TimeSpan(0, 0, 0); + } + TimeSpan info; + if (!TimeSpan.TryParse(o.ToString(string.Empty), out info)) + { + info = t; + } + return info; + } + + /// + /// 将object转换为Guid类型信息。 + /// + /// object。 + /// 默认值。 + /// Guid。 + public static Guid ToGuid(this object o, Guid t = default(Guid)) + { + Guid info; + if (!Guid.TryParse(o.ToString(string.Empty), out info)) + { + info = t; + } + return info; + } + + private static Regex BoolRegex = new Regex("(?(true|false))", RegexOptions.IgnoreCase | RegexOptions.Singleline); + + /// + /// 从object中获取bool类型信息。 + /// + /// object。 + /// bool。 + public static bool GetBool(this string value) + { + bool.TryParse(value, out bool result); + return result; + } + + private static Regex IntRegex = new Regex("(?-?\\d+)", RegexOptions.IgnoreCase | RegexOptions.Singleline); + + + + private static Regex DecimalRegex = new Regex("(?-?\\d+(\\.\\d+)?)", RegexOptions.IgnoreCase | RegexOptions.Singleline); + + /// + /// 从object中获取decimal类型信息。 + /// + /// object。 + /// decimal。 + public static decimal? GetDecimal(this object o) + { + decimal info; + if (!decimal.TryParse(DecimalRegex.Match(o.ToString(string.Empty)).Groups["info"].Value, out info)) + { + return null; + } + return info; + } + + + + /// + /// 从object中获取正数信息。 + /// + /// object。 + /// decimal。 + public static decimal? GetPositiveNumber(this object o) + { + decimal info; + if (!decimal.TryParse(DecimalRegex.Match(o.ToString(string.Empty)).Groups["info"].Value, out info)) + { + return null; + } + return Math.Abs(info); + } + + private static Regex DateTimeRegex = new Regex("(?(((\\d+)[/年-](0?[13578]|1[02])[/月-](3[01]|[12]\\d|0?\\d)[日]?)|((\\d+)[/年-](0?[469]|11)[/月-](30|[12]\\d|0?\\d)[日]?)|((\\d+)[/年-]0?2[/月-](2[0-8]|1\\d|0?\\d)[日]?))(\\s((2[0-3]|[0-1]\\d)):[0-5]\\d:[0-5]\\d)?)", RegexOptions.IgnoreCase | RegexOptions.Singleline); + + /// + /// 从object中获取DateTime?类型信息。 + /// + /// object。 + /// DateTime?。 + public static DateTime? GetDateTime1(this object o) + { + DateTime info; + if (!DateTime.TryParse(DateTimeRegex.Match(o.ToString(string.Empty)).Groups["info"].Value.Replace("年", "-").Replace("月", "-").Replace("/", "-").Replace("日", ""), out info)) + { + return null; + } + return info; + } + + private static Regex TimeSpanRegex = new Regex("(?-?(\\d+\\.(([0-1]\\d)|(2[0-3])):[0-5]\\d:[0-5]\\d)|((([0-1]\\d)|(2[0-3])):[0-5]\\d:[0-5]\\d)|(\\d+))", RegexOptions.IgnoreCase | RegexOptions.Singleline); + + /// + /// 从object中获取TimeSpan?类型信息。 + /// + /// object。 + /// TimeSpan?。 + public static TimeSpan? GetTimeSpan(this object o) + { + TimeSpan info; + if (!TimeSpan.TryParse(TimeSpanRegex.Match(o.ToString(string.Empty)).Groups["info"].Value, out info)) + { + return null; + } + return info; + } + + private static Regex GuidRegex = new Regex("(?\\{{0,1}([0-9a-fA-F]){8}-([0-9a-fA-F]){4}-([0-9a-fA-F]){4}-([0-9a-fA-F]){4}-([0-9a-fA-F]){12}\\}{0,1})", RegexOptions.IgnoreCase | RegexOptions.Singleline); + + /// + /// 从object中获取Guid?类型信息。 + /// + /// object。 + /// Guid?。 + public static Guid? GetGuid(this object o) + { + Guid info; + if (!Guid.TryParse(GuidRegex.Match(o.ToString(string.Empty)).Groups["info"].Value, out info)) + { + return null; + } + return info; + } + + /// + /// 将object转换为SqlServer中的DateTime?类型信息。 + /// + /// object。 + /// 默认值。 + /// DateTime?。 + public static DateTime? GetSqlDateTime(this object o, DateTime t = default(DateTime)) + { + DateTime info; + if (!DateTime.TryParse(o.ToString(string.Empty), out info)) + { + info = t; + } + if (info < new DateTime(1753, 1, 1) || info > new DateTime(9999, 12, 31)) + { + return null; + } + return info; + } + + /// + /// 读取XElement节点的文本内容。 + /// + /// XElement节点。 + /// 默认值。 + /// 文本内容。 + public static string Value(this XElement xElement, string t = default(string)) + { + if (xElement == null) + { + return t; + } + else + { + return xElement.Value; + } + } + + /// + /// 获取与指定键相关的值。 + /// + /// 键类型。 + /// 值类型。 + /// 表示键/值对象的泛型集合。 + /// 键。 + /// 默认值。 + /// 值。 + public static TValue GetValue(this IDictionary dictionary, TKey key, TValue t = default(TValue)) + { + TValue value = default(TValue); + if (dictionary == null || key == null) + { + return t; + } + if (!dictionary.TryGetValue(key, out value)) + { + value = t; + } + return value; + } + + /// + /// 获取与指定键相关或者第一个的值。 + /// + /// 键类型。 + /// 值类型。 + /// 表示键/值对象的泛型集合。 + /// 键。 + /// 默认值。 + /// 值。 + public static TValue GetFirstOrDefaultValue(this IDictionary dictionary, TKey key, TValue t = default(TValue)) + { + TValue value = default(TValue); + if (dictionary == null || key == null) + { + return t; + } + if (!dictionary.TryGetValue(key, out value)) + { + if (dictionary.Count() == 0) + { + value = t; + } + else + { + value = dictionary.FirstOrDefault().Value; + } + } + return value; + } + + /// + /// 获取具有指定 System.Xml.Linq.XName 的第一个(按文档顺序)子元素。 + /// + /// XContainer。 + /// 要匹配的 System.Xml.Linq.XName。 + /// 是否返回同名默认值。 + /// 与指定 System.Xml.Linq.XName 匹配的 System.Xml.Linq.XElement,或者为 null。 + public static XElement Element(this XContainer xContainer, XName xName, bool t) + { + XElement info; + if (xContainer == null) + { + info = null; + } + else + { + info = xContainer.Element(xName); + } + if (t && info == null) + { + info = new XElement(xName); + } + return info; + } + + /// + /// 按文档顺序返回此元素或文档的子元素集合。 + /// + /// XContainer。 + /// 是否返回非空默认值。 + /// System.Xml.Linq.XElement 的按文档顺序包含此System.Xml.Linq.XContainer 的子元素,或者非空默认值。 + public static IEnumerable Elements(this XContainer xContainer, bool t) + { + IEnumerable info; + if (xContainer == null) + { + info = null; + } + else + { + info = xContainer.Elements(); + } + if (t && info == null) + { + info = new List(); + } + return info; + } + + /// + /// 按文档顺序返回此元素或文档的经过筛选的子元素集合。集合中只包括具有匹配 System.Xml.Linq.XName 的元素。 + /// + /// XContainer。 + /// 要匹配的 System.Xml.Linq.XName。 + /// 是否返回非空默认值。 + /// System.Xml.Linq.XElement 的按文档顺序包含具有匹配System.Xml.Linq.XName 的 System.Xml.Linq.XContainer 的子级,或者非空默认值。 + public static IEnumerable Elements(this XContainer xContainer, XName xName, bool t) + { + IEnumerable info; + if (xContainer == null) + { + info = null; + } + else + { + info = xContainer.Elements(xName); + } + if (t && info == null) + { + info = new List(); + } + return info; + } + + /// + /// 删除html标签。 + /// + /// 输入的字符串。 + /// 没有html标签的字符串。 + public static string RemoveHTMLTags(this string html) + { + return Regex.Replace(Regex.Replace(Regex.Replace((html ?? string.Empty).Replace(" ", " ").Replace("\r\n", " ").Replace("\n", " ").Replace("\r", " ").Replace("\t", " "), "<\\/?[^>]+>", "\r\n"), "(\r\n)+", "\r\n"), "(\\s)+", " ").Trim(); + } + + /// + /// 字符串转换为文件名。 + /// + /// 字符串。 + /// 文件名。 + public static string ToFileName(this string s) + { + return Regex.Replace(s.ToString(string.Empty), @"[\\/:*?<>|]", "_").Replace("\t", " ").Replace("\r\n", " ").Replace("\"", " "); + } + + + /// + /// 获取默认非空字符串。 + /// + /// 首选默认非空字符串。 + /// 依次非空字符串可选项。 + /// 默认非空字符串。若无可选项则返回string.Empty。 + public static string DefaultStringIfEmpty(this string s, params string[] args) + { + if (string.IsNullOrEmpty(s)) + { + return string.Empty; + } + foreach (string i in args) + { + if (!string.IsNullOrEmpty(i) && !string.IsNullOrEmpty(i.Trim())) + { + return i; + } + } + + return (s ?? string.Empty); + } + + /// + /// 对 URL 字符串进行编码。 + /// + /// 要编码的文本。 + /// 匹配要编码的文本。 + /// 指定编码方案的 System.Text.Encoding 对象。 + /// 一个已编码的字符串。 + public static string ToUrlEncodeString(this string s, Regex regex = default(Regex), Encoding encoding = null) + { + if (encoding == null) + { + encoding = Encoding.UTF8; + } + if (regex == null) + { + return HttpUtility.UrlEncode(s, encoding); + } + List l = new List(); + foreach (char i in s) + { + string t = i.ToString(); + l.Add(regex.IsMatch(t) ? HttpUtility.UrlEncode(t, encoding) : t); + } + return string.Join(string.Empty, l); + } + + /// + /// 对 URL 字符串进行编码。 + /// + /// 要编码的文本。 + /// 匹配要编码的文本。 + /// 指定编码方案的 System.Text.Encoding 对象。 + /// 一个已编码的字符串。 + public static string ToUrlEncodeString(this string s, string regex, Encoding encoding = null) + { + return ToUrlEncodeString(s, new Regex(regex), encoding); + } + + /// + /// 将日期转换为UNIX时间戳字符串 + /// + /// + /// + public static string ToUnixTimeStamp(this DateTime date) + { + DateTime startTime = TimeZoneInfo.ConvertTimeToUtc(new DateTime(1970, 1, 1)); + string timeStamp = date.Subtract(startTime).Ticks.ToString(); + return timeStamp.Substring(0, timeStamp.Length - 7); + } + + private static readonly Regex MobileRegex = new Regex("^1[3|4|5|7|8][0-9]\\d{4,8}$"); + private static readonly Regex EmailRegex = new Regex("^([a-zA-Z0-9_-])+@([a-zA-Z0-9_-])+((\\.[a-zA-Z0-9_-]{2,3}){1,2})$"); + + /// + /// 判断当前字符串是否是移动电话号码 + /// + /// + /// + public static bool IsMobile(this string mobile) + { + return MobileRegex.IsMatch(mobile); + } + + /// + /// 判断当前字符串是否为邮箱 + /// + /// + /// + public static bool IsEmail(this string email) + { + return EmailRegex.IsMatch(email); + } + + + /// + /// 将 DateTimeOffset 转换成 DateTime + /// + /// + /// + public static DateTime ConvertToDateTime(this DateTimeOffset dateTime) + { + if (dateTime.Offset.Equals(TimeSpan.Zero)) + return dateTime.UtcDateTime; + else if (dateTime.Offset.Equals(TimeZoneInfo.Local.GetUtcOffset(dateTime.DateTime))) + return DateTime.SpecifyKind(dateTime.DateTime, DateTimeKind.Local); + else + return dateTime.DateTime; + } + + /// + /// 将 DateTime 转换成 DateTimeOffset + /// + /// + /// + public static DateTimeOffset ConvertToDateTimeOffset(this DateTime dateTime) + { + return DateTime.SpecifyKind(dateTime, DateTimeKind.Local); + } + + /// + /// 判断是否是富基元类型 + /// + /// 类型 + /// + internal static bool IsRichPrimitive(this Type type) + { + // 处理元组类型 + if (type.IsValueTuple()) return false; + + // 处理数组类型,基元数组类型也可以是基元类型 + if (type.IsArray) return type.GetElementType().IsRichPrimitive(); + + // 基元类型或值类型或字符串类型 + if (type.IsPrimitive || type.IsValueType || type == typeof(string)) return true; + + if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>)) return type.GenericTypeArguments[0].IsRichPrimitive(); + + return false; + } + + /// + /// 合并两个字典 + /// + /// + /// 字典 + /// 新字典 + /// + internal static Dictionary AddOrUpdate(this Dictionary dic, Dictionary newDic) + { + foreach (var key in newDic.Keys) + { + if (dic.ContainsKey(key)) + dic[key] = newDic[key]; + else + dic.Add(key, newDic[key]); + } + + return dic; + } + + /// + /// 判断是否是元组类型 + /// + /// 类型 + /// + internal static bool IsValueTuple(this Type type) + { + return type.ToString().StartsWith(typeof(ValueTuple).FullName); + } + + /// + /// 判断方法是否是异步 + /// + /// 方法 + /// + internal static bool IsAsync(this MethodInfo method) + { + return method.ReturnType.IsAsync(); + } + + /// + /// 判断类型是否是异步类型 + /// + /// + /// + internal static bool IsAsync(this Type type) + { + return type.ToString().StartsWith(typeof(Task).FullName); + } + + /// + /// 判断类型是否实现某个泛型 + /// + /// 类型 + /// 泛型类型 + /// bool + internal static bool HasImplementedRawGeneric(this Type type, Type generic) + { + // 检查接口类型 + var isTheRawGenericType = type.GetInterfaces().Any(IsTheRawGenericType); + if (isTheRawGenericType) return true; + + // 检查类型 + while (type != null && type != typeof(object)) + { + isTheRawGenericType = IsTheRawGenericType(type); + if (isTheRawGenericType) return true; + type = type.BaseType; + } + + return false; + + // 判断逻辑 + bool IsTheRawGenericType(Type type) => generic == (type.IsGenericType ? type.GetGenericTypeDefinition() : type); + } + + /// + /// 判断是否是匿名类型 + /// + /// 对象 + /// + internal static bool IsAnonymous(this object obj) + { + var type = obj.GetType(); + + return Attribute.IsDefined(type, typeof(CompilerGeneratedAttribute), false) + && type.IsGenericType && type.Name.Contains("AnonymousType") + && (type.Name.StartsWith("<>") || type.Name.StartsWith("VB$")) + && type.Attributes.HasFlag(TypeAttributes.NotPublic); + } + + /// + /// 获取所有祖先类型 + /// + /// + /// + internal static IEnumerable GetAncestorTypes(this Type type) + { + var ancestorTypes = new List(); + while (type != null && type != typeof(object)) + { + if (IsNoObjectBaseType(type)) + { + var baseType = type.BaseType; + ancestorTypes.Add(baseType); + type = baseType; + } + else break; + } + + return ancestorTypes; + + static bool IsNoObjectBaseType(Type type) => type.BaseType != typeof(object); + } + + /// + /// 获取方法真实返回类型 + /// + /// + /// + internal static Type GetRealReturnType(this MethodInfo method) + { + // 判断是否是异步方法 + var isAsyncMethod = method.IsAsync(); + + // 获取类型返回值并处理 Task 和 Task 类型返回值 + var returnType = method.ReturnType; + return isAsyncMethod ? (returnType.GenericTypeArguments.FirstOrDefault() ?? typeof(void)) : returnType; + } + + /// + /// 首字母大写 + /// + /// + /// + internal static string ToTitleCase(this string str) + { + return Thread.CurrentThread.CurrentCulture.TextInfo.ToTitleCase(str); + } + + /// + /// 将一个对象转换为指定类型 + /// + /// + /// + /// + internal static T ChangeType(this object obj) + { + return (T)ChangeType(obj, typeof(T)); + } + + /// + /// 将一个对象转换为指定类型 + /// + /// 待转换的对象 + /// 目标类型 + /// 转换后的对象 + internal static object ChangeType(this object obj, Type type) + { + if (type == null) return obj; + if (obj == null) return type.IsValueType ? Activator.CreateInstance(type) : null; + + var underlyingType = Nullable.GetUnderlyingType(type); + if (type.IsAssignableFrom(obj.GetType())) return obj; + else if ((underlyingType ?? type).IsEnum) + { + if (underlyingType != null && string.IsNullOrWhiteSpace(obj.ToString())) return null; + else return Enum.Parse(underlyingType ?? type, obj.ToString()); + } + // 处理DateTime -> DateTimeOffset 类型 + else if (obj.GetType().Equals(typeof(DateTime)) && (underlyingType ?? type).Equals(typeof(DateTimeOffset))) + { + return ((DateTime)obj).ConvertToDateTimeOffset(); + } + // 处理 DateTimeOffset -> DateTime 类型 + else if (obj.GetType().Equals(typeof(DateTimeOffset)) && (underlyingType ?? type).Equals(typeof(DateTime))) + { + return ((DateTimeOffset)obj).ConvertToDateTime(); + } + else if (typeof(IConvertible).IsAssignableFrom(underlyingType ?? type)) + { + try + { + return Convert.ChangeType(obj, underlyingType ?? type, null); + } + catch + { + return underlyingType == null ? Activator.CreateInstance(type) : null; + } + } + else + { + var converter = TypeDescriptor.GetConverter(type); + if (converter.CanConvertFrom(obj.GetType())) return converter.ConvertFrom(obj); + + var constructor = type.GetConstructor(Type.EmptyTypes); + if (constructor != null) + { + var o = constructor.Invoke(null); + var propertys = type.GetProperties(); + var oldType = obj.GetType(); + + foreach (var property in propertys) + { + var p = oldType.GetProperty(property.Name); + if (property.CanWrite && p != null && p.CanRead) + { + property.SetValue(o, ChangeType(p.GetValue(obj, null), property.PropertyType), null); + } + } + return o; + } + } + return obj; + } + + } + + + + /// + /// 标记。 + /// + public enum Flag + { + /// + /// 默认。 + /// + Default, + + /// + /// 真。 + /// + True, + + /// + /// 假。 + /// + False + } +} diff --git a/Infrastructure/Extensions/SecurityEncDecryptExtension.cs b/Infrastructure/Extensions/SecurityEncDecryptExtension.cs new file mode 100644 index 0000000..80efa8c --- /dev/null +++ b/Infrastructure/Extensions/SecurityEncDecryptExtension.cs @@ -0,0 +1,86 @@ +using System; +using System.IO; +using System.Security.Cryptography; +using System.Text; + +namespace Infrastructure.Extensions +{ + public static class SecurityEncDecryptExtensions + { + private static byte[] Keys = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F }; + /// + /// DES加密字符串 + /// + /// 待加密的字符串 + /// 加密密钥,要求为16位 + /// 加密成功返回加密后的字符串,失败返回源串 + + public static string EncryptDES(this string encryptString, string encryptKey) + { + try + { + byte[] rgbKey = Encoding.UTF8.GetBytes(encryptKey.Substring(0, 16)); + byte[] rgbIV = Keys; + byte[] inputByteArray = Encoding.UTF8.GetBytes(encryptString); + + using (var DCSP = DES.Create()) + { + using (MemoryStream mStream = new MemoryStream()) + { + using (CryptoStream cStream = new CryptoStream(mStream, DCSP.CreateEncryptor(rgbKey, rgbIV), CryptoStreamMode.Write)) + { + cStream.Write(inputByteArray, 0, inputByteArray.Length); + cStream.FlushFinalBlock(); + return Convert.ToBase64String(mStream.ToArray()).Replace('+', '_').Replace('/', '~'); + } + } + } + } + catch (Exception ex) + { + throw new Exception("密码加密异常" + ex.Message); + } + } + + /// + /// DES解密字符串 + /// + /// 待解密的字符串 + /// 解密密钥,要求为16位,和加密密钥相同 + /// 解密成功返回解密后的字符串,失败返源串 + + public static string DecryptDES(this string decryptString, string decryptKey) + { + byte[] rgbKey = Encoding.UTF8.GetBytes(decryptKey.Substring(0, 16)); + byte[] rgbIV = Keys; + byte[] inputByteArray = Convert.FromBase64String(decryptString.Replace('_', '+').Replace('~', '/')); + using (var DCSP = DES.Create()) + { + using (MemoryStream mStream = new MemoryStream()) + { + using (CryptoStream cStream = new CryptoStream(mStream, DCSP.CreateDecryptor(rgbKey, rgbIV), CryptoStreamMode.Write)) + { + byte[] inputByteArrays = new byte[inputByteArray.Length]; + cStream.Write(inputByteArray, 0, inputByteArray.Length); + cStream.FlushFinalBlock(); + return Encoding.UTF8.GetString(mStream.ToArray()); + } + } + } + } + + public static bool TryDecryptDES(this string decryptString, string decryptKey, out string result) + { + result = ""; + try + { + result = DecryptDES(decryptString, decryptKey); + return true; + } + catch + { + return false; + } + } + } +} \ No newline at end of file diff --git a/Infrastructure/Extensions/ServerExtension.cs b/Infrastructure/Extensions/ServerExtension.cs new file mode 100644 index 0000000..844fcf8 --- /dev/null +++ b/Infrastructure/Extensions/ServerExtension.cs @@ -0,0 +1,28 @@ +using Infrastructure.Extensions.AutofacManager; +using Infrastructure.Provider; + +namespace Infrastructure.Extensions +{ + public static class ServerExtension + { + /// + /// 返回的路径后面不带/,拼接时需要自己加上/ + /// + /// + /// + public static string MapPath(this string path) + { + return MapPath(path, false); + } + /// + /// + /// + /// + /// 获取wwwroot路径 + /// + public static string MapPath(this string path,bool rootPath) + { + return AutofacContainerModule.GetService().MapPath(path,rootPath); + } + } +} diff --git a/Infrastructure/Extensions/StringExtension.cs b/Infrastructure/Extensions/StringExtension.cs new file mode 100644 index 0000000..6663277 --- /dev/null +++ b/Infrastructure/Extensions/StringExtension.cs @@ -0,0 +1,867 @@ +using System; +using System.Runtime.InteropServices; +using System.Security.Cryptography; +using System.Text; +using System.Text.RegularExpressions; +using Infrastructure.Const; +using Microsoft.International.Converters.PinYinConverter; + +namespace Infrastructure.Extensions +{ + public static class StringExtension + { + public static bool _windows = RuntimeInformation.IsOSPlatform(OSPlatform.Windows); + /// + /// 自动调整windows/linux下面文件目录斜杠的处理 + /// + /// + /// + public static string ReplacePath(this string path) + { + if (string.IsNullOrEmpty(path)) + return ""; + if (_windows) + return path.Replace("/", "\\"); + return path.Replace("\\", "/"); + + } + + /// + /// 把一个字符串转成驼峰规则的字符串 + /// + /// + /// + public static string ToCamelCase(this string str) + { + if(!string.IsNullOrEmpty(str) && str.Length > 1) + { + return Char.ToLowerInvariant(str[0]) + str.Substring(1); + } + return str; + } + + private static DateTime dateStart = new DateTime(1970, 1, 1, 8, 0, 0); + + private static long longTime = 621355968000000000; + + private static int samllTime = 10000000; + /// + /// 获取时间戳 + /// + /// + /// + public static long GetTimeStamp(this DateTime dateTime) + { + return (dateTime.ToUniversalTime().Ticks - longTime) / samllTime; + } + /// + /// 时间戳转换成日期 + /// + /// + /// + public static DateTime GetTimeSpmpToDate(this object timeStamp) + { + if (timeStamp == null) return dateStart; + DateTime dateTime = new DateTime(longTime + Convert.ToInt64(timeStamp) * samllTime, DateTimeKind.Utc).ToLocalTime(); + return dateTime; + } + + + public static bool IsUrl(this string str) + { + if (string.IsNullOrEmpty(str)) + return false; + string Url = @"(http://)?([\w-]+\.)+[\w-]+(/[\w- ./?%&=]*)?"; + return Regex.IsMatch(str, Url); + + } + /// + /// 判断是不是正确的手机号码 + /// + /// + /// + public static bool IsPhoneNo(this string input) + { + if (string.IsNullOrEmpty(input)) + return false; + if (input.Length != 11) + return false; + + if (new Regex(@"^1[3578][01379]\d{8}$").IsMatch(input) + || new Regex(@"^1[34578][01256]\d{8}").IsMatch(input) + || new Regex(@"^(1[012345678]\d{8}|1[345678][0123456789]\d{8})$").IsMatch(input) + ) + return true; + return false; + } + + public static string GetDBCondition(this string stringType) + { + string reslut = ""; + switch (stringType?.ToLower()) + { + case HtmlElementType.droplist: + case HtmlElementType.selectlist: + case HtmlElementType.textarea: + case HtmlElementType.checkbox: + reslut = HtmlElementType.Contains; + break; + case HtmlElementType.thanorequal: + reslut = HtmlElementType.ThanOrEqual; + break; + case HtmlElementType.lessorequal: + reslut = HtmlElementType.LessOrequal; + break; + case HtmlElementType.gt: + reslut = HtmlElementType.GT; + break; + case HtmlElementType.lt: + reslut = HtmlElementType.lt; + break; + case HtmlElementType.like: + reslut = HtmlElementType.like; + break; + default: + reslut = HtmlElementType.Equal; + break; + } + return reslut; + } + + public static LinqExpressionType GetLinqCondition(this string stringType) + { + LinqExpressionType linqExpression; + switch (stringType) + { + case HtmlElementType.Contains: + linqExpression = LinqExpressionType.In; + break; + case HtmlElementType.ThanOrEqual: + linqExpression = LinqExpressionType.ThanOrEqual; + break; + case HtmlElementType.LessOrequal: + linqExpression = LinqExpressionType.LessThanOrEqual; + break; + case HtmlElementType.GT: + linqExpression = LinqExpressionType.GreaterThan; + break; + case HtmlElementType.lt: + linqExpression = LinqExpressionType.LessThan; + break; + case HtmlElementType.like: + linqExpression = LinqExpressionType.Contains; + break; + default: + linqExpression = LinqExpressionType.Equal; + break; + } + return linqExpression; + } + + public static bool GetGuid(this string guid, out Guid outId) + { + Guid emptyId = Guid.Empty; + return Guid.TryParse(guid, out outId); + } + + public static bool IsGuid(this string guid) + { + Guid newId; + return GetGuid(guid, out newId); + } + + public static bool IsInt(this object obj) + { + if (obj == null) + return false; + bool reslut = Int32.TryParse(obj.ToString(), out int _number); + return reslut; + + } + public static bool IsDate(this object str) + { + return IsDate(str, out _); + } + public static bool IsDate(this object str, out DateTime dateTime) + { + dateTime = DateTime.Now; + if (str == null || str.ToString() == "") + { + return false; + } + return DateTime.TryParse(str.ToString(), out dateTime); + } + /// + /// 根据传入格式判断是否为小数 + /// + /// + /// 18,5 + /// + public static bool IsNumber(this string str, string formatString) + { + if (string.IsNullOrEmpty(str)) + return false; + int precision = 32; + int scale = 5; + try + { + if (string.IsNullOrEmpty(formatString)) + { + precision = 10; + scale = 2; + } + else + { + string[] numbers = formatString.Split(','); + precision = Convert.ToInt32(numbers[0]); + scale = numbers.Length == 0 ? 2 : Convert.ToInt32(numbers[1]); + } + } + catch { }; + return IsNumber(str, precision, scale); + } + /**/ + /// + /// 判断一个字符串是否为合法数字(指定整数位数和小数位数) + /// + /// 字符串 + /// 整数位数 + /// 小数位数 + /// + public static bool IsNumber(this string str, int precision, int scale) + { + if ((precision == 0) && (scale == 0)) + { + return false; + } + string pattern = @"(^\d{1," + precision + "}"; + if (scale > 0) + { + pattern += @"\.\d{0," + scale + "}$)|" + pattern; + } + pattern += "$)"; + return Regex.IsMatch(str, pattern); + } + + + public static bool IsNullOrEmpty(this object str) + { + if (str == null) + return true; + return str.ToString() == ""; + } + + + public static int GetInt(this object obj) + { + if (obj == null) + return 0; + int.TryParse(obj.ToString(), out int _number); + return _number; + + } + + /// + /// 获取 object 中的枚举值 + /// + /// + /// + /// + public static long GetLong(this object obj) + { + if (obj == null) + return 0; + + try + { + return Convert.ToInt64(Convert.ToDouble(obj)); + } + catch + { + return 0; + } + } + + /// + /// 获取 object 中的 float + /// + /// + /// + + public static float GetFloat(this object obj) + { + if (System.DBNull.Value.Equals(obj) || null == obj) + return 0; + + try + { + return float.Parse(obj.ToString()); + } + catch + { + return 0; + } + } + + public static double GetDouble(this object obj) + { + if (System.DBNull.Value.Equals(obj) || null == obj) + return 0; + + try + { + return Convert.ToDouble(obj); + } + catch + { + return 0; + } + } + /// + /// 获取 object 中的 decimal + /// + /// + /// + /// + public static decimal GetDecimal(this object obj) + { + if (System.DBNull.Value.Equals(obj) || null == obj) + return 0; + + try + { + return Convert.ToDecimal(obj); + } + catch + { + return 0; + } + } + + /// + /// 获取 object 中的 decimal + /// + /// + /// + /// + public static dynamic GetDynamic(this object obj) + { + if (System.DBNull.Value.Equals(obj) || null == obj) + return null; + + try + { + string str = obj.ToString(); + if (IsNumber(str, 25, 15)) return Convert.ToDecimal(obj); + else return str; + } + catch + { + return string.Empty; + } + } + + public static DateTime? GetDateTime(this object obj) + { + if (System.DBNull.Value.Equals(obj) || null == obj) + return null; + bool result = DateTime.TryParse(obj.ToString(), out DateTime dateTime); + if (!result) + return null; + return dateTime; + } + + + + public static object ParseTo(this string str, string type) + { + switch (type) + { + case "System.Boolean": + return ToBoolean(str); + case "System.SByte": + return ToSByte(str); + case "System.Byte": + return ToByte(str); + case "System.UInt16": + return ToUInt16(str); + case "System.Int16": + return ToInt16(str); + case "System.uInt32": + return ToUInt32(str); + case "System.Int32": + return ToInt32(str); + case "System.UInt64": + return ToUInt64(str); + case "System.Int64": + return ToInt64(str); + case "System.Single": + return ToSingle(str); + case "System.Double": + return ToDouble(str); + case "System.Decimal": + return ToDecimal(str); + case "System.DateTime": + return ToDateTime(str); + case "System.Guid": + return ToGuid(str); + } + throw new NotSupportedException(string.Format("The string of \"{0}\" can not be parsed to {1}", str, type)); + } + + public static sbyte? ToSByte(this string value) + { + sbyte value2; + if (sbyte.TryParse(value, out value2)) + { + return value2; + } + return null; + } + + public static byte? ToByte(this string value) + { + byte value2; + if (byte.TryParse(value, out value2)) + { + return value2; + } + return null; + } + + public static ushort? ToUInt16(this string value) + { + ushort value2; + if (ushort.TryParse(value, out value2)) + { + return value2; + } + return null; + } + + public static short? ToInt16(this string value) + { + if (short.TryParse(value, out short value2)) + { + return value2; + } + return null; + } + + public static uint? ToUInt32(this string value) + { + uint value2; + if (uint.TryParse(value, out value2)) + { + return value2; + } + return null; + } + + public static ulong? ToUInt64(this string value) + { + ulong value2; + if (ulong.TryParse(value, out value2)) + { + return value2; + } + return null; + } + + public static long? ToInt64(this string value) + { + long value2; + if (long.TryParse(value, out value2)) + { + return value2; + } + return null; + } + + public static float? ToSingle(this string value) + { + float value2; + if (float.TryParse(value, out value2)) + { + return value2; + } + return null; + } + + public static double? ToDouble(this string value) + { + double value2; + if (double.TryParse(value, out value2)) + { + return value2; + } + return null; + } + + public static decimal? ToDecimal(this string value) + { + decimal value2; + if (decimal.TryParse(value, out value2)) + { + return value2; + } + return null; + } + + public static bool? ToBoolean(this string value) + { + bool value2; + if (bool.TryParse(value, out value2)) + { + return value2; + } + return null; + } + + + + public static Guid? ToGuid(this string str) + { + Guid value; + if (Guid.TryParse(str, out value)) + { + return value; + } + return null; + } + + public static DateTime? ToDateTime(this string value) + { + DateTime value2; + if (DateTime.TryParse(value, out value2)) + { + return value2; + } + return null; + } + + public static int? ToInt32(this string input) + { + if (string.IsNullOrEmpty(input)) + { + return null; + } + int value; + if (int.TryParse(input, out value)) + { + return value; + } + return null; + } + + /// + /// 替换空格字符 + /// + /// + /// 替换为该字符 + /// 替换后的字符串 + public static string ReplaceWhitespace(this string input, string replacement = "") + { + return string.IsNullOrEmpty(input) ? null : Regex.Replace(input, "\\s", replacement, RegexOptions.Compiled); + } + + private static char[] randomConstant ={ + '0','1','2','3','4','5','6','7','8','9', + 'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z', + 'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z' + }; + /// + /// 生成指定长度的随机数 + /// + /// + /// + public static string GenerateRandomNumber(this int length) + { + System.Text.StringBuilder newRandom = new System.Text.StringBuilder(62); + Random rd = new Random(); + for (int i = 0; i < length; i++) + { + newRandom.Append(randomConstant[rd.Next(62)]); + } + return newRandom.ToString(); + } + public static string MaxSubstring(this string origin, int maxLength) + { + return origin.Length >= maxLength ? origin.Substring(0, maxLength) : origin; + } + + public static string ToMd5(this string origin) + { + if (string.IsNullOrWhiteSpace(origin)) + { + return string.Empty; + } + + var md5Algorithm = MD5.Create(); + var utf8Bytes = Encoding.UTF8.GetBytes(origin); + var md5Hash = md5Algorithm.ComputeHash(utf8Bytes); + var hexString = new StringBuilder(); + foreach (var hexByte in md5Hash) + { + hexString.Append(hexByte.ToString("x2")); + } + return hexString.ToString(); + } + + + // + // 摘要: + // 把汉字转换成拼音(全拼) + // + // 参数: + // text: + // 汉字字符串 + // + // 返回结果: + // 转换后的拼音(全拼)字符串 + public static string ConvertPinYin(string text) + { + string text2 = string.Empty; + for (int i = 0; i < text.Length; i++) + { + char ch = text[i]; + try + { + ChineseChar chineseChar = new ChineseChar(ch); + if (chineseChar.Pinyins.Count > 0 && chineseChar.Pinyins[0].Length > 0) + { + string text3 = chineseChar.Pinyins[0].ToString(); + text2 += text3.Substring(0, text3.Length - 1); + } + } + catch (Exception) + { + text2 += ch; + } + } + + return text2; + } + + + // + // 摘要: + // 获取汉字的拼音简码,即首字母缩写,范例:中国,返回zg + // + // 参数: + // chineseText: + // 汉字文本,范例: 中国 + public static string PinYin(string chineseText) + { + if (string.IsNullOrWhiteSpace(chineseText)) + { + return string.Empty; + } + + StringBuilder stringBuilder = new StringBuilder(); + foreach (char text in chineseText) + { + stringBuilder.AppendFormat("{0}", ResolvePinYin(text)); + } + + return stringBuilder.ToString().ToLower(); + } + + // + // 摘要: + // 解析单个汉字的拼音简码 + // + // 参数: + // text: + // 单个汉字 + private static string ResolvePinYin(char text) + { + byte[] bytes = Encoding.Default.GetBytes(text.ToString()); + if (bytes[0] <= 127) + { + return text.ToString(); + } + + ushort unicode = (ushort)(bytes[0] * 256 + bytes[1]); + string text2 = ResolvePinYinByCode(unicode); + if (!string.IsNullOrWhiteSpace(text2)) + { + return text2; + } + + return ResolvePinYinByFile(text.ToString()); + } + + + // + // 摘要: + // 使用字符编码方式获取拼音简码 + private static string ResolvePinYinByCode(ushort unicode) + { + if (unicode >= 45217 && unicode <= 45252) + { + return "A"; + } + + if (unicode >= 45253 && unicode <= 45760 && unicode != 45464) + { + return "B"; + } + + if (unicode >= 45761 && unicode <= 46317) + { + return "C"; + } + + if (unicode >= 46318 && unicode <= 46825) + { + return "D"; + } + + if (unicode >= 46826 && unicode <= 47009) + { + return "E"; + } + + if (unicode >= 47010 && unicode <= 47296) + { + return "F"; + } + + if (unicode >= 47297 && unicode <= 47613) + { + return "G"; + } + + if (unicode >= 47614 && unicode <= 48118) + { + return "H"; + } + + if (unicode >= 48119 && unicode <= 49061) + { + return "J"; + } + + if (unicode >= 49062 && unicode <= 49323) + { + return "K"; + } + + if (unicode >= 49324 && unicode <= 49895) + { + return "L"; + } + + if (unicode >= 49896 && unicode <= 50370) + { + return "M"; + } + + if (unicode >= 50371 && unicode <= 50613) + { + return "N"; + } + + if (unicode >= 50614 && unicode <= 50621) + { + return "O"; + } + + if (unicode >= 50622 && unicode <= 50905) + { + return "P"; + } + + if (unicode >= 50906 && unicode <= 51386) + { + return "Q"; + } + + if (unicode >= 51387 && unicode <= 51445) + { + return "R"; + } + + if (unicode >= 51446 && unicode <= 52217) + { + return "S"; + } + + if (unicode >= 52218 && unicode <= 52697) + { + return "T"; + } + + if (unicode >= 52698 && unicode <= 52979) + { + return "W"; + } + + if (unicode >= 52980 && unicode <= 53640) + { + return "X"; + } + + if (unicode >= 53689 && unicode <= 54480) + { + return "Y"; + } + + if (unicode >= 54481 && unicode <= 55289) + { + return "Z"; + } + + return string.Empty; + } + + + // + // 摘要: + // 从拼音简码文件获取 + // + // 参数: + // text: + // 单个汉字 + public static string ResolvePinYinByFile(string text) + { + int num = "丂k丄s丅x丆m丏m丒c丗s丢d丠q両l丣y并b丩j丮j丯j丱g丳c丵z丷b丼j乀f乁y讈l乆j乑y乕h乗c乚h乛w乢g乣j乤h乧d乨s乪n乫g乬g乭d乮m乯o乲c乴x乵y乶p乷s乸n乹q乺s乻e乼z乽z乿z亀g亁q乱l亃l亄y亅j亇m亊s亐y亖s亗s亘g亜y亝q亚y亣d亪y亯x亰j亱y亴y亶d亷l亸d亹m亼j亽j亾w仈b仌b仏f仐j仒e仚x仛t仜h仢b仦c仧c仩c仭r仮f仯c仱q仴w仸y仹f仺c仼w仾d伀z伂p伃y伄d伅d伆w伇y伈x汲j伌a伒j伓p伔d伖t伜c伝y伡c伣q伨x伩x伬c伭x伮n伱n伳x伵x伷z伹q伻b伾p伿z佀s佁a佄h佅m伫z布b佉q佊b佋z佌c佒y占z佖b佡x佢q佦s佨b徊h佫h佭x佮g佱f佲m佷h佸h佺q佽c侁s侂t侅g来l侇y侊g侌y侎m侐x侒a侓l侕e仑l侘c侙c侚x侜z侞r侟c価s侢d侤t侫n侭j侰j侱c侲z侳z侴h侣l局j侸s侹t侺s侻t侾x俀t俣y系j俆x俇g俈k俉w俋y俌f俍l俒h俓j俔q俕s俖p俙x俛f侠x俢x俤d俥c俧z俫l俬s俰h俲x俴j俵b俶c俷f俹y俻b俼y俽x俿h伥c倁z倂b倃j倅c俩l倇w倈l仓c倊z个g倎t倐s们m倓t倕c幸x倗p倛q倝g倞j倠s倢j仿f値z倧z伦l倯s倰l倱h倲d倳z倴b倵w倶j倷n倸c倹j倻y倽s倿n偀y偁c偂q偄r偅z偆c伟w偊y偋b偍t偐y偑f偒t偓w偔e偖c偗s偘k偙d偛c偝b偞x偠y偡z偢c偣a偦x偧z偨c偩f偪b偫z偭m偮j偯y偰x偱x偲c偳d侧c侦z偸t偹b咱z偼j伪w傁s傂z傃s傄x傆y傇r傉n傊y傋j傌m傎d傏t傐h杰j傒x傓s傔q傕j伧c傗c伞s备b傚x傛r傜y傝t傞s傟y傠f傡b家j傤z傦g傪c傫l佣y傮z偬z傰p傱s传c伛y债z伤s傹j傼h傽z倾q傿y僀d僁x偻l僃b僄b仅j僆l戮l僈m佥q僊x僋t僌y働d僎z僐s侨q僒j僓t僔z仆p僗l僘c僙g僛q僜d僝c僞w僟j僠b僡h僢c僣t僤d侥j偾f僩x僪y僫e僯l僰b雇g僲x僴x僶m僷y僸j价j僺q僼f僽z僾a僿s仪y儁j侬n儃c亿y当d侩k俭j儊c儌j儍s儎z儏c傧b儑a儓t俦c侪c儖l儗y尽j儙q儚m儛w儜n儝q儞n偿c儠l儢l儣k儤b儥y儦b儧z儨z儩s优y儫h儬q儭c儮l儯t儰w儱l储c儳c儴x儵s儶h俪l罗l儹z傩n傥t俨y儽l儾n兂z凶x兊d兑d兎t兏c児e儿e兖y兘s兟s兡b兤h兦w内n两l兪y兯h兲t兾j兿y冃m冄r円y冇m册c冋j冎g冏j冐m胄z冓g冔x冘y冚k冝y冞s冟s冡m冣z冦k冧l冨f冩x幂m冭t冮g冴y冸p冹f冺m冾q冿j凁s凂m凃t凅g净j凊q冻d凎g凐y凒a凓l凔c凕m凖z凗c凘s凙d凚j凛l凞x凟d凢f凣f凥j処c凧y凨f凩k凪n凬f凮f凯k凲g凴p凷k凾h刄r刅c刉j刋q刌c刏j刐d刓w刔j刕l刜f刞q刟d刡m刢l刣z别b刦j刧j删s刬c刯g刱c刲k刴d刵e刼j刾c刭j剅d剆l则z剈y剉c克k刹c剒c剓l剕f剗c剘q剙c剚z刚g剥b剟d剠q剢d剣j剤j剦y剨h剫d剬z剭w剐g剰s剱j剳d剀k创c剶c铲c剸t剹l剺l剻p剼s剾k劀g划h劄z劅z劆l剧j刘l刽g劋j刿g剑j劎j劏t剂j劒j劔j劕z劖c劗z劘m劙l劚z劜y劤j劥k劦x劧z劮y劯z劰m労l劵j劶k劷y劸w効x劺m匡k劼j劽l勀k劲j勂g勄m勅c勆l勈y勊k勌j勍q勎l勏b勑c勓k勔m动d勖x务w勚y勋x勜w胜s劳l勠l勡p势s积j勥j剿c勧q勨x勪q勫f勬j勭t勮j勯d劢m勲x勳x勴l励l勶c勷x劝q匀y勼j勽b匁m匂x匃g匄g匇y匉p匊j匋t匌g匎e匑g匒d匓j匔g匘n匛j匜y匞j匟k匢h匤q匥f匧q匨z匩k匫h匬y匦g汇h匰d匮k匲l匳l匴s匵d匶j匷j匸x匼k匽y区o卂x卄n卆z卋s卌x卍w卐w协x単d卙j卛s卝g卥x卨x卪j卬a卭q卲s卶c恤x却q卼w卽j卾e厀x厁s厃w厇z厈h厊y厎d厏z厐p厑y厒q厓y厔z厖m厗t厍s厛t厜z厞f厡y厤l厧d厪j厫a厬g厌y厯l厰c厱q厉l厳y厣y厵y厷g厸l厹r厺q厼k厽l厾d叀z参c叄c叅c叆a叇d収s叏g叐b叒r叓l叕z叚j叜s叝j叞w叡r丛c叧g叴q叺c叾d叿h吀m吂m吅x吇z寸c吔y吘o吙h吚y吜c吢q吤j吥b吪e吰h吴w呐n吷x吺d吽h吿g呁j吕l呄g呅w呇q呉w呌j呍h尺c呏s呑t呚h呝e呞s呟j呠p呡w呣m呥r呧d呪z呫t呬x呭y呮q呯p呰z呴g呹y呺x呾d呿q咁x咃t咅p咇b咈f咉y咊h咍h咑d咓w咗z咘b咜t咞x咟h咠q咡e咢e咥d咮z咰s咲x咵k咶g咷t咹e咺x呙g咾l哃t哅x哊y哋d哖n哘x哛p哠h员y哢l哣p哤m哫z哬h哯x哰l哱b哴l哵b哶m哸s哹f哻h哾c唀y唂g唃g呗b含h唈y唊j唋t唌d唍w唎l唒q唓c唕z唖y唗d唘q唙d唚q唜m唝g唞d唟g唡l唥l唦s唨z唩w唫j唭q唲e唴q唵a唶j念n唹y唺t唻l唽x啀a啂g啇d啈h啋c啌q啍z啎w问w啑s啒g啓q啔q啗d啘w啚b啝h哑y启q啠z啢l衔x啨q啩g啫z啯g啰l啱y啲d啳q啴c啹j啺t啽a啿d喅y喆z喌z喍c喎w喐h喒z喓y喕m喖h喗y唤h喛h喞j喠z喡w喢s喣x喤h喥d喦y喨l喩y丧s吃c乔q喭y单c喯p喰c哟y喴w営y喸p喺x喼j喿z嗀h嗁t嗂y嗃h呛q啬s嗈y嗊g嗋x吗m嗏c嗐h嗕r嗗w嗙p呜w嗛q嗞z嗠l嗢w嗧j唢s嗭z嗮s嗰g嗱n嗴q哔b嗸a嗹l嗺z嗻z嗼m嗿t嘂j嘃c嘄j嘅k叹t嘇s嘊a嘋x喽l嘐x嘑h嘒h呕o嘕x啧z尝c嘙p嘚d唛m嘝h嘠g嘡t嘢y嘥s嘦j嘨x哗h嘪m嘫r唠l啸x叽j嘳k哓x嘷h呒m嘺q嘼c嘽t嘾d噀x恶e噂z噃f噄c噅h噆z噇c噈c噉d噊j噋t噏x噐q噑h噒l嘘s噕h噖y噚x噛n噝s噞y噟y哒d噡z噣z哝n哕y噧x噭j噮y嗳a噰y哙k噳y喷p噷x吨d噺h噽p噾y噿z咛n嚁d嚂l嚃t嚄h吓h嚈y嚉d嚊x嚋z哜j嚑x嚔t噜l嚖h嚗b嚘y啮n嚚y嚛h嚜m嚝h嚞z嚟l嚠l嚡x嚢n嚤m咽y呖l嚧l咙l嚩p嚪d嚫c嚬p嚭p向x嚰m嚱x嚲d喾k严y嚵c嘤y嚸d嚹l嚺t嚻x嚽c嚾h嚿h啭z嗫n嚣a囃z冁c囆c囇l呓y囋z苏s囍x囎z囏j囐y嘱z囒l囓n囕r囖l囘h囙y囜n団t囥k囦y囧j囨p囩y囱c囬h囮e囯g囲w図t囶g囷q囸r囼t圀g圁y圂h圅h囵l国g圌c围w圎y圏q圐k圑p园y圆y圔y圕t图t圗t团t圙l圚h圛y圝l圞l圠y圡t圢t圤p圥l圦k圧y圫y圱q圲q圴z圵d圶q圷x圸s圼n圽m圿j坁z坃x坄y坅q坆m坈r坉t坋b坒b坓j坔d坕j坖j坘d坙j坢b坣t坥q坧z坬g坮t垧s坱y坲f坴l坵q坸g坹x坽l坾z坿f垁z垇a垈d垉p垊m垍j垎h垏l垐c垑c垔y垕h垖d垗z垘f垙g垚y垜d垝g垞c垟y垥x垨s垪b垬h垯d垰k垱d垳h垵a垶x垷x垹b垺p垻b垼y垽y垾h垿x埀c埁c埄b埅d埆q埇y埈j埉x埊d埌l埍j埐q埑z埓l埖h埗b埛j埜y埞d垭y埢q埣s埥q埦w埧j埨l埩z埪k埫c埬d埮t埰c埲b埳x埵d执z埻z埼q埾j埿n堁k堃k堄n坚j堈g堉y垩e堌g堎l堏f堐y堒k堓a堔s堖n堗t堘c堚h堛b堜l埚g堟z堢b堣y堥m堦j堧r堨a堩g堫z堬y堭h堮e尧y报b堲j堳m场c堶t堷y堸f堹z堺j堻j堼f堽g堾c碱j塀p塁l塂j塃h塅d塆w塇x塉j块k茔y塎y垲k塐s埘s塓m塕w塖c涂t塙q塚z塛l塜p塝b塟z塠d塡t坞w塣z埙x塦z塧a塨g塩y塪x塭w塮x塯l塰h塱l塲c塳p塴b尘c塶l塷l塸o堑q塺m塻m塼z塽s塿l墂b墄q墆z墇z墈k垫d墋c墌z墍x墎g墏q墐j墒s墔c墕y墖t増z墘q墛w坠z墝q墠s墡s墢f墣p墤k墥t墦f墧q墪d墫c墬d墭s堕d墯d墰t墱d墲w墴h墵t墶d墷y墸z墹j墺a墙q垦k墿y壀p壂d壃j壄y壆x坛t壈l壉j壊h壋d壌r壍q壏x壐x壒a压y壖r壗j垒l圹k垆l壛y壜t壝w坏h垄l壠l壡r坜l壣l壥c壦x壧y壨l坝b壪w壭s壮z壱y売m壴z壵z壷h壸k壶h壻x壼k寿s壾m壿d夀s夁y夃g夅j夆f夈z変b夊s夋q夌l夎c夐x夑x夒n夓x夗y夘w夛d夝q夞y够g夡q梦m夣m夦c夨c夬g夰g夲b夳t夵y夶b夻h夽y夹g夿b奀e奃d奅p奆j奊x奌d奍q奂h奒k奓z奙b奛h奜f奝d奞x奟b奡a奣w奤h奦w奥a奨j奁l夺d奫y奬j奭s奋f奯h奰b奱l奲d奵d奷q奺j奻n奼c奾x奿f妀j妅h妉d妋f妎h妏w妐z妑p妔k妕z妘y妚f妜y妆z妟y妠n妡x妦f妧w妬d妰z妱z你n妴y妵t妶x妷z妸e妺m妼b妽s妿e姀h姁x姂f姄m姅b姇f姈l姉z姌r姗s姎y姏g姕z姖j姙r姛d姞j姟g姠x姡h姢j姤g奸j姧j姩n侄z姫z姭x姮h姯g姰j姱h姲y姳m姴l姵p姶y姷y姸y姺x姼s姽g姾q娀s娂h娋s娍c娎x娏m娐f娒m娔k娕c娖c娗t娙x娚n娱y娝b娞n娡z娢h娤z娦p娧t娨m娪w娫y娬w娭a娮y娯y娰s娳l娵j娷s娸q娹x娺z娻d娽l娾a娿e娄l婃c婄p婅j婇c婈l婋x婌s婍q婎h婏f婐w婑w婒t婓f婔f婖t婗n婘j婙j婛j婜q婝d婞x婟h婠w婡l婣y婤z婥c妇f婨l婩a婫h婬y娅y婮j婯l婰d婱x婳h婸d婹y婻n婼c婽j婾t媀y媁w媂d媃r媄m媅d媆r媇q媈h媉w媊q媋c媌m媍f媎j媏d媐y媓h媔m媕a媖y媗x媘j媙w媜z媞t媟x媠d媢m媣r媤s媥p媦w娲w媨c媩h媫j媬b媭x媮t妫g媰c媱y媴y媶r媷r媹l媺m媻p媪a妈m媿k嫀q嫃z嫄y嫅j嫆r嫇m嫈y嫊s嫋n嫍t嫎p嫏l嫐n嫑b嫓p嫕y妪y嫙x嫚m嫛y嫝k嫞y嫟n嫢g嫤j嫥z嫧z嫨h嫪l嫬z嫭h嫮h嫯a嫰n嫲m嫳p嫴g妩w嫶q嫷t嫸z嫹m嫺x娴x嫼m嫽l嫾l嬀g嬁d嬂z嬄y嬅h嬆x嬇k娆r嬊y婵c娇j嬍m嬎f嬏f嬐x嬑y嬒h嬔f嬕s嬘s嫱q嬚l嬛h袅n嬞d嬠c嫒a嬢n嬣n嬷m嬥t嬦c嬧j嬨c嬩y嫔p嬫r嬬r嬭n嬮y嬯t婴y嬱q嬳y嬵m嬶k婶s嬹x嬺n嬻d嬼l嬽y嬾l嬿y孁l孂j娘n孄l孅x孆y孇s孈x孉h孊m孋l娈l孍y孎z孏l孖m孞x孠s孡t孧y孙s孭m孮c孯q孲y孴n孷l学x孹b孻n孼n孾y孪l宂r宆q宊t宍r宎y宐y宑j宒z宔z宖h実s宧y宨t宩s宬c宭q宫g宯x宱z宲b宷c宺h宻m宼k寀c寁z寃y寈q寉h寊z寋j寍n寎b寏h寑q寔s寕n寖j寗n寘z寙y寚b寛k寜n寠j寝q寣h实s宁n审s寪w写x宽k寭h寯j寱y寲y寳b寴q宠c宝b寷f寽l対d尀p専z尃f将j专z寻x尌s对d导d尐j尒e尓e尗s尙s尛m尞l尟x尠x尡h尣w尦l尨m尩w尪w尫w尭y尮d尯k尰z尲g尳g尵t尶g尴g屃x届j屇t屌d屍s屒z屓x屔n屖x屘m屚l屛p屉t扉f屟x屡l层c屧x屦j屩j屪l屫j属s屭x屰n屲w屳x屴l屵a屶h屷h屸h屻r屼w屽h屾s岀c岃y岄y岆y岇a岉w岊j岋e岏w岒q岓q岕j岝z岞z岠j冈g岤x岥p岦l迢t岨j岮t岯p岰a岲k岴q岶p岹t岺l岻d岼p峀x峂t峃x峅b峆h峇b峈l峉e峌d峍l峎e峏e峐g峑q峓y峔m峕s峖a峗w峘h峚m峛l峜f峝t峞w峟y峠q峢l峧j峩e峫x峬b峯f峱n峲l峳y岘x峵r岛d峷s峸c峹t峺g峼g峡x峿w崀l崁k崄x崅q崈c崉t崊l崋h崌j崃l崏m崐k崑k崒z崓g崕y岗g崘l崚l崜d崝z崟y岽d崡h峥z崣w崥p崨j崪z崫j崬d崯y崰z崱z崲h嵛y崵y崶f崷q崸y崹t崺y崻z崼s崿e嵀z嵁k嵂l嵃y嵄m嵅h嵆j嵈h嵉t嵍w嵎y嵏z岚l嵑k岩y嵓y嵔w嵕z嵖c嵗s嵙k嵚q嵜q嵞t嵟c嵠x嵡w嵢c嵣d嵤r嵥j嵦k嵧l嵨w嵪k嵭b嵮d嵰q嵱y嵲n嵳c嵵s嵶r嵷s嵸z嵹j嵺l嵻k嵼y嵾c嵿d嶀t嵝l嶃z崭z嶅a嶆c岖q嶉w嶊z嶋d嶌d嶍x嶎y嶏p嶐l嶑x嶒c嶓b嶔q嶕j嶖y崂l嶘z嶚l嶛l嶜q嶞t嶟z峤j嶡j嶢y嶣j嶤y嶥j嶦z峄y嶨x嶩n嶫y嶬y嶭n嶮x嶯j嶰x嶱k嶲g嶳d嶴a嶵z嶶w嵘r嶹d岭l嶻j屿y岳y嶾y嶿r巀j巁l巂x巃l巄l巇x巈j巉c巊y岿k巌y巎n巐c漓l峦l巓d巅d巕n巗y巘y巙k巚y巜k巟h巠j巣c巤l巪g巬p巭b巯q巵z巶z巸y卺j巺x巻j巼p巿f帀z帄d帇n帊p帋z帍h帎d帒d帓m帗f帞m帟y帠y帡p帢q帣j帤r帅s帨s帩q帪z师s帬q帯d帰g帲p帐z帴j帵w带d帹s帺q帾z帿h帧z幁z帏w幆y幇b幈p幉d幊g幋p幍t幎m幏j幐t幑h幒z幓s幖b帼g帻z幙m幚b幜j幝c帜z幠h币b幤b幦m幧q幨c蒙m帮b帱c幭m幮c幰x幱l幵j幷b干g几j庁t仄z広g庈q庉d庌y庍b庎j庒z庘y庛c庝t庡y庢z庣t庤z庨x庩t庪g库k庬m庮y庯b庰b庱c庲l庴j庻s庼q庽y庿m廀s厕c厢x廃f厩j廅e廆h廇l厦s廋s廌z廍b廎q廏j廔l廕y廗d廘l廙y厨c廜t厮s廞x庙m厂a庑w废f广a廤k廥k廦b廧q廪l廫l庐l廭j廮y廯x廰t痈y廲l厅t廵x廸d廹p廻h廼n廽h弆j弇y弉z弌y弍e弎s弑s吊d弖h弙w弚t弜j弝b弡j弢t弣f弤d弨c弫z弬y弮j弰s弲x弪j弴d张z弶j强j弸p弻b弽s弾d弿j彁g彂f彃b彄k彅j彇x弹d彉g彋h弥m彍g弯w彏j彑j彔l彚h彛y彜y彞y彟h彠h彣w彦y彧y彨c雕d彮y彯p彲c彴z彵t彶j彸z彺w彽d彾l佛f徃w徆x徍w徎c徏z径j従c徔c徖c徝z从c徟z徕l徢x徣j徤j徥s徧b复f旁p徯x徰z徱p徲t徳d徶b彻c徺j徻h徾m徿l忀x忁b忂q忈r忊d忋g忎r忓h忔y忕s忚x忛f応y忞m忟m忢w忣j忥x忦j忨w忩c忬y忯q忰c忲t忳d忴q忶h忷x忹w忺x忼k怇j怈y怉b怋m怌p怐j怑b怗t怘h怚c怞y怟d怢t怣y怤f怬x怮y怰x怲b怳h怴x怶b怷s怸x怹t怺y怽m恀s恄x恅l恒h恇k恈m恉z恊x恌y恏h恑g恓x恔x恖s恗h恘q恛h恞y恟x恠g恡l耻c恦s恮z恱y恲p恴d恵h恷q恾m悀y悁j悂p悦y悆y悇t悈j悊z悋l悎h悏q悐t悑b悓q悕x悗m悘y悙h悜c悞w悡l悢l悤c悥y悧l悩n悪e悮w悰c悳d怅c闷m悷l悹g悺g凄q悾k悿t惀l惁x惂k惃g惄n惇d惈g惉z惌y惍j惎j惏l惐y惒h惓q惔t惖t惗n啜c惛h惞x惢s惣z惤j惥y惪d恼n恽y惵d惷c惸q恻c惼b惽m惾z惿t愃x愄w愅g愇w愊b愋x愌h愐m愑y愒q愓d愔y愖c愗m愘k愙k爱a惬q愝y愞n愡c愢s愥y悫q愩g愬s愭q愮y愯s愰h愱j愳j怆c愵n愶x恺k愸z愹y愺c愻x愼s愽b忾k慀x慁h慂y栗l慅s慆t殷y慉x态t愠y慏m慐g慒c慓p慔m慖g慗c惨c惭c慛c慜m慞z恸t慠a慡s惯g慤q慥z慦j慩l怄o怂s慬q慭y虑l慯s慱t慲m悭q慴s庆q慸d慹z慺l慻j戚q慽q欲y慿p憀l憁c忧y憃c憄z憅t憆c憇q憈q惫b憌q憍j憏c怜l凭p愦k憓h憕c憖y憗y憘x憙x惮d憛t憞d憟s憠j憢x憣f愤f憥l憦l憪x悯m憭l怃w憯c憰j憱c宪x憳t憴s憵p忆y憸x憹n憺d憻t憼j憽s懀w懁x懃q懄q懅j懆s恳k应y怿y檩l懎s懐h懓a懕y懖g懗x懘c懙y懚y懛d懜m懝a怼d懠q懢l懑m懤c懥z懧n恹y懩y懪b懫z懬k懭k懮y懯f懰l懱m惩c懳h懴c懒l怀h悬x懹r忏c懻j惧j懽h慑s恋l戁n戂m戃t戄j戅g戆g钺y戓g戋j戙d戜d戝z戞j戠z戣k戦z戗q戨g戬j戫y戭y戯x战z戱x戏h户h戸h戹e戺s戻t戼m扂d扄s扅y扆y扊y扏q仂l払f扖h扗z扙z扚d扜y扝y扞h扟s叉c扡t扦q扤w扥d扨s扱c扲q扴j扵y扷b扸x抵d扻z扽d抁y抂k拚p抅j擦c抇h抈y抋q抌d抍z殒y抏w抐n抔p抙p抜b抝a択z抣y抦b抧z抩n抪p抭y抮z抰y抲h抳n曳y抶c抷p抸j抺m抾q拀z拁j拃z抛p拏n拑q拕t拝b拞d拠j拡k拤q拪q拫h拰n拵c拸y拹x拺c拻h挀b挃z挄k挅d挆d挊n挋z挍j挏d挐n挒l挓z挔l挕d挗j挘l挙j挜y挦x挧y挩t挬b挭g挮t挰c挱s挳k挴m挵n挷b挸j挻s挼r挟x挿c捀p捁j捄j捇h捈t捊p捑z捒s捓y捔j捖w捗b捘z捙y捚z捛l捜s捝t捠b捤w捥w捦q舍s捪m扪m捬f捯d捰w卷j捳y捴z捵c捸t捹b捼r捽z捾w捿q掁c扫s抡l掅q掆g掋d掍h掑q掓s掔q掕l掗y挣z掚l挂g掜y掝h掞y掟z采c掦t掫z掯k掱p掲j掵m掶j掹m掻s掽p掿n拣j揁z揂j揃j揅y揇n揈h揊p揋w揌z揑n揓s揔z揕z揗x揘h揙b扬y换h揜y揝z揟x揢k揤j揥d揦l揧l揨c揫j揬t挥h揯g揰c揱x揳x揵q揷c背b揺y揻w揼b揾w搃z搄g构g搇q搈r搉q搊c损s搎s搑r搒b搕k摇y捣d搘z搙n搚l搝q擀g搢j搣m搤e搥c搧s搨t搩j搫p搯t搰h搱z搲w搳h搵w抢q搷t搸z搹e搻n搼q搾z摂s摃g摉s摋s摌c摍s摎n摏c摐c掴g摓f摕d摗s摙l摚c摛c掼g摝l搂l摠z摡g摢h摣z摤q摥t摦h摨z摪j摫g摬y摮a挚z摰n摱m摲c抠k摴c摵s抟t摷j掺c摼k摽b摿y撀g撁q撃j撆p捞l撉d撊x撋r撌g撍z撎y撏x撑c挠n撔h撗h搭d捻n撛l撜z撝h挢j撠j撡c掸d拨b撦c撨x撪b抚f撯z撱w扑p揿q撴d撶h撹j挞t撽j挝w捡j拥y擃n掳l擆z择z擈p擉c击j挡d擌s擏q擑j擓k担d擕x擖y擙a据j擛y擜e擝m挤j擡t擥l擧j擨y擩r擪y擫y拟n擭h擮j摈b拧n搁g掷z擳z扩k擵m擶j撷x擸l擹t摆b擞s撸l擽l扰r攁y攃c摅s攅z撵n攇x攈j攊l攋l攌h攍y攎l隆l攐q攑q攓q拦l攕x撄y攗m搀c撺c携x摄s攞l攟j攠m攡c攒c挛l摊t攦l攧d攨w攩d搅j揽l攭l攰g攱g攲q攳x考k攼g攽b敀p敁d敂k敃m敄w敆h敇c敊c敋g敍x敎j敐c敒s敓d敔y败b叙x敚d敜n敟d敠d敡y敤k敥y敧q敨t敩x敪d敭y敮x敯m敱a敳a敌d敶c数s敹l敺q敻x敼y敽j敾s敿j斀z斁d敛l毙b斄l斅x斆x斈x斉q斊q斍j斎z斏l斒b斔y斓l斖w斘s斚j斝j斞y斠j斢t斣d斦y斨q斪q斩z斱z斲z斴l斵z斶c断d斸z斺c斻h斾p斿l旀m旗q旇p旈l旉f旊f旍j旐z旑y旓s旔j旕e旘z旙f旚p旛f旜z旝k旞s旟y旡j旣j旪x旫t旲t旳d旴x旵c旸y旹s旻m旼m旽t旾c旿w昁b昄b昅j升s昈h昉f昋g昍x昑q昒h昖y昗z昘f昚s昛j昜y昞b昡x昢p昣z昤l昦h昩m昪b昫x昬h昮z昰s昲f昳d昷w昸d昹a昺b昻a昽l昿k晀t时s晄h晅x晆k晈j晋j晊z晍t晎h晐g晑x晘h晙j晛x晜k昼z曦x晠s晢z晣z晥w晧h晩w晪t晫z晭z晱s晳x晵q晸z晹y晻a晼w晽l晿c暀w暁x暃f暅g暆y晕y晖h暊x暋m暍y暏s暐w暒q暓m暕j阳y暙c暚y暛s暜p暞j暟k暠g暡w畅c暣q暤h暥y暦l暩j暪m暂z暬x暯m暰c昵n暲z暳h暵h暶x暷c了l暺t暻j暼p暽l暿x曀y曁j曂h曃d晔y曅y历l昙t曈t晓x曊f曋s曍h曎y曏x曐x曑s曒j曓b曔j曕y暧a曘r曚m曞l曟c旷k曡d曣y曤h曥l曧r曨l曪l曫l晒s曭t曮y曯z曱y曵y曶h书s曺c曽z朁c朂x会g朄y朅q朇b朎l朏k朒g朓t朖l朘j朙m朚h朜t朞j朠y朡z朢w朣t朤l朥l胧l术s朰t朲r朳b朶d朷d朸l朹q朻j朼b朾c朿c杁r杄q杅y圬w杊x杋f杍z杒r杔t杕d杗m杘c杙y杚g杛g杝t杢j杣m杤w杦j杧m杫s杬y杮f东d杴x杶c杸s杹h杺x杻c杽c枀s枂w枃j枅j枆m枊a枍y枎f枏n枑h枒y枓d枔x枖y栀z枛z枟y枠h枡s枤d枦l枩s枬z枮x枱s枲x拐g枹b枺m枻y枼y枽y枾s枿n柀b柂d柅n柆l柇h柈b柉f柊z柋d柌c柍y柎f柕m柖s柗s柟n柡y柣z柤z柦d柧g柪a柫f柭b柮w柲b栅s柶s柷z柹s柺g査c柼y柾j栁l栂m栃l栄r栆z栍s栐y栒x栔q栕c栘y栙x栚z栛l栜s栞k栟b栠r栢b栣r栤b栥z栦c栧y栨c栫j栬z栮e栯y栰f栱g栴z栵l栶y栺z栻s栿f桇r桋y桍k桏q桒s桖x桘c桙y桚z桛k桝j桞l桟z桪x桬s桭c桮b桯t桰g桱j桲b桳b桵r桸x桹l桺l桻f桼q桽w桾j杆g梀s梂q梄y梇l梈p梉z梊d梋x梌t梎a梐b梑d梒h梕r梖b梘j梙h梚w梛n梜j条t梞j枭x梠l梡k梣c梥s里l梪d梬y梮j梱k梲z梴c梶w梷j梸l梹b梺x梻x梼d梽z梾l梿l棁z弃q棅b棆l棈q棊q棌c棎c棏d棐f棑p棓b棔h枨c枣z棙l棛y棜y棝g棞j栋d棡g棢w棤q棥f棦c栈z棨q棩y棪y棫y棬j棭y棯s栖q棳z棴f棶l棷z棸z棻f棽c棾q棿n椀w椂l椄j椆c椇j椈j椉c椊z椌k桠y椑b椓z椔z椕b椖p椗d椙c椚m椛h検j椝g椞x椡d椢g椣d椥z椦q椧m椨f椩g椪p椫s椬y椮s椯d椱f椲h椳w椵j椶z椷j椸y椺x椻y椼y椾j楀y楁h楃w楄p楅b楆y楇g楈x楉r杨y楋l楌y楍b楎h楏k楐j楑k楒s枫f楘m楙m楛h楜h楟t楢y楤c楥x楧y桢z楩p楪d楬j业y楯d楰y楲w楳m楴d极j楶j楺r楻h楽l楾h楿k榁m榃t榅w榊s榋c榌b榎j榏y榑b榒n榓m榖g榗j榚y榝s榞y榟z榠m榡s榢j榣y榤j榥h榩q杩m榬y荣r榯s榰z榲w榳t榵r榶t榸z榹s榺s榼k榽x榾g桤q槂s盘p槄t槅g槆c槇d槈n槉j枪q槏q槑m槒x杠g槕z槖t様y槙d槚j槜z槝d槞l槡s槢x槣j槤l槥h槦r椠q椁g槩g槪g槫t槬h槮s槯c槰p槱y桨j槴h槵h槶g槷n槹g槺k槻g槼g槾m樀d桩z樃l樄c枞c樆l樇x樈q樉s樋t樌g樍z樎s樏l梁l樒m楼l樔c樕s樖k标b樚l樛l樜z樝z枢s樠m樢n样y樤t樥p樦z樧s権q樫j樬c樭j樮y樰x樲e樳x樴z樶z樷c朴p树s桦h樻g橀x橁c橂d橃f橅m橆w桡r橉l橊l桥q橌x橍r橎f橏j橑l橒y橓s橔t橕c橖t橗m橚s橜j橝t橞h机j橠n椭t橣n橤r橦t橧z橪y横h橬q橭g橮l橯l橰g橲x橳s橴z橵z橶j橷d橸j橺x橻c橽t橾s橿j檃y檅h檆s檇z檈x柽c檊g檋j檌z檍y檏p檒f檓h档d檕j檖s檘p檙c檚c檛z桧g檝j檞j檟j檡z检j樯q檤d檥y檦b檧s檨s檭y檮c台t檰m檱q檲t槟b檴h檵j檶q檷n柠n檹y檺g槛j檼y檽n檿y櫀q櫁m櫂z柜g櫄c櫅j櫆k櫇p櫉c櫊g櫋m櫌y櫍z櫎h櫏q櫑l櫒s橹l櫔l櫕c櫖l櫗m櫘h櫙o榈l栉z櫜g椟d橼y栎l櫠f櫡z櫣l櫤j橱c櫦q槠z栌l櫩y枥l櫫z榇c櫭j櫮e櫯s櫰h櫱n櫲y栊l櫵q櫶x櫷g榉j櫹x棂l樱y櫼j櫽y櫾y欀x欁n栏l欅j欆s欇s欈w欉c权q欋q欌z欍j欎y椤l欐l欑c栾l欓d欔q欕e榄l欗l欘z欙l欚l欛b欜n欝y欥y欨x欩c欪c欫q欬k欭y欮j欯x欰x欱h欳k欴l欵k欶s欸a欻c欼c钦q欿k歀k歁k歂c歄g歅y歈y歊x歋y歍w歏j欧o歑h歒t歓h歔x歕p歖x歗x歘x歚s歛l歜c歝y欤y歠c欢h歨b歩b歫j歭z歮s歯c歰s歱z岁s歳s歴l歵z歶y归g歺d歽z歾m殁m殀y殅s殈x殌j殎q殐s殑j殔y殕f殗y残c殙h殜d殝z殟w殠c殢t殣j殇s殥y殦c殧j殨k殩c殚d殬d殓l殡b殰d殱j歼j殶z殸q殹y杀s殻k壳k殽x殾x毁h毃q毄j殴o毇h毈d毉y毊x毋w毎m毐a毑j毘p毚c毞b毟m毠j毢s毣m毤t毦e毧r毨x毩j球q毭d毰p毱j毲d毴b毶s毷m毸s毺s毻t毼h毾t毵s氀l氁m氂m氃t氄r毡z氉s氊z氋m氇l氎d氒j気q氜y氝n氞n氠s气q氥x氢q氩y氭d氱y氲y氶z氷b氹d氺s氻l氼n泛f氿g汃p汄z汅m汈d汋z汌c丸w汏d汑t汒m汓q汖p汘q污w汚w汢t汣j汥z汧q汫j汬j汭r汮j汯h汱t汳b汵g汷z汸f决j汼n汿x沀x沄y沇y沊d沋y沍h沎h沑n没m冲c沗h沘b沚z沜p沝z沞z沠p沢z沨f沬m沯z沰t沴l沵m沶y沷f沺t泀s况k泂j泃j泆y泇j泋h泍b泎z泏c泑y泒g泙p泚c泜z泝s泟c泤s泦j泧y泩s泬j泭f泲j泴g泹d洀p洃h洅z洆c洈w洉h洊j洍s洏e洐x洑f洓s洔z洕y洖w洘k洝a洟y洠s洡l洢y洣m洤q洦p洨x泄x洬s洭k洯q洰j洴p汹x洷z洸g洺m洿w浀q浂y浄j浉s浌p浐c浕j浖l浗q浘w浛h浝m浟y浡b浢d浤h浥y浧y浨l浫h浭g浱c浳y浵t浶l浃j浺c浻j浽s浾c浿p涀x涁s涃k涄p涆h泾j涊n涋t涍x涏t涐e涒t涗s涘s涙l涚s涢y涥h涬x涭s涰c涱z涳n涴w涷d涹w涺j涻s凉l涽h涾t淁q淂d淈g淉g淊y淍z淎p淏h淐c淓f淔z淕l淗j泪l淛z淜p淟t淢y淣n渌l淧m淩l沦l淭q淯y淰n淲b淴h渊y涞l淸q浅j淽z淾y淿b渀b渁y渂w渃r渄f渆y渇k済j渉s渋s渏y渒p渓x渕y渘r涣h减j渞q渟t渢f涡g渧t渨w渪r测c渮h渰y渱h渳m渵m渶y渷y渹q渻s渼m渽z浑h渿n湀g湁c湂e湅l湆q湇q湈m湉t凑c湋w湌c湏h湐p湑x湒j湗f湙y湚y湜s湝j浈z湠t湡y湢b湣m湤s湥t湦s涌c湨j湩d湪t湬q湭q汤s湰l湱h湳n湴b湵y湶q湷z湸l湹c湺x湻c湼n湽z満m溁y溂l溄f溇l沩w溊b溋y溌h溍j溎y溑s溒y溓l溔y溕m准z溗c溙t溚t溛w沟g溞s溠z溡s溣l溤m溦w溨z溩w温w溬q溭z溮s溰a溵y溸s溹s湿s溾a溿p滀c滃w沧c灭m滆g滈h滉h滊q涤d滍z荥x滐j滒g滘j滙h滛y滝l滣c滧y滪y滫x沪h滭b滮b滞z滰j滱k渗s滳s滶a卤l浒h滺y滻c滼f滽y滚g满m漀q渔y漃j漄y漅c漇x漈j漊l漋l漌j漍g漎c漑g漒q漖j漗c漘c漙t沤o漛t漜y漝x漞m漟t漡s汉h涟l漥w漦c漧g漨f渍z漮k漰p涨z漴c漵x漷h渐j漺s漻l漼c漽t浆j潀c潂h潃x潄s潅g潈z潉k潊x潌z潏y潐j泼p潒d潓h洁j潕w潖p潗j潙w潚s潜q潝x泻x潠s潡d润r潥s潧z潨c潩y潪z潫w潬t浔x溃k潱y潳t潵s潶h滗b潹c潻s潽p潾l涠w涩s澁s澂c澃j澅h浇a涝l澊c澋j澏h澐y澑l澒h澓f澔h澕h澖x涧j澘s澙x澛l澝n澞y澟l渑m澢d澣h泽d澥x澦y澨s泶x澪l澫w澬z澭y浍k澯c澰l淀d澲y澴h澷m澸g澺y澻s澼p澽j澾t澿q浊z浓n濄g濅j濆p濇s濈j濊h濋c濌t濍s濎d濏s濐z濓l濔m濖s濗m泞n濙y濚y濜j濝q济j濢c濣w涛t濥y濦y濧d濨c濩h濪q滥l濬j濭a潍w滨b濲g濳q濴y濶k濷f濹b溅j濻w泺p濽z滤l濿l瀁y瀂l瀃s瀄z滢y渎d瀈h瀊p渖s瀌b瀍c瀎m浏l瀒s瀓c瀔g濒b瀖h瀗x泸l瀙q瀜r沥l瀞j潇x潆y瀡s瀢w瀤h瀥x瀦z泷l濑l瀩d瀪f瀫h瀬l瀭s瀮l瀯y瀱j潋l瀳j瀴y瀶l瀷y瀸j瀺c瀻d瀼r瀽j澜l瀿f灀s灁y灂z沣f滠s灅l灆l灇c灈q灉y灊q灋f灍j灎y灐y洒s灒z灓l灔y灖m灗s滩t灙d灚j灛c灏h灟z灠l灡l灢n湾w滦l灥x灦x灧y灨g灪y灮g灱x灲x灳h灴h灷z灹z灺x灻c灾z炁q炂z炄n炆w炇p炈y炋p炌k炐p炑m炘x炚g炛g炞b炟d炠x炡z炢z炣k炤z炥f炦b炧x炨x炩l炪z炰p炴y炵t炶s为w炿z烄j烅x烆h烇q烉h烋x烌x烍x烎y乌w烐z烑y烒s烓w烔t烕m烖z烗k烚x烜x烝z烠h烡g烢c烣h烥c烰f烱j烲x烳p烃t烵z烶t烸h烺l烻y烼x烾c烿r焀h焁x焂s焃h焄x焅k焆j焇x焈x焋z焌j焍d焎x焏j焒l焔y焗j焛l焜k焝h焞t焟x焠c无m焢h焣j焤f焥w焧z焨f焩p焪q焫r焬x焮x焳j焴y焵g焸x焹g焻c焼s焽x焾n焿g煀q煁c煂h煃k煄z煆x煇h煈f炼l煋x煍j煏b煐y煑z炜w煓t煔s煕x煖n煗n煘c烟y煚j煛j煝m煟w煠y煡j茕q煣r焕h烦f煪q煫s炀y煭l煯j煰z煱g煴y煵n煶s煷h煹g煻t煼c煾e煿b熀h熁x熂x熃w熅y熆h熇h熈x熉y熋x熌s熍q熎y熐m熑l荧y熓w熕g熖y炝q熚b熛b熜c熝l熞j熡l熢p熣s熤y熥t熦j熧z熩h熪y熫z熭w熮l熯h熰o热r熲j熴k熶c熷z熸j熺x熻x熼y熽x炽c熿h燀c烨y燂t焰y燅x燆q燇j灯d炖d燊s燋q燍s燏y磷l燑t烧s燖x燗l燘m烫t燚y燛j焖m燝z营y燡y燣l燤t灿c燨x燩q燪z燫l烛z燯l燱y燲x燳z烩h燵t燶n燷l燸x燺h燻x烬j燽c焘d燿y爀h爂b爃r爄l爅m爇r爈l爉l爊a爋x爌k烁s爎l爏l炉l爑j爒l爓y爔x爕x爖l爘c爚y烂l爜c爞c爟g爡c爢m爣t爤l爦l爧l爩y爫z争z爮p爯c爲w爳h爴j爷y爼z尔e牀c牁k牂z牃d牄q牅y牉p牊c牋j牎c牏y牐z牑b牓b牕c牗y牍d牚c牜n牞j它t牣r牤m牥f牨g瘪b牫g牬b牭s牰y牱k牳m牶q牷q牸z牻m牼k牵q犂l犃p犅g犆z犇b犈q犉c犌j犎f犐k犑j犓c犔x犕b荦l犗j犘m犙s犚w犁l犜d犝t犞q犠x犡l犊d犣l犤b犥p犦b牺x犨c犩w犪k犫c犮q犱j犲c犳z犵g犺k犻p犼h犽y犾y犿h状z狅k狆z狇m狉p狊j狋y狌s狏t狑l狓p狕y狖y狘x狛b狜k狝x狟h狢h狣z狤j狥x狦s狧t狪t狫l狵m狶x狭x狈b狾z狿y猀s猂h猄j猅p猆f猇x猈b猉q猋b猌y猐q猑k猒y猔z猘z狰z猚y猟l猠c猣z猤g猦f猧w猨y猭c猯t猰y猲h猳j猵p犹y猺y狲s猼b獀s獁m獂h獃d狱y狮s獆h獇q獈y獊c獋h獌m奖j獏m獑c獓a獔h獕s獘b獙b獚h獛p獜l獝x獞t獟y獡s獢x獣s獤d獥j獦g獧j独d獩h狯k猃x猕m狞n獱b获h獳r獴m猎l獶n犷g兽s獭t献x獽r獿n猡l玁x玂q玃j玅m兹c玈l玊s玌q玍g玏l玐b玒h玓d玔c玕g玗y玘q玙y玚c玜h玝w玞f玠j玡y玣b玤b玥y玦j玧m珏j玪j玬d玭p玱q玴y玵a玶p玸f玹x玼c玽g玾j玿s珁c珃r珄s珅s珆y珇z珋l珌b珎z珒j珓j珔j珕l珖g珗x珘z珚y珛x珜y珝x珟s珡q珢y珣x珤b珦x珨x珪g珫c珬x佩p珯l珰d珱y珳w珴e珵c珶d珷w珸w珹c珺j珻m珼b珽t现x珿c琀h琁x琂y琄x琇x琈f琋x琌l琍l琎j琑s琒f琓w琔d琕p琖z琗c琘m琙y琜l琝m琞s琟w琠t琡s琣b琤c琧e琩c琫b琭l琯g琱d琲b琷q琸z琹q珐f琻j琽d琾j珲h瑀y瑂m瑃c瑄x瑅t瑆x瑇d瑈r瑉m玮w瑌r瑍h瑎x瑏c瑐j瑑z瑒y瑓l瑔q瑖d瑝h瑠l瑡s瑢r琐s瑶y瑥w瑧z瑨j莹y玛m瑫t瑬l瑮l琅l瑱t瑲q瑳c瑴j瑵z瑹t瑺c瑻k瑼z瑽c瑿y璂q璄j璅s璆q璈a琏l璌y璍y璏z璑w璒d璓x璔z璕x璖q璗d璘l璙l璚j璛s璝g璟j璡j璢l玑j璤h璥j瑷a璪z璫d璬j璭g璮t璯h环h璱s璲s璳t璴c璵y璶j璷l璸b璹s璻z璼l玺x璾j璿x瓀r瓁w瓂g瓃l瓄d瓅l瓆z瓇r瓈l瓉z琼q瓋t瓌g瓍s瓎l珑l瓐l瓑l瓓l璎y瓕m镶x瓗q瓘g瓙d瓒z瓛h瓝b瓟b瓡z瓥l瓨x瓩q瓪b瓫p瓬f瓭d瓳h瓵y瓸b瓹j瓺c瓻c瓽d瓾w甀z甁p甂b甃z甅l甆c甈q甉x甊l甋d瓯o甎z甐l甒w甔d瓮w甖y甗y甛t甝h甞c甠q甡s产c産c甤r甧s甪l甮f甴y甶f甹p甼t甽z甿m畁b畂m畃x畆m畇y畉f畊g畍j畐f畑t畒m畓d畕j畖w畗d畘n亩m畟j畠t畡g毕b畣d畤z畨f畩y画h畲s畭s畮m畯j异y畱l畳d畵h畷z畺j畻c畽t畾l疀c疁l疂d疄l疅j畴c疈p疉d叠d疌j疍d疎s疐z疓n疕b疘g疛z疞x疢c疦j疧q疨y疩c疭z疶x疷z疺f疻z痱f痀g痁d痆n痋t痌t痎j痏w痐h痑t痓z痗m痉j痚x痜t痝m酸s痡f痥d痩s痬y痮z痯g麻m痵j痶t痷a痸c痹b痻m痽d痾k瘂y瘄c瘆s瘇z瘈z瘉y疯f疡y瘎c瘏t瘑g瘒w痪h瘔k瘖y瘚j瘜x瘝g瘗y疮c瘣h疟n瘨d瘬z瘮s瘯c瘱y瘲z瘶s瘷s瘘l瘻l瘽q疗l癄q癅l痨l痫x癈f瘅d癋h癎x癏g癐g癑n癒y癓w癕y癗l疠l癙s癚d癛l癝l癠j痴c痒y疖j症z癧l癨h癞l癪j癣x瘿y瘾y瘫t癫d癳l癴l癵l癶b癹b発f发f癿q皀b皁z皃m皅p皉c皊l皌m皍j皏p皐g皒e皔h皕b皗c皘q皑a皛x皜h皝h皞h皟z皠c皡h皢x皣y皥h皦j皧a皨x皩h皪l皫p皬h皭j皯g疱p皳q皵q皶z皷g皲j皹j皱z隳h皼g皽z皾d盀q盁y盃b啊a阿a埃a挨a哎a哀a癌a蔼a矮a艾a碍a隘a鞍a氨a安a俺a按a暗a岸a胺a案a肮a昂a盎a凹a敖a熬a翱a袄a傲a懊a澳a芭b捌b扒b叭b吧b笆b八b疤b巴b拔b跋b靶b把b耙b霸b罢b爸b白b柏b百b佰b拜b稗b斑b班b搬b扳b般b颁b板b版b扮b拌b伴b瓣b半b办b绊b邦b梆b榜b膀b绑b棒b磅b蚌b镑b傍b谤b苞b胞b包b褒b盄d盇h盉h盋b盌w盓y盕f盙f盚q盗d盝l盏z盠l盢x监j盦a卢l盨x盩z荡d盫a盬g盭l盰g盳m盵q盷x盺x盻x盽f盿m眀m眂s眃y眅p眆f眊m県x眎s眏y眐z眑y眒s眓h眔d眕z眖k眗j眘s眛m眜m眝z眞z眡s眤n眦z眧c眪b眫p眬l眮t眰d眱d眲n眳m眴s眹z眻y眽m众z眿m睂m睄s睅h睆h睈c睉c睊j睋e睌m睍x睎x困k睒s睓t睔g睕w睖l睗s睘q睙l睁z薄b雹b保b堡b饱b抱b暴b豹b鲍b爆b杯b碑b悲b卑b北b辈b贝b钡b倍b焙b被b奔b本b笨b崩b绷b甭b泵b蹦b迸b逼b鼻b比b鄙b笔b彼b碧b蓖b蔽b毖b庇b闭b敝b弊b必b辟b壁b臂b避b陛b鞭b边b编b贬b扁b便b变b卞b辨b辩b辫b遍b彪b膘b表b鳖b憋b彬b斌b宾b兵b冰b柄b丙b秉b饼b炳b睝l睐l睟s睠j睤b睧h睩l睾g睭z睮y睯h睰m睱x睲x睳h睴h睵z睶c睷j睸m睺h睻x睼t瞁x瞂f瞃w瞆g眯m瞉k瞊d瞋c瞐m瞕z瞖y瞗d瞘k瞙m瞚s瞛c瞜l瞝c瞒m瞡g瞣w瞤s瞦x瞨p瞫s瞮c瞯x瞱y瞲x瞴w瞶g瞷j瞸y瞹a瞺h睑j瞾z矀w矁c矂s矃n矄x矅y矆h矈m矉p矋l矌k矎x矏m矐h矑l矒m矔g矕m矖x矘t矙k瞩z矝j矞y矟s矠z矡j矤s病b玻b菠b播b钵b波b博b勃b搏b铂b箔b伯b帛b舶b脖b膊b渤b泊b驳b捕b卜b哺b补b埠b不b步b簿b部b怖b猜c裁c材c才c财c睬c踩c彩c菜c蔡c餐c蚕c苍c舱c藏c操c糙c槽c曹c草c策c蹭c插c茬c茶c查c碴c搽c察c岔c差c诧c拆c柴c豺c蝉c馋c谗c缠c阐c颤c昌c猖c矦h矨y矪z矫j矰z矱y矴d矵q矷z矹w矺z矻k矼g砃d砄j砅l砆f砇m砈e砊k砋z砎j砏p砐e砓z砕s砙w砛j砞m砠j砡y砢l砤t砨e砪m砫z砮n砯p砱l炮b砳l砵b砶p砽y砿k硁k硂q朱z硄k硆e硈q硉l硊w硋a硍k硏y硑p硓l硔h硘q硙w硚q硛c硜k硞q硟c硠l硣x硖x硥m硦l硧y砗c硩c砚y硰s硱k硲y硳z硴h硵l硶c硹s硺z硻k硽y硾z硿k碀c碁q碂z碃q常c长c肠c敞c唱c倡c超c抄c钞c朝c嘲c潮c巢c吵c炒c车c扯c撤c掣c澈c郴c臣c辰c晨c忱c沉c陈c趁c衬c称c城c橙c成c呈c乘c程c澄c诚c承c逞c骋c秤c持c匙c池c迟c弛c驰c齿c侈c赤c翅c斥c充c虫c崇c抽c酬c踌c稠c愁c筹c仇c绸c瞅c丑c臭c初c出c躇c锄c雏c滁c除c楚c碄l碅j碆b碈m碊j碋h碏x碒y碔w碕q碖l碙n碝r碞y碠d碢t碤y碦k碨w硕s碪z碫d碬x砀d碮t碯n碵t碶q碷d碸f确q码m碿s磀e磃s磄t磆h磇p磈k磌t磍x磎x磏l磑w磒y磓d磖l磗z磘y砖z磛c磜q磝a磞p磟l磠l磡k磢c碜c磤y磥l磦b碛q磩z磪c磫z磮l矶j磱l磳z磵j磶x磸d磹d磻b磼j硗q磾d磿l礀j礂x礄q礆j礇y礈z礉h礊k礋z礌l础c矗c搐c触c处c揣c川c穿c椽c船c喘c串c窗c幢c床c闯c吹c炊c捶c锤c垂c春c椿c醇c唇c淳c纯c蠢c戳c绰c疵c茨c磁c雌c辞c慈c瓷c词c此c刺c赐c次c聪c葱c匆c粗c醋c簇c促c蹿c篡c窜c摧c崔c催c脆c瘁c粹c淬c翠c村c存c磋c撮c搓c措c挫c错c达d答d瘩d打d大d呆a歹d傣d戴d殆d代d贷d袋d待d逮d礍j礏y礐q礑d礒y礔p礕p礖y礗p礘e礚k礛j礜y礝r礟p礠c礡b礢y礣m礥x矿k礧l礨l礩z砺l砾l矾f礭q礮p礯y礰l砻l礲l礳m礵s礷j礸c礹y礽r礿y祂t祃m祄x祆x只z祊b祋d祌z祎y祏s佑y祑z祒t祔f秘b祙m祡c祣l祤y祦w祩z祪g祫x祬z祮g祰g祲j祳s祴g祵k祹t祻g祼g祽z祾l禄l禂d禃z禆b禇c禉y禋y禌z祸h禐y禑x禒x怠d耽d丹d郸d胆d旦d氮d但d淡d诞d蛋d党d刀d蹈d倒d祷d到d稻d悼d道d德d得d的d蹬d登d等d瞪d凳d邓d堤d低d滴d迪d笛d狄d翟d嫡d底d地d蒂d第d帝d弟d递d缔d颠d掂d滇d碘d点d典d靛d电d佃d甸d店d惦d奠d殿d碉d叼d凋d刁d掉d钓d调d跌d爹d碟d蝶d迭d谍d禓s禔z禕y禖m禗s禘d禙b禛z禜y禝j禞g禟t禠s禡m禢t禣f禤x禥q御y禨j禩s禅c禫d禬g禭s礼l禯n祢m禲l禴y禵t禶z禷l禸r禼x秃t秂r秄z秅c秇y籼x秊n秌q秏h秐y秓z秔j秖z秗y秙k秚b秛p秜n秝l秞y秠p秡b秥n秨z秪d秬j秮h秱t秲s秳h秴h秵y秶z秷z秹r秺d秼z秾n秿f稁g稄x税s稇k秆g稉j稊t稌s稏y稐l稑l稒g稓z稕z稖b稘j稙z稛k棱l丁d盯d叮d钉d顶d鼎d锭d定d订d冬d董d懂d侗d恫d洞d兜d抖d陡d豆d逗d痘d都d督d毒d读d堵d睹d赌d杜d镀d肚d度d渡d妒d端d短d锻d段d缎d堆d队d墩d蹲c敦d顿d囤d钝d盾d遁d掇d哆d多d垛d躲d朵d跺d舵d剁d惰d蛾e峨e鹅e俄e额e讹e娥e厄e扼e遏e鄂e饿e恩e而e耳e饵e洱e二e稝p禀b稡z稢y稤l稦y稧x稨b稩j稪f稫p稭j种c稯z稰x稲d稴x稵z稶y稺z稾g谷g穁r穂s穃r穄j穅k穇c穈m穊j穋l稣s颖y穏w穐q穒k穓y穔h穕q穖j穘x穚j穛z穜t穝z穞l穟s穑s秽h穣r穤n穥y穦p穧j穨t稳w穪c穬k穭l穮b穯s穱z穲l穳c穵w穻y穼s穽j穾y窂l窅y窇b窉b窊w窋z窌j窎d窏w窐w窓c窔y窙x窚c窛k窞d窡z窢h贰e罚f筏f伐f乏f阀f法f藩f帆f番f翻f樊f钒f繁f凡f反f返f范f贩f犯f饭f芳f方f肪f房f防f妨f访f纺f放f菲f非f啡f飞f肥f匪f诽f吠f肺f沸f费f芬f酚f吩f氛f分f纷f焚f汾f粉f份f忿f粪f丰f封f蜂f峰f锋f风f烽f逢f冯f缝f讽f奉f凤f否f敷f肤f孵f扶f拂b辐f幅f氟f符f伏f俘f服f窣s窤g窝w洼w窫y穷q窑y窰y窱t窲c窴t窵d窭j窷l窸x窹w窥k窻c窼c窾k竀c竁c竂l竃z窍q竆q窦d竈z竉l窃q竍s竎f竏q竐c竑h竒q竓h竔s竗m竘q竚z竛l竜l竝b竡b竢s竤h竧j竩y竫j竬q竮p竰l竱z竲c竳d竴c竵w竞j竷k竻l竼p竾c笀m笁z笂w笅j笇s笉q笌y笍z笎y笐h笒c笓b笖y笗d笘s笚d笜z笝n笟g笡q笢m笣b笧c笩f笭l浮f涪f福f袱f弗f甫f辅f俯f釜f斧f脯f腑f府f腐f赴f副f覆f赋f傅f付f阜f父f腹f负f富f讣f附f缚f咐f噶g嘎g该g改g概g钙g盖g溉g甘g柑g竿g肝g赶g感g敢g赣g钢g缸g肛g纲g港g篙g皋g高g膏g羔g糕g搞g镐g稿g哥g歌g戈g鸽g胳g疙g割g革g葛g格g蛤g阁g隔g铬g各g给g根g跟g耕g更g庚g羹g笯n笰f笲f笴g笵f笶s笷m笹t筇q笽m笿l筀g筁q筂c筃y筄y筈k筊j笋s筎r筓j筕h筗z筙l筜d筞c筟f筡t筣l筤l筥j筦g笕j筨h筩t筪x筫z筬c筭s筯z筰z筶g筸g筺k筼y筽o筿x箁p箂l箃z箅b箆b箉g箊y笺j箌z箎c筝z箑s箒z箓l箖l箘q箙f箚z箛g箞q箟j箠c箣c箤z箥p箮x箯b箰s箲x箳p箵x箶h箷s箹y箺c箻l箼w箽d箾s箿j节j篂x篃m埂g耿g梗g工g攻g功g恭g龚g供g躬g公g弓g巩g汞g拱g贡g共g钩g勾g苟g狗g垢g购g辜g菇g咕g箍g估g沽g孤g姑g鼓g古g蛊g骨g股g故g顾g固g刮g瓜g寡g褂g乖g怪g棺g关g官g冠g观g管g馆g罐g灌g贯g光g逛g瑰g规g圭g硅g闺g轨g鬼g诡g癸g桂g跪g贵g辊g棍g锅g郭g果g裹g过g哈h篅c篈f筑z篊h箧q篍q篎m篏q篐g篔y篕h篖t篗y篘c篛r篜z篞n篟q筱x篢l篣p笃d篧z篨c筛s篫z篬q篭l篯j篰b篲h筚b篴z篵c篶y篸c篹s篺p篽y篿t箦z簁s簂g簃y簄h簅c簆k簈p簉z簊j篓l簎c簐n蓑s簒c簓s簔s簕l簗z簘x簙b簚m簛s簜d簝l箪d簠f简j簢m篑k簤d簥j簨z簩l箫x簬l簭s簮z簯q簰p簱q簲p簳g簴j簵l簶l檐y簹d簺s簻z簼g签q帘l籂s骸h孩h海h氦h亥h害h骇h酣h憨h邯h韩h涵h寒h函h喊h罕h翰h撼h捍h旱h憾h悍h焊h汗h夯b杭h航h壕h嚎h豪h毫b郝h好h耗h号h浩h呵h喝h荷h菏h核h禾h和h何h合g盒h貉h阂h河h涸h赫h褐h鹤h贺h嘿h黑h痕h很h狠h恨h亨h衡h轰h哄h烘h虹h鸿h洪h宏h弘h红g喉h侯h猴h吼h厚h候h后h呼h乎h忽h瑚h葫h胡h蝴h狐h糊h湖h篮l籄k籅y籆y籇h籈z籉t籊t籎y籏q藤t籑z籒z籓f籔s籕z籖q籗z籘t籙l籚l籛j箨t籝y籞y籁l笼l籡s籢l籣l龠y仠g籦z籧j籨l笾b簖d籫z篱l籭s箩l籯y籰y籱z吁x籵f籶s籷z籸s籹n籺h籾n籿c粀z粁q粂z粃b粄b粅w粆s粇k粈r粊b粋c粌y粍z佂z粏t粐h佹g侀x粔j粖y粙z粚c粛s粠h侼b侽n粣c粦l粨b粩l粫e粬q粭h粯x粰f粴l粤y粶l粷j粸q粺b粻z弧h虎h唬h护h互h花h华h猾h滑h化h话h槐h淮h桓h还h缓h患h豢h宦h幻h荒h慌h黄h磺h蝗h簧h皇h凰h惶h煌h晃h幌h恍h谎h灰h辉h徽h恢h蛔h回h悔h慧h卉h惠h晦h贿h讳h诲h绘h荤h昏h婚h魂h混h豁h活h伙h火h或h惑h霍h货h圾j基j畸j稽j箕j粿g糀h倄y糃t糄b糆m糉z糋j糎l糏x糐f糑n糒b糓g糔x糘j糚z糁s糡j馍m糣s糤s糥n糦x粮l糩k糪b糫h糬s糭z糮x糱n粝l糳z籴d糵n粜t糷l糹s糺j糼g糽z纠j纪j紁c纣z紃x约y纡y纥g纨w纫r纹w紌q纳n紎z紏t纽n紑f紒j纾s纰p紖z纱s紘h纸z级j纭y紝r紞d紟j紣c紥z紦h紨f紩z紪q紬c紭h扎z细x绂f绁x绅s紴b紵z紶q偟h肌j饥j迹j激j讥j鸡j姬j绩j缉j吉j棘j辑j籍j集j及j急j疾j即j嫉j脊j己j蓟j技j偤y冀j季j伎j祭j悸j寄j寂j计j记j既j忌j际j妓j继j嘉j枷j佳j加j荚j颊j贾g甲j钾j假j稼j架j驾j嫁j尖j间j煎j兼j肩j艰j缄j茧j柬j硷j剪j荐j鉴j践j贱j见j键j箭j件j紷l紸z绍s绀g紻y绋f紽t紾z绐d绌c絁s终z弦x组z絅j絇q絈m絉s絊z絋k経j絍r绗h絏x傶q结j絑z絒c傸c絓g絔b绝j絖k絗h絘c絙h絚g绦t絜j絝k绞j絟q絠g络l绚x絣p絤x絥f絧t绒r絩t絪y絫l絬x絭j絯g絰d统t丝s絴x絵h絶j絸j绢j絺c絻w絼z絽l絿q綀s綂t绡x綄h綅q绠g綇x绨t綉x綊x綋h綌x綍f綎t绥s綐d捆k綒f经j綔h綕z綖y綗j綘f健j舰j饯j建j僵j姜j江j疆j蒋j讲j匠j酱j降j蕉j椒j礁j焦j胶j交j郊j骄j嚼j铰j脚j狡j角j饺j缴j教j酵j轿j较j叫j窖j揭j接j皆j秸j街j阶j截j劫j桔j捷j睫j竭j姐j戒j藉j芥g界j借j介j疥j诫j巾j筋j斤j金j兠d今j津j襟j紧j锦j谨j进j靳j禁j近j浸j継j続x综z綝c缍d綟l绿l綡j绻q綤s綧z綨j綩w綪q綫x绶s维w綯t绾w网w綳b缀z綶g綷c纶g绺l绮q绽z綼b绫l绵m緀q緁j緂t緃z绲g緅z緆x缁z緈x緉l绯f緌r緍m緎y総z緐f緑l绪x緓y緔s緕z緖x缃x缂k线x緜m緟c缗m緢m缘y緤x緥b缌s緧q緪g緫z缅m緭w緮f纬w緰x缑g缈m緳x练l緵z缏b緷g緸y缇t緺g荆j兢j茎j睛j晶j鲸j京j惊j精j粳j井j警j景j颈g静j境j敬j镜j靖j竟j炯j窘j揪j究j玖j韭j久j灸j九j酒j救j旧j臼j舅j咎j就j疚j鞠j拘j狙j疽j居j驹j菊j咀j矩j举j沮j聚j拒j巨j具j距j剏c踞j锯j俱j句g炬j捐j鹃j娟j倦j眷j撅j攫j抉j掘j倔j爵j觉j诀j均j菌j钧j军j君j峻j致z緼w緽c緾c緿d縀x縁y縂z縃x縄s縅o縆g縇s萦y缙j缢y缒z縌n縍b縎g縏p绉z缣j縒c縓q縔s縕y縖x縗c縘x縙r縜y缜z缟g缛r縠h縡z縢t县x縤s縥z縦z縧t縨h縩c縪b縬c缡l缩s演y縰x纵z缧l纤q缦m絷z缕l縸m缥p縺l縼x总z縿s繀s繂l繄y缫s缪m襁q繉s繊x繋j繌z繍x繎r繐s缯z繓z织z缮s繖s繗l繘j繙f缭l繛c繜z勩y俊j竣j浚j郡j骏j喀k咖g卡k咯g开k揩k楷j慨k刊k堪k勘k坎k砍k看k康k慷k糠k扛g抗k亢g炕k拷k烤k靠k坷k苛k柯k棵k磕k颗k科k咳h可k渴k刻k客k课k肯k啃k坑k吭h空k恐k孔k控k口k扣k寇k枯k哭k窟k苦k酷k裤k夸k垮k挎k跨k胯k筷k快k款k筐k狂k框k眶k亏k盔k葵k奎k魁k傀g绕r繟c繠r绣x缋h繣h繤z繥x繦q繧w繨d绳s繬s繮j缳h缲q繱c繲x繴b繵d繶y繷n繸s绎y繺s繻x缤b缱q繿l纀p纁x纃z纅y纆m纇l缬x纉z纩k续x累l纎x纐k纑l纒c缨y厠c纗z缵z纙l缆l纝l纞l纮h纴r纻z纼z绖d绤x绬y绹t缊w缐x缞c缷x缹f缻f缼q缾p罀z罁g罃y罆g罇z罉c罊q罋w罂y罍l罎t罏l罒w罓w馈k愧k坤k昆k括g廓k阔k垃l拉l喇l蜡l腊l辣l啦l莱l赖l蓝l婪l阑l兰l谰l览l榔l狼l廊l郎l朗l浪l牢l老l佬l姥l酪l烙l勒l雷l镭l蕾l磊l儡l肋l类l楞l冷l厘l梨l黎l狸l离l理l李l鲤l莉l荔l吏l丽l利l傈l例l俐l罙s罛g罜z罝j罞m罠m罣g罤t罥j罦f罧s罫g罬z罭y罯a罳s骂m罶l罸f罺c呩s罻w罼b罽j罿c羀l羂j羃m羄z罴p羇j羁j羉l羍d羏y羐l羑y羓b羕y羖g羗q羙m羠y羢r羟q羡x义y羬x羱y羴s羺n羾h翀c翃h翆c翇f翈x翉b翋l翍p翏l翐z翑q习x翓x翖x翗k翙h翚h翛x翜s翝h翞j翢d翣s哷l痢l立l粒l隶l力l璃l哩l联l莲l连l镰l廉l脸l链i啅z粱l良l辆l量l晾l亮l谅l撩l聊l僚l燎l寥l辽l潦l撂l镣l廖l料l列l烈l劣l琳l林l霖l临l邻l鳞l淋l赁l啙z啛c吝l拎l玲l菱l零l龄l铃l伶l羚l凌l灵l陵l领l另l令l溜l琉l榴l硫l馏l留l瘤l流l柳l六l龙l聋l窿l翤c翧x翨c翪z翫w翬h翯h翲p翴l翵h翶a翷l翸p翘q翽h翾x翿d耂l耇g耈g耉g耊d耎n耏n耓t耚p嗘j耝q耞j耟j耡c耣l耤j耫z耧l耭j耮l耯h耰y耲h耴y耹q耺y耼d耾h聀z聄z聅c聇z聈y聉w聏e聐y聑t聓x聕h圣s聗l聙j聛b聜d聝g闻w聟x聠p聡c聢x聣n聤t聥j聦c聧k聨l聫l聬w聭k聮l声s耸s聴t聩k聂n职z聸d聍n聺q聻n嘓g聼t听t拢l陇l漏l陋l芦l颅l虏l鲁l麓l碌l露l路l赂l鹿l潞l录l陆l驴l铝l旅l履l氯l律l率l卵l掠l略l轮l噵d论l萝l螺l逻l锣l骡l裸g落l洛l嚍j骆l嚒m蚂m马m嘛m埋m买m麦m卖m迈m脉m馒m蛮m蔓m曼m慢m漫m肁z肂s肃s肈z肊y肍q肎k肏c肐g肑d囄l肔c肕r肗r肙y肞c肣q肦b肧p肨p肬y肰r肳w肵q肶p肹x肻k胅d胇b胈b胉b胊q坺f垀h胏z胐f胒n胓p胔z胕f胘x胟m胠q胢q胣c胦y胮p胵c胷x胹e埱c埶y胾z胿g脀z脁t脃c脄m胁x脇x脋x脌n脕w脗w脙x胫j脜r脝h脟l脠s脡t脢m脤s脥j塈x睃s脨c修x脪x脱t脭c脮n脰d脳n脴p脵g脷l胀z脼l脽s脿b谩m芒m墽q茫m盲m氓m壔d忙m莽m猫m茅m锚m毛m矛m铆m卯m茂m冒m帽m貌m贸m么m玫m枚m梅m酶m霉m煤m眉m媒m镁m每m美m昧m寐m妹m媚m门m萌m檬m盟m锰m猛m孟m醚m靡m糜m迷m谜m米m觅m泌b蜜m密m棉m眠m冕m免m勉m娩m面m苗m妌j描m瞄m藐m秒m妛c妭b渺m妙m蔑m民m抿m皿m敏m姃z闽m明m螟m鸣m铭m名m命m谬m摸m腀l腁p腂g腃j腄c腅d腇n腉n腍r肾s腏z腒j腖d腗p腘g腛w腜m腝r腞z娊x脶l腢o腣d腤a脑n腨s腪y肿z腬r腯t腲w腵j婂m腶d腷b膁q腽w膄s膅t膆s膇z膉y膋l膌j膍p膎x膐l膒o膓c膔l膕g膖p膗c膙j膞z膟l膡y膢l膤x膥c膧t腻n膫l膬c膭k膮x媑z膯t膰f膱z膲j膴h媝q媡l膵c膶r膷x膸s膼z脍k脓n臄j臅c臇j臈l臋t脐q臎c膑b臐x臑n嫿h嬃x嬓j臓z摹m蘑m模m嬜x膜m磨m摩m嬟y魔m抹m末m莫m墨m默m沫m漠m寞m陌m谋m牟m某m拇m牡m姆m母m墓m暮m幕m募m慕m木m目m孨z睦m牧m穆m拿n哪n孶z钠n那n娜n氖n乃n奶n耐n奈n南n男n难n囊n闹n淖n呢n馁n嫩n能n妮n霓n倪n泥n尼n匿n逆n溺n蔫n拈n年n碾n酿n鸟d尿n捏n孽n镊n镍n涅n您n凝n臔x臕b臖x臗k臙y胪l臛h臜z臝l臞q脏z脔l臡n臢z卧w臦g臩g臫j臮j臯g臰c岅b臲n臵g臶j臷d臸z臹x岟y臽x臿c舃x与y兴x岪f岾z峊f舎s舏j舑t峣y舓s舕t铺p舗p峮q舘g舙h舚t舝x舠d舤f舥p舦t舧f舩c舮l舲l舼q峾y舽p艀f艁z艂f艃l艅y艆l艊b艌n艍j艎h艐k艑b艒m艓d艔d嵽d嶈q艖c艗y艛l艜d艝s艞y艠d艡d艢q舣y艥j艧h艩q牛m扭n钮n嶪y农n弄l奴n努n怒n女n暖n虐n挪n懦n糯n诺n哦e巆y鸥o藕o偶o巏q啪p趴p爬p帕p怕p琶p拍p排p牌p徘p湃p派p攀p潘f磐p盼p畔p判p叛p乓p庞p耪p胖p咆p刨b袍p跑p泡p呸p胚p培p裴p赔p陪p配p沛b盆p砰p抨p烹p澎p彭p蓬p棚p硼p篷p膨p朋p鹏p捧p碰p坯p砒p霹p批p披p劈p琵p毗p艪l舻l艬c艭s艵p艶y艳y艹a艻l艼d幯j庅m庺s芃p芅y芆c芇m廐j芉g芌y苄b芓z芔h芕s弐e芚t芛w芞q芠w芢r芣f芧x芲l弞s芵j芶g芺a刍c芼m芿r苀h苂c苃y苅y苆s苉p苐d苖d苙l苝b苢y苎z苨n苩p苪b苬x苭y苮x苰h徛j苲z徦j苳d苵d苶n苸h苺m苼s苽g苾b苿w徴z茀f徸z茊z茋z茐c茒y茓x茖g茘l忇l茙r茝c茞c怓n茠h茡z茢l茣w茤j茥g茦c茩h茪g茮j怱c茷f茻m茽z啤p脾p疲p皮p匹p恎d痞p僻p屁p譬p篇p偏p片p骗p恜c飘p瓢p票p撇p瞥p拼p频p贫p品p聘p乒p坪p苹p萍p平p瓶p评p屏b坡p颇p婆p破p魄b迫p粕p剖p莆p葡p菩p蒲p埔b圃b普p浦p谱p曝b瀑b期j欺q妻q七q漆q柒q沏q其j棋q奇j歧q畦q崎q愂b齐j祈q祁q起q岂q乞q企q契q器q迄q汽q泣q愪y讫q掐q茾q茿z荁h荂f荄g荅d愲g荈c荋e荌a荍q荎c荓p荕j荖l荗s荘z荙d荝c荢z慃y荰d荱w荲l荳d荴f荵r荶y荹b荺y荾s荿c莀c莁w莂b莃x莄g莇z莈m庄z莋z莌t莏s莐c莑p莔m莕x憉p莗c莙j莚y莝c莟h莡c莣w莤s莥n莦s憜d苋x莬w莭j莮n憡c莯m莵t莻n莾m莿c菂d菃q菄d菆c菈l菉l菋w菍n菎k菐p菑z菒g菓g菕l菗c憿j菙c菚z菛m菞l菢b菣q菤j菦q菧d菨j菫j懏j菬q菭t恰q洽q钎q铅q千q懡m迁q仟q谦q乾q黔q钱q钳q前q遣q谴q嵌k欠q歉q腔q羌q蔷q橇q锹q敲q悄q瞧q巧q鞘q撬q峭q俏q切q茄j且j怯q戵q侵q亲q秦q琴q勤q芹q擒q禽q沁q青q轻q卿q清q擎q晴q氰q情q顷q请q秋q丘q邱q求q囚q抯z酋q泅q趋q蛆q曲q躯q屈q驱q渠q菮g菳q庵a菵w拲g菶b菷z菺j挌g菼t菾t菿d萀h萅c苌c萈h萉f萐s萒y萓y萔t萕q萖w萗c萙k萚t萛j萞b萟y萠p萡b萢p萣d萩q萪k萫x万m萭y萮y萯f萰l萲x萳n萴c掤b莴w萶c萷s萹b萺m萻a萾y葀k葁j葂m葃z葄z葅z葇r葈x叶x葊a葋q葌j葍f葎l葏j葐p荭h葓h搮l摀w葕x葖t葘z葝q葞m葟h葠s葢g葤z摖q葥q苇w葧b葨w葪j葮d药y葰j葲q葴z摾j葹s葻l葼z取q娶q龋q趣q去q圈j颧q醛q泉q全q痊q拳q犬q券q撧j缺q炔g瘸q鹊q榷q裙q群q然r燃r冉r染r瓤r壤r攘r嚷r让r饶r惹r壬r擟m仁r人r忍r韧r任r认r刃r妊r扔r仍r日r戎r茸r擿t攂l蓉r融r熔r溶r攚w容r冗r揉r柔r肉r茹r蠕r儒r孺r如r辱r乳r汝r攺y入r褥r软r阮r蕊r瑞r锐r闰r若r弱r撒s萨s腮s鳃s塞s赛s三s叁s葽y葾y葿m蒀y蒁s蒃z蒄g蒅s蒆x蒍w蒏y蒐s蒑y蒒s蒓c莳s蒕y蒖z蒘r蒚l蒛q蒝y莅l蒟j蒠x蒢c蒣x蒤t蒥l蒦h蒧d蒨q蒩z蒪p蒫c蒬y蒭c蒮y蒰p蒱p蒳n斮z蒵x斳q蒷y蒻r蒾m荪s蓂m蓃s蓅l蓆x蓇g蓈l蓌c蓎t蓏l蓒x蓔y蓕g蓗z蓘g蓙g蓚t蓛c蓜p蓞d蓡s旤h蓢l蓤l蓧d蓨t蓩m蓪t蓫z蓭a苁c蓱p蓲q蓳j蓴c蓵j蓶w蓷t蓸c蓹y蓺y蓻z荜b蓾l蔀b蔁z蔂l散s桑s嗓s搔s骚s嫂s瑟s色s森s僧s莎s砂s沙s傻s啥s晇x煞s珊s苫s杉s山s煽s衫s闪s陕s擅s赡s膳s善s汕s晬z扇s商s赏s晌s上s尚s裳c梢s捎s稍s芍s勺s韶s少s哨s邵s奢s赊s蛇s舌s赦s射s涉s社s设s砷s申s呻s伸s身s深s娠s神s沈s暎y暔n甚s慎s生s甥s牲s蔃q暭h蔄m蔅y蔇j蔈b蔉g蔊h蔋d蔍l蔎s蔏s蔐d蔒h蔕d蔖c蔘s蔙x蔛h蔜a曌z曗y曻s蒌l蔠z蔢p蔤m茑n蔧h蔩y蔪j荫y蔮g蔯c蔰h蔱s蔲k蔳q枈p蔵z蔶z蔾l蔿w蕀j荨q蕂s蕄m蕅o蒇c蕇d蕋r蕌l蕍y荞q蕏z蕐h蕑j芸y蕔b莸y蕗l柛s荛r蕚e蕛t蕜f蕝j蕟f蕠r柨b蒉k蕣s蕥y蕦x蕧f柸b芜w蕫d蕬s萧x栭e蕯s蕰y蕱s蕳j蕵s蕶l蓣y蕸x桗d蕼s蕽n蕿x薀y薁y桜y省s盛c剩s失s施s诗s尸s虱s十s石d拾s什s梍z食s蚀s识s史s矢s使s屎s驶s始s式s示s士s世s梫q柿s事s拭s誓s逝s嗜s噬s适s棃l仕s侍s释s棇c饰s氏s市f恃s室s视s试s收s手s首s守s授s售s受s椃h瘦s蔬s梳s殊s抒s输s叔s舒s淑s椘c疏s赎s孰s熟s薯s暑s曙s署s蜀s黍s鼠s述s束s戍s竖s墅s庶s漱s薂x薃h楕t楖j楡y荟h薉h薋c芗x薍w薎m薐l薒c薓s薕l薖k榐z薗y薘d薙t薚t薝z薞s槀g薠f薡d薢x薣g薥s槁g薫x薬y薭b薱d槗q薲p薳y薴n薵c薶m薸p荠j薻z薼c薽z薾e薿n藀y藂c藃x藄q藅f藆j藇x藈k藊b藋d藌m荩j槸y藑q藒q藔l藖x藗s樐l藘l藙y藚x藛x艺y藞l藟l藠j藡d藢z藣b藦m藧h藨b薮s藫t藬t藭q藮q藯w藰l樼z樿s藲o藳g藴y藵b苈l藸z橩q恕s刷s耍s摔s衰c甩s栓s拴s霜s双s爽s谁s水s睡s吮s瞬s顺s舜s说s朔s斯s撕s嘶s思s私s司s死s肆s寺s嗣s四s似s饲s巳s松s颂s送s宋s讼s诵s搜s艘s嗽s酥s俗s素s檪l速s粟s僳s塑s溯s宿s诉s蒜s算s檾q櫈d櫐l虽s隋s随s髓s碎s穗s遂s隧s祟s櫢s梭s櫴l唆s索s锁s所s櫿y塌t他t她t欃c塔d蔺l欟g欦q藽q藾l歞e蘁w蘂r歬q殏q蕲q蘈t蘉m蕴y蘌y蘍x蘎j蘏j蘐x殭j毥x蘔j蘕p蘖n蘘r蘙y藓x蘛y蘜j蘝l蔹l蘟y蘠q蘡y茏l蘣t蘤h蘥y蘦l蘨y蘪m蘫l蘬g蘮j蘯d蘰k蘱l蘲l蘳h蘴f蘵z蘶w蘷k汦z汻h沕m蓠l蘻j蘽l蘾h虀j虁k泈z泘h虃j泿y虄s虅t洜l虇q虈x虉y虊l虋m虌b虒s虓x虖h虗x虘c虙f虚x虝h虠j浰l虡j虣b浲f虤y虥z虦z涖l涶t虩x虪s蹋t踏t胎t淃j抬t泰t酞t太t汰t坍t贪t檀t痰t潭t谭t谈t坦t毯t袒t碳t探t炭t渜n塘t搪t堂t棠t膛t唐t糖t倘c躺t淌t趟t掏t滔t萄t桃t逃t淘t陶t讨t套t特t腾t疼t誊t梯t湕j剔t踢t锑t提d题t蹄t啼t体t替t嚏t惕t涕t剃t湲y天t添t填t田t甜t恬t舔t腆t挑t眺t跳t贴t铁f帖t虭d虯q虰d虳j虴z虵s溳y滖s滜z滵m虸z蚃x蚄f漐z蚆b蚇c蚈q蚉w蚎y蚏y蚐j蚑q蚒t漹y潎p蚖y潣m蚗j蚘h蚙q蚚q蚛z蚞m蚟w蚡f蚢h蚥f蚦r蚫b澚a蚮d蚲p蚳c蚷j蚸l蚹f蚻z蚼g蚽p蚾p蛁d蛂b蛃b蛈t蛌t蛍y蛒g蛓c蛕h蛖m蛗f蛚l蛜y蛝x蛠l濵b蛡y蛢p蛣j濸c蛥s蛦y蛧w蛨m蛪q蛫g蛬q瀀y瀇w瀐j蛵x蛶j蛷q蛱j蜕t蛼c蛽b蛿h蜁x蜄s蜅f蚬x蜋l蜌b蜎y蜏y蜐j蜑d蜔d蜖h汀t廷t停t亭t庭t挺t艇t通t桐t酮t瞳t同t铜t彤t童t桶t灜y捅t筒t痛t偷t投t头t灩y透t凸t突t徒t途t屠t土t吐t兔t湍t推t颓t腿t褪t退t吞t屯t臀t拖t托t炏y鸵t陀t驮d炓l炗g驼t妥t拓t唾t挖w哇w蛙w娃w瓦w袜w炲t歪w外w豌w玩w顽w烷w完w碗w挽w晚w皖w惋w宛w烞p婉w腕w汪w王w烮l亡w枉w往w旺w望w忘w妄w威w蜙z蜛j蜝q蜟y蜠j蜤s蜦l蜧l蜨d蜪t蜫k蜬h蜭h蜯b蜰f蜲w蜳d蜵y蜶s蜸q蜹r蜺n蜼w蜽l蝀d蝁e蝂b蝃d蝄w蝅c焭q蝆y焲y蝋l蝍j蝏t焷p蝐m蝑x蝒m蝔j蝘y蝚r蝛w蝜f蝝y蝞m蝟w蝡r蝢x虾h蝧y蝨s蝩c蝪t蝫z蝬z蝭d蝯y蝱m蝲l蝳d蝵q蝷l蜗w蝹y蝺q蝿y螀j螁b蛳s螆c螇x螉w螊l螌b螎r螏j螑x螒h螔y螕b螖h螘y螙d螛h螜h螝g螠y螡w萤y螣t螤z巍w微w危w韦w违w桅w唯w惟w萎w委w尾w未w蔚w味w畏w胃w喂w魏w位w渭w谓w尉w慰w卫w瘟w蚊w文w燰w吻w紊w嗡w翁w爁l我w斡w握w沃w巫w钨w诬w屋w爗y爙r梧w吾w武w爠q五w捂w午w爥z舞w伍w侮w戊w雾w晤w物w勿w悟w误w昔x熙x析x西x硒x矽x晰x嘻x吸x牔b锡x螥c螦s螧q螩d螪s螮d螰l螱w螲d螴c螶q螷p螸y螹c蝼l螼q螾y螿j蟁w蟂x蟃w蛰z蟅z蝈g蟉l蟌c蟍l蟎m蟏x蟐c蟔m蟕z蟖s蟗q蟘t蟙z蟚p蟜j蟝q蟞b蟟l蟡g蟢x虮j蟤z蟦f蟧l蟨j蟩j蟫y狔n狚d蛲n猍l蟰x猏j蟱w蟳x蟴s蟵c蛏c蟷d蟸l蟺s蚁y蟼j蟽d蟿q蠀c蠁x蠂s蠄q蝇y虿c蠇l蠈z蠉x蠋z猽m蠌z蠍x蠎m蠏x蛴q蝾r蠒j蚝h獉z蠗z蠘j蠙b蠚h蠜f蠝l蠞j蠠m蛎l稀x息x希x悉x膝x夕x惜x熄x烯x溪x汐x犀x檄x袭x席x媳x喜x铣x洗x獹l隙x瞎x匣x霞x辖x暇x下x夏x掀x锨x先x仙x鲜x咸x贤x舷x闲x涎x嫌x显x险x腺x馅x陷x限x相x香x箱x襄x湘x乡x翔x祥x详x想x响x享x项x巷h橡x像x象x硝x霄x削x哮x销x消x宵x淆x蠤q蠥n蠦l蠧d蠨x蠩z蠪l蠫l蠬l蠭f蠮y蠯p蠰n蠳y蠴s蠵x瑊j瑘y蠸q瑦w瑸b璊m蠽j蠾z蠿z璠f衃p衆z衉k衋x衎k衏y衐q衑l衒x衕t衘x衦g衧y衪y甇y衱j衳z衴d衵r衶z衸j衺x衻r衼z袀j袃c袆h袇r畄l袉t袊l袌p袎y袏z袐b袑s袓j袔h袕x袗z袘y袙p畞m袚f畧l袝f袟z袠z袡r袣y袥t袦n袧g袨x袩z袪q小x孝x校j肖x疜x笑x效x楔x些x歇x疪b蝎x鞋x邪x斜x谐x械x卸x蟹x懈x痟x谢x屑x薪x痭b芯x锌x欣x辛x新x忻x心x信x衅x星x腥x猩x惺x刑x型x形x邢x行h醒x杏x性x姓x兄x胸x瘹d匈x雄x癁f熊x休x羞x朽x嗅x癊y锈x秀x袖x墟x戌q需x须x徐x许x蓄x酗x癦m旭x序x畜c絮n婿x轩x喧x宣x旋x玄x袬y袮m袯b袲c袳c袵r袶p袸j袹b袺j袻e袽r袿g裀y裃k裄h裇x裈k裋s裌j裍k裐j裑s裓g裖z裗l裚j裛y装z裞s裠q裦b裧c裩k裪t裫y裬l裭c裮c裯c裲l裵p裶f裷y裺y裻d制z裿y褀q褁g褃k褄q褅t褆t褈c褉x褋d褌k褍d褎x褏x褑y褔f褕y褖t褗y褘h褜p褝d褞y褟t褠g褢h褣r褤y褦n褧j褨s褩b褬s褭n褮y褯j褱h裢l褵l褷s选x眩x靴x薛x穴x雪x血x熏x循x旬x询x驯x巡x殉x汛x训x讯x逊x迅x押y鸦y鸭y丫y芽y牙y蚜y崖y衙y涯y雅y盶y讶y焉y阉y淹y盐y研y蜒y延y言y颜y阎y炎y沿y奄y掩y眼y衍y堰y燕y雁y唁y宴y谚y验y殃y央y鸯y秧y佯y羊y洋y氧y仰y养y漾y邀y腰y妖y褛l褹y褺d亵x褼x褽w褾b褿c襀j襃b襅q襆f襇j襈z襉j襊c襋j襌d襍z襎f襏b襑x襒b襓r襔m襕l襗z襘g襙c襚s襛n襜c裣l裆d襡s襢z襣b褴l襥f襧z襨t襩s襫s襭x襮b襰l襱l襳x襴l襵z襶d襷t襸z襹s襺j襼y襽l襾y覀y覂f覄f覅f覇b覉j瞈w覊j瞏q覌g覍b覎y瞔z覐j覑p覒m覔m覕p覗s觇c覙z覚j覛m覜t覝l覞y覟z覠j觋x遥y谣y姚y咬y舀y要y矊m耀y椰y噎y耶y野y冶y也y页y掖y腋y夜y液y一y壹y医y揖y铱y依y伊y衣y颐y夷y遗w移y胰y疑y沂y宜y姨y彝y椅y倚y已y乙y矣y以y抑y易y邑y屹g役y臆y逸y肄y疫y亦y裔y意y毅y益y溢y诣y议y谊y译y翼y翌y茵y因y音y阴y姻y吟y银y淫y寅y饮y尹y引y隐y覢s覣w覤x覥t觎y覧l矲b覨e覩d觊j覭m覮y觏g覰q覱z觐j観g覴d覵j覶l觑q覸j硡h硢y覻q覼l覾s觌d觃y觍t觕c觗z觘c觙j觛d觝d觟h觠q觡g觢s觤g觧j觨h觩q觪x觬n觭j觮j觰z觱b觲x觞s觵g觯z觷x觹x觺y觻l觼j觽x觾y觿x訁y訄q訅q訆j訉f訋d讧h訍c訏x讦j訑y訒r訔y讪s訙x訚y印y英y鹰y迎y赢y盈y影y硬y映y臃y庸y雍y踊y蛹y咏y碐l泳y永y恿y勇y用y幽y悠y尤y由y邮y铀y油y游y酉y碻q有y碽g友y磂l右y釉y诱y又y幼y迂y淤y于y盂y榆y虞y愚y舆y余y俞y逾y鱼y愉y渝y磭c隅y予y磰s雨y禹y宇y语y羽y玉y域y礃z芋y郁y遇y喻y峪y愈y育y誉y訞y訠s訡y讷n訦c訧y訨z訩x訫x訬c訮y訯s訰z訲y訳y訵c诃h訷s訸h訹x诊z注z证z訽g訿z詀z诂g詂f詃j詄d詅l诋d詇y詊p詋z詌g詍y讵j詏y诈z詑t诒y詓q诏z詖b礶g祍r祘s诎q詙b詚d诅z詜t詝z詟z诩x祱s詤h詥h祶d詧c詨x詪h诟g禈h诠q詯h诘j祯z詴w诜s詶c詷t詸m詻l诙h詽y詾x诖g誀e浴y寓y裕y预y豫y驭y鸳y冤y元y垣y袁y原y援y辕y猿y源y远y苑y愿y怨y院y曰y越y跃y钥y月y阅y耘y云y郧y陨y允y运y酝y韵y孕y匝z砸z杂z栽z哉z宰z载z再z在z赞z赃z葬z遭z糟z凿z藻z早z澡z蚤z躁z噪z造z皂z灶z燥z责z贼z怎z增z憎z曾c赠z喳c秢l渣z札z轧g誁b誂t誃y诔l诛z诓k誈w誋j志z誏l誐e诳k诶a誔t誖b誗c誙k诮q誛q誜s誝a誟x誢x诰g誧b誩j誫z説s読d誮h誯c誱j誳q誴c誵x谇s誷w誸x誺c誻t誾y諀p諁z谄c諃c谆z諅j諆j諈z诿w諊j諌d诤z諎z诹z諐q诼z諓j諔c諕x谂s諘b諙h諚b谀y諝x稬n谝p諟s諠x諡s诨h諣h稸x穉z铡z闸z眨z榨z咋z穙p乍z炸z摘z斋z穠n宅z窄z寨z瞻z詹z粘n沾z辗z展z蘸z站z湛z樟z章z彰z漳z掌z杖z丈z账z仗z瘴z障z招z昭z找z沼z赵z照z罩z兆z肇z召s遮z折s哲z辙z者z锗z蔗z这z浙z珍z斟z真z甄z砧z臻z贞z针z枕z疹z震z振z镇z阵z蒸z征z怔z整z拯z正z政z谔e諥z谛d諨f諩p諪t谏j諬q谕y谘z諯z諰x諲y谙a諴x諵n谌c诸z諹y諻h谖x窧z窽k谒y竌c竨d竪s诌z謆s謈b謉k謋h謌g謍y謏x竸j谧m謑x謒q謓c谑x謕t谡s謘c谥s謜y謞h謡y謢l謣y謤b謥c謧l谟m謩m謪s谪z謭j謮z謯j謰l謱l讴o謴g謵x謶z謷a謸a謺z謻y謼h謽j謿c譀h譂c譃x譄z譅s郑z芝z枝z支z吱z蜘z知z肢z脂z汁z之z直z植z殖s值z址z指z止z趾z旨z至z置z峙s智z秩z稚z质z炙z痔z治z窒z中z盅z筳t筴c忠z钟z衷z重c仲z舟z箈t周z州z洲z粥y轴z肘z帚z咒z宙z骤z珠z株z蛛z猪z逐z竹z煮z拄z主z著z柱z助z蛀z贮z铸z譆x譇z譈d譊n譋l譌e谲j譐z譑j譒b譓h譔z譕w谮z譗z谯q譛j譝s譞x譠t譡d譢s譣x譤j譥j譧z譨n譩y谵z譭h譮h譱s譲r譳r篒y譶t譸z譹h譺a譻y譼j譾j譿h讁z讂j讃z讄l讅s讆w讇c讉y讋z篻p讌y讍e雠c讏w讐c讑y讔y谶c讗x讘n讙h讛y谠d讝z谳y讟d讬t讱r讻x诇x诐b诪z谉s谞x住z祝z驻z抓z爪z拽z转z撰z赚z篆z撞z椎c锥z追z赘z捉z拙z卓z桌z琢z茁z酌z啄z着z灼z咨z资z姿z滋z淄z孜z紫z仔z籽z滓z自z字z鬃z棕z踪z宗z邹z走z奏z揍z租z足z卒c族z祖z阻z钻z纂z嘴z醉z最z罪z尊z遵z昨z左z佐z柞z做z作z坐z座z谸q谹h谺x谻j谼h谽h谾h谿x豀x豂l豃h豄d豅l豋d豍b豏x豑z豒z豓y籋n豖c豗h豘t豙y豛y豜j豝b豞h豟e豠c豣j豤k豥g豦j豧f豨x豩b豭j豮f豯x豰h豱w豲h豴d豵z豷y豻a豼p豽n豾p豿g貀n貁y貃m貄s貆h貇k貈h貋a貎n貏b貐y貑j貒t貕x貖y貗j貙c貚t貛h貜j粎m粓g貟y粡t貣d貤y貦w貭z亍c丌q兀w丐g廿n卅s丕p丞c鬲g孬n噩e丨g禺o丿p匕b乇z夭a爻y卮z氐d囟x胤y馗k毓y鼗t丶z亟j鼐n乜m亓q孛b嘏g厝c厥j靥y赝y匚f叵p糂s匾b赜z卦g卣y刂d刈y刎w刳k剌l糛t剞j剡s剜w蒯k剽p劂j劁q劐h劓y冂j罔w亻r仃d仉z仨s糿g仫m仞r仳p伢y佤w仵w伉k紤j佞n佧k攸y佚y佝g貮e贳s貱b赀z貵p貹s贶k絾c綛k綥q緛r繏x繑q繝j纄p纋y纕x纚l缿x翄c翭h翺a耛s聎t肒h肸x胋t胑z胻h脦t脺c脻j腟c臒w臤q臱m舋x舿k艕b芀t芁q芖z苚y茟y茰y莍q菻l萂h萿k葔h蔝m蔨j蔴m蕮x薆a莶x藱h藼x蘀t蘃r蘒q蘓s蘹h虂l虆l虨b虶y虷h蚅e蚔q蚭n蚿x蛯e蝖x螐w螚n蟭j蠷q蠺c衂n衭f袛d袾z襂s襐x覫p覹w觓q訤x詉n詗x詺m誎c誽n諽g諿x謃x謟t謲c譍y譪a譵d貥h乩j仡g斛h颢h銮l乊h乥h夫f兓j兙s兛q兝g兞h兣g兺b囻g坟f妢f婲h孒j尅k屗u岎f帉f幥z幩f弅f徚u怭b怾g慙c昐f晲n曢u朆f朌f朑u朩p枌f梤f乐l橨f檂n欂b毜c毮s洂y涜d颍y澵z炃f炍p炾h烪u焑y焺s燌f燓f燞u燢x獖f瓧s瓰f瓱m瓲t瓼l榛z彳c夔k璞p丟d並b丬p乂y乄s亙g亞y亠t亳b仝t伋j伕f伲n伽g佇z佈b佔z佗t佘s佟t佪h佴e併b佶j佻t佼j佾y侃k來l侉k侏z侑y侔m侖l侶l侷j俅q俎z俑y俚l俜p俟q俠x俳p俸f俾b倆l倉c個g倌g倏s們m倖x倜t倣f倥k倨j倩q倫l倬z倭w倮l偃y偈j偉w偌r偎w偕x側c偵z偺z偽w傖c傘s備b傢j傭y傳c傴y債z傷s傺c傾q僂l僅j僇l僉q僑q僕p僖x僥j僦j僬j僭j僮t僱g價j儀y儂n億y儅d儆j儇x儉j儋d儐b儔c儕c儘j優y儲c儷l儸l儺n儻t儼y兌d兒e兕s兗y內n兩l兮x冊c冑z冖m冢z冥m冪m冫b冱h冼x冽l凇s凈j凍d凫f凱k凵k凼d刖y別b刪s剄j則z剋k剎c剛g剝b剮g剴k創c剷c劃h劇j劉l劊g劌g劍j劑j劬q劭s劻k劾h勁j勐m動d勗x務w勛x勝s勞l勢s勣j勦j勰x勱m勵l勹b勻y匍p匏p匐f匭g匯h匱k區q協x卟b卩j卹x卻q厙s厭y厲l厴y厶s參c叟s叢c叨d叩k叱c叻l吆y吋c吒z吖y吡b吣q吲y吳w吶n呂l呃e呋f呎c呔d呤l呦y呱g呲c呶n呷g咂z咄d咔k咚d咝s咣g咤c咦y咧l咩m咪m咫z咭j咻x咼w咿y哂s哌p哏g哐k哚d哞m員y哧c哳z哽g哿g唄b唅h唏x唑z唔m唣z唧j唪f唰s唳l唷y唸n唼s唿h啁z啉l問w啐c啕t啖d啞y啟q啣x啵b啶d啷l啻c啾j喁y喃n喈j喋z喏n喑y喔o喙h喚h喟k喪s喫c喬q單d喱l喵m嗄a嗆q嗇s嗉s嗌y嗍s嗎m嗑k嗒d嗔c嗖s嗚w嗝g嗟j嗤c嗥h嗦s嗨h嗪q嗬h嗲d嗵t嗶b嗷a嗾s嘀d嘁q嘈c嘌p嘍l嘔o嘖z嘜m嘞l嘟d嘣b嘧m嘩h嘬c嘭p嘮l嘯x嘰j嘵x嘸f嘹l噁e噌c噍j噓x噔d噗p噘j噙q噠d噢o噤j噥n噦y噫a噯a噱j噲k噴p噸d噹d噻s噼p嚀n嚅r嚆h嚇h嚌j嚐c嚓c嚙n嚥y嚦l嚨l嚮x嚯h嚳k嚴y囀z囁n囂x囅c囈y囉l囌s囑z囔n囗w囝j囡n囪c囫h囹l囿y圄y圇l圉y圊q國g圍w園y圓y圖t圜h圩w圪g圮p圯y圳z圻q坂b坌b坨t坩g坫d坭n坰j坳a坶m坻c坼c垅l垌d垓g垠y垡f垤d垴n埏s埒l埕c埝n埡y埤p埭d埯a埴z執z埸y埽s堀k堅j堇j堊e堋b堍t堙y堞d堠h報b場c堿j塄l塊k塋y塍c塒s塗t塢w塤x塥g塬y塵c塹q塾s墀c墁m墉y墊d墑d墚l墜z墮d墻q壅y壇t壑h壓y壙k壚l壞h壟l壢l壩b壯z壺h壽s夂z夙s夠g夢m夤y夥h夼k夾j奐h奕y奘z奚x奧a奪d妁s妃f妍y妗j妝z妞n妣b妤y妯z妲d妳n妾q姊z姍s姒s姘p姝s姣j姦j姪z姹c娉p娌l娑s娓w娛y娣d娼c婀e婊b婕j婢b婦f婧j婭y婷t婺w媛y媧w媯g媲p媸c媼a媽m媾g嫖p嫗y嫘l嫜z嫠l嫣y嫦c嫫m嫵w嫻x嬈r嬉x嬌j嬖b嬗s嬙q嬝n嬡a嬤m嬰y嬲n嬴y嬸s孀s孃n孑j孓j孚f孢b孥n孫s孱c孳z學x孿l宀m宄g宓m宕d宥y宮g宸c寢q寤w實s寧n審s寫x寬k寮l寰h寵c寶b將j專z尋x對d尕g尜g尢w尥l尬g尷g尻k屄b屆j屐j屙e屜t屣x層c屬s屮c屺q岈y岌j岍q岐q岑c岙a岜b岡g岢k岣g岧t岫x岬j岱d岵h岷m峁m峋x峒d峴x島d峽x崆k崍l崗g崙l崛j崞g崠d崢z崤x崦y崧s崮g崴w崽z崾y嵇j嵊s嵋m嵒y嵩s嵫z嵬w嵯c嵴j嶁l嶂z嶄z嶇q嶗l嶙l嶷n嶸r嶺l嶼y嶽y巋k巑c巔d巖y巛c巰q巹j巽x帑t帔p帙z帥s帳z帶d帷w幃w幄w幔m幘z幛z幞f幟z幡f幣b幪m幫b幬c幹g幺y幾j庀p庂z庋g庖p庠x庥x庫k庳b庹t庾y廁c廂x廄j廈s廑q廒a廚c廛c廝s廡w廢f廣g廨x廩l廬l廱y廳t廴y廾g弁b弈y弋y弒s弔d弩n弭m弳j張z強q弼b彀g彆b彈d彊q彌m彎w彐x彖t彗h彘z彙h彡s彥y彫d彷f彿f徂c徇x徉y後h徑j徙x徜c從c徠l徨h復f徫w徬p徭y徵z徹c徼j忉d忐t忑t忒t忖c忝t忡c忤w忪s忭b忮z忸n怊c怍z怏y怙h怛d怡y怦p怩n怫f怵c恁n恂x恆h恙y恚h恝j恣z恥c恧n恪k悃k悅y悌t悒y悖b悚s悛q悝k悱f悴c悵c悶m悻x悽q惆c惘w惙c惝t惡e惱n惲y惴z惻c愀q愆q愍m愎b愕e愛a愜q愣l愨q愫s愴c愾k慄l慇y慊q態t慍y慘c慚c慝t慟t慣g慫s慮l慵y慶q慼q慾y憂y憊b憐l憑p憔q憚d憝d憧c憩q憫m憬j憲x憶y憷c懇k應y懋m懌y懍l懔l懞m懟d懣m懨y懲c懵m懶l懷h懺c懼j懾s懿y戀l戇g戉y戔j戕q戛j戟j戡k戢j戤g戥d戧q戩j戰z戲x戶h戽h戾l扃j扈h扌s扐c扠c扢g扺z抃b抆w抎y抴y抻c拊f拋p拗a拮j拶z挈q挲s挶j挹y挾x捃j捋l捨s捩l捫m捭b捱a捺n掃s掄l掊p掎j掙z掛g採c掬j掭t掮q掰b掾y揀j揄y揆k揎x揚y換h揞a揠y揲d揶y揸z揹b搆g搋c搌z損s搖y搗d搛j搟x搠s搡s搦n搴q搶q搿g摁e摑g摒b摜g摞l摟l摭z摯z摳k摶t摺z摻c撈l撓n撙z撚n撟j撣d撥b撫f撲p撻t撾z撿j擁y擄l擇z擊j擐h擔d擗p擘b據j擠j擢z擣d擤x擬n擯b擰n擱g擲z擴k擷x擺b擻s擼l擾r攄s攆n攉h攏l攔l攖y攙c攛c攜x攝s攢z攣l攤t攥z攬l攮n攴p攷k敉m敕c敗b敘x敫j敵d斂l斃b斐f斕l斫z斬z斷d於w旂q旃z旄m旆p旌j旎n旒l旖y旮g旯l旰g昀y昃z昇s昊h昕x昝z昱y昴m昶c晁c時s晉j晏y晗h晝z晞x晟c晡b晷g暄x暈y暉h暌k暘y暝m暨j暫z暱n暸l暹x暾t曄y曆l曇t曉x曖a曛x曜y曠k曩n曬s曷h書s會h朊r朐q朕z朦m朧l朮s杇w杈c杌w杓b杞q杪m東d杲g杳y杵c杷b杼z枇p枋f枘r枙e枰p枳z枵x枸g柁d柃l柘z柚y柝t柢d柩j柰n柵s栝g栩x栲k栳l桀j桁h桄g桉a桊j桎z桕j桫s桴f桷j桿g梃t梏g梓z梔z條t梟x梩q梵f棄q棖c棗z棟d棣d棧z棰c棲q棹z棼f椋l椏y椐j椴d椹s楂c楊y楓f楗j楝l楠n楣m楦x楨z楫j業y楮c楱c極j楸q楹y榀p榍x榕r榘j榙t榧f榪m榫s榭x榮r榱c榻t榿q槃p槊s槌c槍q槎c槓g槧q槨g槭q槲h槳j槿j樁z樂l樅c樑l樓l樗c樘t標b樞s樨x樵q樸p樹s樺h樽z樾y橄g橈r橋q橐t橘j橛j機j橢t橫h檁l檉c檎q檔d檗b檜g檠q檢j檫c檯t檳b檸n檻j櫓l櫚l櫛z櫝d櫟l櫥c櫧z櫨l櫪l櫬c櫳l櫸j櫺l櫻y欄l權q欏l欒l欖l欞l欷x欹q歃s歆x歎t歐o歙s歟y歡h歲s歷l歿m殂c殄t殍p殘c殞y殤s殪y殫d殮l殯b殲j殳s殺s殼k毀h毂g毆o毌g毪m毬q毳c毹s毽j毿s氅c氆p氈z氌l氍q氕p氘d氙x氚c氡d氣q氤y氪k氫q氬y氳y氵s氽c氾f汆c汊c汍w汎f汔q汜s汨m汩g汴b汶w決j沅y沆h沌d沐m沒m沓d沔m沖c沭s沱t沲t泐l泓h泔g泖m泗s泠l泫x泮p泯m泱y洄h洇y洌l洎j洙z洚j洧w洩x洫x洮t洳r洵x洶x洹h浜b浞z浠x浣h浬h浯w浹j浼m涇j涑s涓j涔c涫g涮s涼l涿z淅x淇q淒q淙c淚l淝f淞s淠p淥l淦g淨j淪l淵y淶l淼m渙h渚z減j渥w渦w渫x測c渲x渾h湄m湊c湎m湓p湔j湞z湟h湧y湫j湮y湯t溆x溏t準z溘k溝g溟m溥p溧l溫w溱q溲s溴x溷h溻t溼s溽r滂p滄c滅m滎x滏f滕t滟y滬h滯z滲s滷l滸h滹h滾g滿m漉l漕c漚o漢h漣l漤l漩x漪y漬z漭m漯l漲z漶h漸j漿j潁y潑p潔j潛q潟x潢h潤r潯x潰k潲s潴z潷b潸s潺c潼t澀s澆j澇l澉g澌s澍s澗j澠m澤d澧l澩x澮k澱d澶c澹d濂l濃n濉s濕s濘n濛m濞b濟j濠h濡r濤t濫l濮p濯z濰w濱b濺j濼l瀅y瀆d瀉x瀋s瀏l瀕b瀘l瀚h瀛y瀝l瀟x瀠y瀣x瀧l瀨l瀰m瀲l瀵f瀹y灃f灄s灑s灕l灘t灝h灞b灣w灤l灬h災z炅g炫x炱t為w炻s烀h烊y烏w焐w焓h無w焯c焱y煅d煉l煊x煒w煙y煜y煢q煦x煨w煩f煬y煲b煳h煸b煺t熒y熗q熘l熠y熨y熱r熳m熵s熹x熾c燁y燄y燉d燐l燒s燔f燙t營y燠y燦c燧s燬h燭z燮x燴h燹x燼j燾d爐l爛l爝j爨c爭z爰y爺y爾e爿p牆q牒d牖y牘d牝p牠t牦m牪y牮j牯g牴d牽q牿g犄j犍j犏p犒k犛l犟j犧x犭q犰q犴a犸m狀z狁y狃n狍p狎x狒f狨r狩s狳y狴b狷j狹x狺y狻s狽b猁l猊n猓g猗y猙z猝c猞s猢h猥w猬w猱n猶y猷y猸m猹c猻s獄y獅s獍j獐z獒a獗j獠l獨d獪k獫x獬x獯x獰n獲h獵l獷g獸s獺t獻x獼m獾h玀l玆z玎d玟m玢b玨j玳d玷d珀p珂k珈j珉m珙g珞l珥e珧y珩h珮p現x琊y琚j琛c琥h琦q琨k琪q琬w琮c琰y琺f琿h瑁m瑕x瑗y瑙n瑛y瑜y瑣s瑤y瑩y瑪m瑭t瑯l瑾j璀c璇x璉l璋z璐l璜h璣j璦a璧b璨c璩q璺w璽x瓊q瓏l瓖x瓚z瓞d瓠h瓴l瓿b甍m甏b甑z甓p甙d產c甦s甬y町d甾z畀b畈f畋t畎q畚b畛z畝m畫h畬s異y畹w畼c畿j疃t疊d疋y疒n疔d疝s疣y疬l疰z疳g疴k疸d疿f痂j痃x痄z痍y痖y痙j痣z痤c痦w痧s痲m痳m痼g痿w瘀y瘃z瘊h瘌l瘍y瘐y瘓h瘕j瘙s瘛c瘞y瘠j瘡c瘢b瘥c瘭b瘰l瘳c瘺l瘼m癀h療l癃l癆l癇x癍b癔y癖p癜d癟b癡c癢y癥z癩l癬x癭y癮y癯q癰y癱t癲d發f皈b皎j皓h皙x皚a皤f皰p皴c皸j皺z皻z盍h盜d盞z盡j監j盤p盥g盧l盪d盱x盹d眄m眇m眈d眍k眚s眢y眣d眥z眭s眵c眸m睇d睏k睚y睜z睞l睢s睥b睨n睪y睽k睿r瞀m瞌k瞍s瞑m瞞m瞟p瞠c瞢m瞭l瞰k瞵l瞼j瞽g瞿j矇m矍j矓l矚z矜j矧s矬c矯j矸g砉h砑y砘d砜f砝f砟z砣t砥d砩f砬l砭b砲p砹a砼t硃z硇n硌g硎x硐d硤x硨c硪w硭m硯y碇d碓d碚b碣j碥b碩s碲d碹x碼m磉s磔z磙g磚z磣c磧q磬q磯j磲q磴d礅d礎c礓j礙a礞m礤c礦k礪l礫l礱l礴b礻s祀s祅y祇q祉z祐y祓f祕m祗q祚z祛q祜h祠c祧t祺q祿l禊x禍h禎z禚z禦y禧x禮l禰m禱d禳r禿t秈x秕b秣m秫s秭z稂l稃f稅s稆l稈g稔r稜l稞k稟b種z稱c稷j稹z穀g穌s積j穢h穩w穫h穰r穸x穹q窀z窆b窈y窕t窠k窨x窩w窪w窬y窮q窯y窳y窶j窺k竄c竅q竊q竦s競j竺d竽y笄j笈j笊z笏h笙s笞c笠l笤t笥s笪d笫z笮z笱g笳j笸p笻q筆b筌q筍s筘k筠j筢p筮s筲s筵y筻g箄b箇g箋j箏z箐q箜k箝q箬r箴z箸z節j篁h範f築z篋q篌h篚f篝g篠x篤d篥l篦b篩s篪c篳b篼d篾m簀z簋g簌s簍l簏l簑s簟d簡j簣k簦d簪z簫x簷y簸b簽q簾l籀z籃l籌c籐t籜t籟l籠l籤q籥y籩b籪d籬l籮l籲y粑b粞x粢z粧z粲c粵y粼l粽z糅r糇h糈x糌z糍c糗q糝s糢m糧l糨j糰t糲l糴d糶t糸m糾j紀j紂z約y紅h紇h紈w紉r紋w納n紐n紓s紕p紗s紙z級j紜y紡f紮z細x紱f紲x紳s紹s紺g紼f紿d絀c終z絃x組z絆b絎h結j絛t絞j絡l絢x給g絨r統t絲s絳j絹j綁b綃x綆g綈t綏s綑k經j綜z綞d綠l綢c綣q綦q綬s維w綮q綰w綱g網w綴z綵c綸l綹l綺q綻z綾l綿m緇z緊j緋f緒x緗x緘j緙k線x緝j緞d締d緡m緣y緦s編b緩h緬m緯w緱g緲m練l緶b緹t緻z縈y縉j縊y縐z縑j縚t縛f縝z縞g縟r縣x縫f縭l縮s縯y縱z縳z縴q縵m縶z縷l縹p縻m總z績j繅s繆m繇y繈q繒z織z繕s繚l繞r繡x繢h繩s繪h繫x繭j繯h繰q繳j繹y繼j繾q纈x纊k續x纍l纏c纓y纔c纖x纘z纛d纜l纟s绔k绛j绱s缰j缶f缽b罄q罅x罈t罌y罘f罟g罡g罨y罰f罱l罵m罹l罾z羅l羆p羈j羋m羝d羧s義y羯j羰t羲x羸l羼c羿y翊y翎l習x翕x翡f翥z翦j翩p翮h翳y翹q耄m耆q耋d耑d耒l耔z耖c耜s耠h耢l耥t耦o耨n耩j耬l耱m耵d耷d聃d聆l聒g聞w聯l聰c聱a聲s聳s聵k聶n職z聹n聽t聾l聿y肅s肓h肜c肟w肫z肭n肱g肴y肷q肼j肽t胂s胍g胗z胛j胝z胥x胨d胩k胬n胭y胱g胲h胴d胼p脅x脈m脎s脒m脘w脛j脞c脣c脧j脩x脫t脬p脲n脹z腈j腌a腎s腓f腙z腚d腠c腡l腦n腧s腩n腫z腭e腱j腴y腸c腼m膂l膃w膈g膚f膠j膦l膪c膺y膻s膽d膾k膿n臁l臉l臊s臌g臍q臏b臚l臟z臠l臧z臨l臬n臾y舁y舂c舄x與y興x舉j舊j舐s舖p舛c舡c舢s舨b舫f舭b舯z舳z舴z舸g舺x舾x艄s艉w艋m艏s艚c艟c艣l艦j艨m艫l艮g艱j艴f艷y艸c艽j艿n芄w芊q芎x芏d芑q芘b芙f芟s芡q芤k芨j芩q芪q芫y芮r芰j芴w芷z芻c芾f苊e苒r苓l苕s苘q苜m苠m苡y苣j苤p苧n苴j苷g苻f茆m茇b茈c茉m茌c茍j茗m茚y茛g茜q茭j茯f茱z茲z茳j茴h茺c茼t荀x荃q荇x荊j荏r荑t荬m荸b荻d荼a荽s莊z莒j莓m莘s莛t莞g莠y莢j莧x莨l莩f莪e莰k莺y莼c菀w菁j菅j菔f菖c菘s菝b菟t菡h菥x華h菰g菸y菹z菽s萁q萃c萆b萊l萋q萏d萑h萘n萜t萬w萱x萵w萸y萼e葆b葉y葑f葙x葚r葜q葦w葩p葭j葯y葳w葶t葷h葸x葺q蒈k蒎p蒔s蒗l蒞l蒡b蒴s蒺j蒼c蒽e蒿h蓁z蓊w蓋g蓍s蓐r蓓b蓥y蓦m蓯c蓰x蓼l蓽b蔌s蔔b蔟c蔣j蔥c蔦n蔭y蔸d蔻k蕁q蕃f蕈x蕎q蕓y蕕y蕖q蕙h蕞z蕢k蕤r蕨j蕩d蕪w蕭x蕷y蕹w蕺j蕻h薅h薇w薈h薊j薌x薏y薑j薔q薜b薟x薤x薦j薧h薨h薩s薰x薷r薹t薺j藁g藍l藎j藜l藝y藥y藪s藶l藷s藹a藺l藿h蘄q蘅h蘆l蘇s蘊y蘋p蘗b蘚x蘢l蘧q蘩f蘭l蘺l蘼m蘿l虍h虔q處c虛x虜l號h虢g虧k虬q虺h虻m蚋r蚍p蚓y蚣g蚧j蚨f蚩c蚪d蚯q蚰y蚱z蚴y蚵k蚶h蚺r蛄g蛅z蛉l蛐q蛑m蛘y蛞k蛟j蛩q蛭z蛸s蛺j蛻t蜃s蜆x蜇z蜈w蜉f蜊l蜍c蜓t蜚f蜞q蜢m蜣q蜥x蜩t蜮y蜱p蜷q蜻q蜾g蜿w蝌k蝓y蝕s蝙b蝠f蝣y蝤q蝥m蝦x蝮f蝰k蝸w蝻n蝽c螂l螃p螅x螋s螓q螗t螞m螢y螨m螫s螬c螭c螯a螳t螵p螻l螽z蟀s蟄z蟆m蟈g蟊m蟋x蟑z蟒m蟓x蟠p蟥h蟪h蟬c蟮s蟯n蟲c蟶c蟻y蟾c蠃l蠅y蠆c蠊l蠐q蠑r蠓m蠔h蠖h蠛m蠟l蠡l蠣l蠱g蠲j蠹d蠼q衄n術s衛w衝c衢q衤y衩c衲n衹z衽r衾q衿j袂m袈j袞g袢p袤m袷q裊n裎c裏l裒p裘q補b裝z裟s裡l裥j裨b裰d裱b裼t製z裾j複f褊b褓b褙b褚c褡d褫c褰q褲k褳l褶z褻x襝l襞b襠d襤l襦r襪w襬b襯c襻p覃q見j規g覓m視s覘c覡x覦y親q覬j覯g覲j覷q覺j覽l覿d觖j觚g觜z觥g觫s觳h觴s觶z觸c訂d訃f訇h訊x訌h討t訐j訓x訕s訖q託t記j訛e訝y訢x訣j訪f設s許x訴s診z註z証z訾z詁g詆d詈l詎j詐z詒y詔z評p詘q詛z詞c詠y詡x詢x詣y詩s詫c詭g詮q話h該g詵s詼h誅z誆k誇k誌z認r誑k誕d誘y誚q語y誠c誡j誣w誤w誥g誨h誰s課k誶s誼y調d諂c諄z談t諉w請q諍z諏z諑z諒l論l諗s諛y諜d諞p諢h諤e諧x諫j諮z諱h諳a諶c諷f諸z諺y諾n謀m謁y謂w謅z謇j謊h謎m謐m謔x謗b謙q謚s講j謝x謠y謦q謨m謫z謬m謳o謹j譁h證z譎j譖z識s譙q譚t譜p譟z譫z譯y議y譴q護h譽y讀d讎c讒c讓r讕l讖c讚z谫j豇j豈q豉c豎s豐f豔y豕s豚t豬z豳b豸z貂d貅x貊m貍l貓m貔p貘m貝b負f財c貢g貧p貨h販f貪t貫g責z貯z貰s貲z貳e貴g貶b貸d貺k貼t貾c貿m賁b賂l賃l賄h賅g賆p資z賈j賉x賊z賋j賌g賎j賏y賐j賑z賒s賓b賔b賕q賖s賗c賘z賙z賛z賜c賝c賞s賟t賠p賡g賢x賣m賤j賥s賧d賨c賩c質z賬z賭d賮j賯x賰c賳z賴l賵f賶c賷j賸s賹y賺z賻f購g賽s賾z賿l贀y贂c贃w贄z贅z贆b贇y贈z贉d贊z贋y贌p贍s贎w贏y贐j贑g贒x贓z贔b贕d贗y贘s贙x贚l贛g贜z贠y贲b贻y贽z赅g赆j赇q赈z赉l赍j赑b赒z赓g赕d赗f赙f赟y赥x赧n赨t赩x赪c赬c赭z赮x赯t赱z赲l赳j赸s赹q赺y赻x赼z赽j赿c趂c趃d趄j趆d趇x趈z趉j趌j趍q趎c趏g趐x趑z趔l趖s趗c趘x趚s趜j趝j趞q趠c趡c趤d趥q趧t趨q趩c趪h趫q趭j趮z趯t趰e趱z趲z趵b趷k趹j趺f趻c趼j趽f趿t跁b跂q跄q跅t跆t跇y跈j跉l跍k跎t跏j跐c跒q跓z跔j跕d跖z跗f跘p跙j跚s跛b跜n跞l跠y跡j跢d跣x跤j跦z跧q跩z跫q跬k跭x跰p跱z跲j跴c跶d跷q跸b跹x跻j跼j跽j跾s跿t踀c踁j踂n踃x踄b踅x踆c踈s踉l踍q踎m踑j踒w踓w踔c踕j踗n踘j踙n踚l踛l踝h踟c踡j踣b踥q踦q踧c踫p踬z踭z踮d踯z踰y踱d踲d踳c踴y踵z踶d踷z踸c踹c踺j踻g踽j踾f踿c蹀d蹁p蹂r蹃n蹇j蹉c蹊q蹍n蹐j蹑n蹒p蹓l蹔z蹕b蹖c蹗l蹘l蹙c蹚t蹛d蹜s蹝x蹞k蹟j蹠z蹡q蹢d蹣p蹤z蹥l蹧z蹨n蹩b蹪t蹫j蹮x蹯f蹰c蹱z蹳b蹴c蹵c蹶g蹷j蹸l蹹t蹺q蹻q蹼p蹽l蹾d躀g躂d躃b躄b躅z躆j躈q躉d躊c躋j躍y躎n躏l躐l躑z躒l躓z躔c躕c躖d躗w躘l躙l躚x躛w躜z躝l躞x躟r躠s躡n躢t躣q躥c躦z躧x躨k躩j躭d躮s躰t躱d躵h躶l躷a躸j躹j躻w躽y躾x躿k軀q軁l軃d軄z軆t軇d軉y車c軋y軌g軎w軏y軐x軑d軒x軓f軔r軕s軖k軗s軘t軙c軚d軜n軝q軟r軠k軡q転z軣h軤h軥q軦k軧d軨l軩d軪a軫z軬f軮y軰b軱g軲g軴z軵r軶e軷b軹z軺y軻k軼y軽q軾s軿p輀e輁g輂j較j輄g輅l輆k輈z載z輋s輌l輍y輎s輏y輐w輓w輔f輕q輖z輗n輘l輙z輚z輛l輝h輞w輟c輡k輢y輣p輤q輥g輦n輨g輩b輫p輬l輭r輮r輰y輱x輲c輴c輵g輶y輷h輸s輹f輺z輻f輽f輾z輿y轀w轁t轂g轃z轄x轅y轆l轇j轈c轉z轊w轍z轎j轏z轐b轑l轔l轖s轗k轘h俁y係x轛d轝y轞j轟h轠l轡p轢l倀c轤l轥l轪d轫r轭e轱g轲k轳l轵z轶y轷h轸z轹l轺y轼s轾z辁q辂l辄z辇n辋w辌l辍c辏c辒w辔p辘l傑j辚l辝c辠z辡b辢l辥x辦b辪x辬b辮b辯b農n辳n辵c辶c辷y辸r辻s込y辿c迃y迆y迉q迊b迋w迌t迍z迏d儈k迕w迖d迗e迚d迤y迥j迨d迩e迬z迮z迯t迱y迲k迳j迴h迵d迺n迻y凜l迾l逄p逅h逇d逈j逋b逍x逎q逑q逓d逕j逖t這z逜w逡q連l逥h逦l逧g逨l逪c逬b逭h逯l週z進j逳y逴c逵k逶w逷t逹d逺y勸q遀s遄c遅c遈s遊y運y遌e過g遐x遑h遒q遖n遘g遙y遛l遜x遝t遞d遟c遠y遡s遢t遤h遦g遨a遪c遫c遯d遲c咴h遴l遶r遷q選x遹y遺y遻e遼l遽j遾s邁m邂x邃s還h邅z邆t邇e邈m邉b邊b邋l邌l邏l邒t邔q邕y邖s邘y邙m邛q邜x邝k邟k邠b邡f邥s邧y邬w邭j邰t邲b邳p邴b邶b邷w邸d邺y邼k邾z邿s喹k郀k郂g郃h郄q郅z郆j郇x郈h郉x郍n嗩s郏j郐k郒l郓y郔y郕c郖d郗x郘l郙f嘆t郚w郛f郜g郟j郠g嘗c郢y郤x郥b郦l郪q郫p郮z郯t郱p郳n郵y郶b郷x郹j郻q郾y郿m鄀r鄁b鄃s鄄j鄆y鄇h鄈k鄉x嚕l鄊x鄌t鄎x鄏r鄐c鄑z鄓y嚶y鄔w鄕x鄗h鄚m鄛c鄜f鄝l鄞y鄟z鄠h鄡q鄢y鄣z鄥q鄦x鄨b鄫z鄬w鄭z鄮m鄯s鄰l鄱p鄲d鄴y鄵c鄶k鄷f鄸m團t鄺k鄻l鄼z鄽c鄾y酀y酁c酂z酃l酄h酆f酇z酈l酊d酎z酏y酐g酑y酓y酔z酕m酖d酘d酙z酛k酜f酟t酠q酡t酢c酤g酦f垸y酧c酩m酫c酭y酯z酰x酲c酴t酹l酺p酻z堝g酾s醀w醁l醂l醃y醄t醅p醊z醌k醍t醎x醏d醐h醑x醕c醖y醗p醘k醙s醜c醝c醞y醟y醠a醡z醢h醣t醤j醥p醧y醨l醩z醪l醫y醬j醭b醮j醯x醰t醱p醲n醳y醴l醵j醶y醷y醸n醹r醺x醻c醼y醽l醾m醿m墼j釁x釃s釄m釅y壎x釆b壘l釐l釓g釖d奩l釘d釚q釛h針z釞z釟b釡f釣d釦k釧c釨z釩f釬h釭g釮q釰r釱d釲s釳x釴y釵c釶s釷t釸x釹n釺q釻q釼r釽p釾y鈀b鈁f鈂c鈅y鈆q嬋c鈉n鈊x鈋e鈌j鈍d嬪p孌l鈐q鈒s鈓r鈔c鈕n鈗y鈘y鈛g鈜h鈞j導d鈡z鈢x鈣g鈤r鈥h鈦t鈧k屝f鈫q鈬d鈭z鈮n鈯t鈰s鈲g鈳k鈴l屨j鈶s鈷g鈸b鈹p鈺y鈻s鈼z鈽b鉁z鉂s鉃s鉄t鉆z鉈t鉉x鉊z鉋b鉌h鉍b鉎s鉏c鉐s鉑b鉓c鉕p鉖t鉘f鉙z鉚m鉛q鉜f鉝l鉟p鉠y鉣j鉤g鉦z崳y鉨x鉩x鉪d鉫j鉭t鉮s嵐l鉯y鉰s鉱k鉶x鉷h鉸j鉹c鉺e鉻g鉼b嶝d鉿h嶧y銀y銂z巒l銄x銆m銇l銈j銉y銊x銋r銌z師s銎q銏s銒x銓q銔p銖z銙k銚d銛x銜x銝x銞j銟c銢p銥y銦y銧g銩d銪y銭q銯k銲h銳r銴s廟m銵k廠c銶q銷x銸z銺z銻t鋀t鋁l鋂m鋄w鋅x鋆y鋇b鋈w鋉s鋋c鋌t鋎h鋑j鋕z鋖s鋗x鋘h鋙w鋚t鋛k忄x鋞x鋟q鋠s鋡h鋢l鋣y鋤c鋥z鋦j鋧x鋨e鋩m鋪p鋬p鋭r鋮c鋯g鋰l鋳z鋴z鋵t惚h鋷z鋸j鋹c鋺y愷k鋼g鋽d鋿s錀l慪o錁k錂l錃p慳q憒k錅l憮w錇p錉m錊z錋p錌a錍p錎x錏y懸x錑l錒a錓k錕k錗n錘c錙z錚z錜n錞c錟t錠d錡q錢q錣z錤j錥y錦j錧g錨m錩c錪t捲j錬l錭d錮g錯c錱z録l錳m錴l錵h錷g錸l錹k錺k錻b揮h錼n錽w錾z錿h鍀d鍁x鍄l鍆m鍇k鍈y鍉c鍊l鍋g鍌x鍍d鍏w鍐z鍑f鍒r鍓j鍔e鍕j鍖c鍗t撐c鍘z鍙h鍚y撖h鍛d撘d撢d撳q擋d鍞k鍟s鍠h鍡w鍢f鍣z鍤c鍥q鍦s鍧h鍨k鍩n鍪m鍫q鍬q鍭h鍯c鍱y鍲m鍳j鍵j鍶s鍷k鍸h鍹x攪j鍺z鍻j鍼z鍽b鍾z鍿z鎀x鎁y鎂m鎃p鎄a鎅j鎆q鎇m鎈c鎉d鎊b攵p鎋x鎌l鎍s數s鎏l鎐y鎑y鎒n鎓w鎔r鎕t鎖s鎘g鎙s鎛b鎜p鎝d鎞b鎟s鎠g鎡z鎤h暢c鎦l鎧k鎨s鎩s鎪s鎫w鎬g鎭z鎮z鎯l鎰y鎱y鎲t鎳n鎴x鎵j鎶g鎷m鎸j鎹k鎺h鎻s鎼x鎽f鎾w鎿n鏀l鏁s鏂o鏃z鏄t鏅x鏆g鏇x鏈l鏉s鏊a鏋m鏌m鏍l鏎b鏏w鏐l鏑d鏒s鏔y鏕l鏖a鏗k鏘q鏙c鏚q鏛s鏝m鏞y鏟c鏠f鏡j鏢b鏣s鏤l鏥x枧j枴g柙x鏧l鏨z鏩j鏪c鏫l鏬x鏭x鏮k鏯s鏰b鏱z鏲q鏳z鏴l鏵h鏶j鏸h鏹q鏺p鏻l鏼s鏽x鏾s鏿c鐀g鐁s鐂l鐃n鐄h鐅p鐆s鐇f鐈q鐉q鐊x鐋t鐌x鐎j鐏z鐐l鐑q鐒l鐓d鐔x鐕z鐖j鐗j鐘z鐙d鐚y鐛y鐜d鐝j鐞n鐟z鐠p鐡t鐣c鐤d鐥s鐦k鐧j鐨f鐩s鐪l鐫j鐬h鐭y鐮l鐯z鐰q鐱j鐲z鐴b鐵t榦g鐷y鐸d鐹g鐻j鐽d鐾b鐿y鑀a構g鑁z鑂x槔g鑄z鑅h鑆z鑇j鑈n鑉h鑊h鑋q鑌b樣y鑎g鑏n鑐x鑑j鑒j鑓y鑔c鑕z鑖m鑗l鑘l檑l鑜s鑝p檣q鑞l鑟d鑠s鑡c鑢l鑣b鑤b鑥l鑦x鑧k鑨l鑩e櫃g鑪l鑫x鑬j櫞y鑮b鑯j鑰y鑱c鑲x鑳j鑴x鑵g鑷n鑸l鑹c鑺q鑻p鑼l鑽z鑾l鑿z钀n钁j欽q钃z钄l钅j钆g钇y钊z钋p钌l钍t钏c钐s钑s钔m钕n钖y钗c钚b钛t钜j钣b钤q钪k钫f钬h钭t钯b钰y钲z钴g钶k钷p钸b钹b钼m钽t铄s铈s铉x铊s铋b铌n铍p铎d铏x铐k铑l铒e铓m铔y铕y铖c铗j铘y铙n铚z铛c铞d铟y铠k铢z汙w铤d铥d铦x铧h铨q铩s铪h铫d铮z铯s铳c铴t铵a铷r铹l铻w铼l铽t铿k锂l锃z锆g锇e锉c锊l锍l锎k锏j況k锒l锓q锔j锕a锖q锘n锛b锜q锝d锞k锟k锠c锢g锧z锩j锪h锫p锬t锱z锲q锴k锵q锶s锷e锸c锺z锼s锽h锾h锿a镂l镃z镄f镅m镆m镈b镉g镋t镌j镎n镏l镒y镓j镔b镕r镖b镗t镘m镙l镚b镛y镝d镞z镟x镠l镡t镢j镤p淺q镥l镦d镧l镨p镩c镫d镬h镯z镱y镲c镳b镴l镵c長c镸c镹j溈w镻d镼j镽l門m閂s閃s閄h滌d閆y閇b閈h閉b閊c開k閌k閍b閎h閏r閐s閑x閒x漁y閔m閕x閖l閗d閘z閙n閚z閛p閜x閝l閞b閠r閡h関g閣g閤g閦c閧h閨g閩m閪s閫k閬l閭l閮t閰j閱y閲y潿w閳c閴q閶c閷s閸k閹y閺w閻y閼e閽h閾y閿w闀h闁b濁z闄y闅w闆b闈w闉y闊k闋q闌l闍d闎q闏p闐t濾l闓k瀾l闕q闖c闗g闘d闙q闚k闛t關g闝p闞k闟x闠h闡c闤h闥t闦w闩s闫y闬h闱w闳h闵m闶k闼t闾l闿k阃k阄j阆l阇d阈y阊c阋x阌w阍h阏e阒q阓h阕q阖h阗t阘t阙j阚k阛h阝f阞l阠x烴t阡q阢w阣y阤t阥y阦y阧d阨a阩s阪b阫p阬k阭y阯z阰p阱j阷c阸e阹q阺d阼z阽d阾l陁t陂b陃b陉x陊d煥h陎s陏d陑e陒g陓y陔g陖j陗q陘x陙c陚w陜x陝s陞s陟z陠p陣z陦d陧n陫f燈d燜m陭q陮d爍s陰y陱j陲c陳c陴p陸l陹s険x陻y陼z陽y陾r陿x隀c隁y隂y隃y隄d隇w隈w隉n隊d隌a牾w隍h階j隑g隒y隓h犋j隕y隖w隗k隚t際j犖l隝d隞a犢d隟x隠y隡s隢r隣l隤t隥d隦p隨s隩a險x隬n隭e隮j隯d隰x隱y隲z隴l隵x隷l隸l隹z隺h隻z隼s隽j隿y雂q雃j雈h雉z雊g雋j雎j雐h雑z雒l雓y雔c雖s雗h雘h雙s雚g雛c雜z雝y雞j雟g雡l離l難n雤y雥z雦c雧j雩y雫n雬f獎j雭s雮m雯w雰f雱p雲y雳l雴c雵y雸a獮x電d雼d雽h雿d霁j霂m霃c霅z霆t霈p霊l霋q霌z霎s霏f霐h霑z霒y霔s霕t霗l霘d霙y霚w霛l霝l霟h霠y霡m霢m霤l霥m霦b霧w霨w霩k霪y霫x霬y霭a霮d霯t霰s霱y霳l霴d霵j霶p霷y霺w霼x瑋w霽j霾m霿m靀m靁l靂l靃h靄a靅f靆d靇l靈l靉a靊f靋l靌b靍h靎h靏h靐b璁c靑q靓j靔t靕z靗c靘q靚j靜j靝t靟f靣m靤p靦m靧h靨y靪d靫c靬j靭r靮d靯d靰w靱r靲q靵n靷y環h靸s靹n靺m靻z靼d靽b靾x靿y鞀t鞁b瓔y鞂j鞃h鞄p鞅y鞆b鞇y鞈g鞉t鞊j鞌a鞎h鞏g鞐k鞑d鞒q鞓t甌o鞔m鞕b鞖s鞗t鞙x鞚k甕w鞛b鞜t鞝s鞞b鞟k鞡l鞢x鞣r鞤b鞥e鞦q鞧q鞨h鞩q鞪m鞫j鞬j鞮d鞯j鞰w鞱t鞲g鞳t鞴b鞵x畢b鞶p鞷g鞸b鞹k鞺t鞻l鞼g當d鞽q鞾x鞿j韀j疇c韂c韃d韅x韆q韇d韈w韉j韊l痠s韋w韌r韍f韎m韏q韐g韑w韒q韓h韔c韕k痺b韖r韗y韘s韙w韚g韛b韜t韝g瘋f韞y韟g韠b韡w韢s韣d韤w韥d韨f韪w韫y韬t韮j韯x韰x韱x韲j韴z韷l韸p韹h韺y韻y韼p韽a韾y響x頀h頁y頂d頃q項x癉d頇h須x頉y癘l頊x頋e頌s頍k頎q頏h癤j預y頑w頒b頓d頔d頕d頖p頗p領l頙c頚j頛l頜h頝q頞e頟e頠w頡j頢k頣s頤y頥y頦k頧d頨y頩p頪l頫f頬j頭t頮h頯k頰j頱l頲t頳c頴y頵j頶h頷h頸j頹t頺t頻b頼l頽t頾z頿z顀c顁d顂l顃t顄h顅q顆k顇c顈j顉q顊y顋s題t額e顎e顏y眙y顐w顒y顓z顔y顕x顖x顗y願y顙s顚d顛d顜j眾z顝k類l顟l顠p顡w顢m顣c顤y顥h顦q顧g顨x顩y顪h顫c顬r顭m顮b顯x顰p瞇m顱l顲l顳n顴q顸h顼x颀q颉j颋t颌g颎j颏k颒h颔h颕y颙y颚e颛z颞n颟m颡s颣l颥r颦p風f颩d颪g颫f颬x颭z颮b颯s颰b颱t颲l颳g颴x颵x颶j颷b颸s颹w颺y颻y颼s颽k颾s颿f飀l飁x飂l飃p飄p飅l飆b飇b飈b飉l飊b飋s飌f飍x飏y飐z飑b飒s飓j飔s飕s飖y砦z飗l飙b飚b飛f飜f飝f飡c飢j飣d飤s飥t飦z飧s飨x飩t飪r飫y飬y飭c飮y飯f飰f飱s碭d飲y飳z飴y飵z飶b飷j確q飸t飹b飺c飻t飼s飽b飾s飿d餀h餁r餂t餃j餄h餅b餆y餇t餈c餉x養y餋j餌e磽q餍y餎l餏x餑b餒n餓e餔b餕j餖d餗s餘y餙s餚y餛h餜g餝s餞j餟c餠b餡x餢b餣y餤d餥f餦z餧w館g餩e餪n餫y餬h餭h餮t餯h餰j餱h餲a餳x礬f餵w餶g餷c餸s餹t餺b餻g餼x餽k餾l餿s饀t饂w饃m饄t饅m饆b饇y饈x饉j饊s饋k饌z饍s饎x饏d饐y饑j饒r饔y饕t饖w饗x饘z饚h饛m饜y饝m饞c饟x饠l饡z饢n饣s饤d饦t饧x饨t禪s饩x饪r饫y饬c饳d饴y饷x饸h饹g饻x饽b饾d馀y馂j馃g馄h馇c馉g馊s馌y馎b馐x馑j馓s馔z馕n馘g馛b馜n馝b馞b馟t馠h馡f馢j馣a馤a馥f馦x馧y馨x穎y穡s馪p馫x馬m馭y馮f馯h馰d馱t馲t馳c馴x馵z馶z馷p馸x馹r馺s馻y馼w馽z馾d馿l駀y竇d駂b駃j筅x駅y駆q駇w駈q駉j駊p駋z駌y筧j駍p駎z駏j駐z駑n駒j駓p駔z駕j駖l駗z駘t駙f駚y駛s駜b駝t駞t駟s箢y駠l駡m駢p駣t駤z駥r駦t駧d駨x駩q駪s駫j駬e駭h駮b簞d駰y駱l駲z駳d駴h駵l駶j駷s駸q駹m駺l駻h駼t駽x駾t駿j騀e騁c騂x騃a騄l騅z騆z騇s騈p騉k騊t騋l騌z騍k騎q騏q騐y騑f騒s験y騔g騕y騖w騗p騙p騚q騛f紆y騜h騝q騞h騟y騠t騡q騢x騣z純c騥r騦s騧g騨t絕j騩g騪s騬c騭z騮l騯p騰t騱x騲c騳d騴y綽c緄g騶z騷s騸s騹q騺z騻s騼l騽x騾l騿z縋z驁a驂c驃p驄c驅q縲l驆b驇z驈y驉x驊h驋b驌s驍x驎l驏z驐d繃b驑l驒t驓c驔d驕j驖t驗y驘l驙z驚j驛y驜y繽b驝t驞p驟z驠y驡l驢l驣t驤x驥j驦s驧j驨x驩h驪l驫b驲r驵z驷s驸f驺z驽n驿y骀d骁x骃y骅h骈p骉b骊l骍x骎q骐q骒k骓z骔z骕s骖c骘z骙k罷b骛w骜a骝l骞q骟s骠b骢c骣c骥j骦s骧x骩w骪w骫w骬y骭g骮y骯a骰t骱j骲b骳b骴c骵t羨x骶d骷k骹q骺h骻k骼g骽t骾g骿p羻q髀b髁k髂q髃y髄s髅l髆b髇x髈b髉b髋k髌b髎l髏l聖s髐x髑d髒z體t髕b髖k髗l髙g髚q髛k髜q髝l髞s髟b髠k髡k髢d肀y髣f髤x髥r髦m髧d髩b髪f髫t髬p髭z髮f髯r髰t胙z髱b髲b髳m髴f髷q髸g髹x髺k髻j髽z髾s髿s鬀t鬁l膣z鬄t鬅p鬆s鬇z鬈q鬉z鬊s鬋j鬌d鬍h鬎l鬏j鬐q鬑l鬒z鬓b鬔p鬕m鬖s鬗m鬘m鬙s鬚x鬛l鬜q鬝q鬞n鬟h鬠k臘l鬡n鬢b鬣l鬤r鬥d臥w鬧n鬨h鬩x鬪d鬬d鬭d鬮j鬯c鬰y艙c鬳y艤y鬴f鬵z鬶g鬷z鬸l鬹g鬺s鬻y鬽m鬾j鬿q魀g魃b魅m魆x魇y芐h魉l魊y魋t魍w魎l魐g魑c魒p魕j魖x魗c魘y魙z魚y荮z莖j魜r魝j魞a魟h魠t魡d魢j魣x魤e魥j魦s魧h菪d菴a魩m萇c魪j魫s魬b魭y魮p魯l魰w葒h魲l魳z魴f魶n魷y魸n魹m魺h魻x魼q魽h魾p魿l鮀t蒹j蓀s鮂q鮃p鮄f鮅b鮆c鮇w鮈j鮉d蓮l鮊b鮋y鮌g鮍p鮎n鮏x鮐t鮑b蔆l鮒f鮓z鮔j鮕g鮖s蔞l蕆c蕘r鮘c鮙t鮚j鮛s鮜h鮝x鮞e鮟a鮠w鮡z鮢z鮣y鮤l鮥l鮦t鮧y鮨y鮩b鮪w鮫j鮬k鮭g鮮x鮯g鮰h鮱l鮲m鮳k鮴x鮵t鮶j鮷t鮸m鮹s鮺z鮻s鮼q鮽y鮾n鯀g鯁g鯂s鯃w鯄q鯅s鯆p鯇h鯈t蘞l鯊s鯋s鯌k鯍m鯎c鯏l虼g鯐s鯑k鯒y鯓s鯔z鯕q鯖q鯗x鯘n鯚j鯛d鯜q鯝g鯞z鯟d鯠l鯡f鯢n鯣y鯤k蜴y鯦j鯧c鯨j鯩l鯪l螄s螈y鯬l鯭m鯮z鯯z鯰n鯱x鯲d鯳s鯴s鯵s鯶h鯷t鯹x鯺z鯻l鯼z鯽j鯾b鯿b鰀h蟛p鰁q鰂z鰃w蟣j鰄w鰅y鰆c鰇r鰈d鰉h鰊l鰋y鰌q鰍q鰎j鰏b蠶c鰑y蠻m衊m鰓s鰔j鰕x鰖t鰗h鰘s鰙r鰚h鰛w鰜j鰝h鰞w鰟p鰠s鰡l鰢m鰣s鰤s鰥g鰦z鰧t鰨t鰩y鰪e鰫y鰬q鰭q鰮w鰯r鰰h鰱l鰲a袼g裉k鰴h鰵m鰶j鰷t鰸q鰹j鰺s鰻m鰼x鰽q鰿j鱀j鱁z鱂j鱃x鱄z鱅y鱆z鱇k鱈x襖a襲x鱊y鱋q鱌x鱍b鱎j鱏x鱐s鱑h鱒z覈h觀g觔j鱔s鱕f鱖g鱗l鱘x鱙y鱚x鱛a鱜k鱞g鱟h鱠k鱡z鱢s鱣z鱤g鱥g計j鱦y鱧l鱨c鱩h鱪s鱫a訟s訥n訶h鱭j鱮x鱯h鱰s鱱l鱲l鱳l鱴m鱵z鱶x鱷e鱸l鱹g鱺l鱻x鱽d鱾j試s鱿y鲀t鲂f詬g鲃b鲄h鲅b詰j鲆p鲇n詳x鲈l鲉y鲊z鲋f詿g鲌b鲎h誄l鲏p鲐t鲑g鲒j鲓k鲔w鲕e鲖t鲗z鲘h鲙k鲚j鲛j鲝z鲞x鲟x誦s鲠g鲡l說s鲢l鲣j鲥s鲦t誹f鲧g鲨s鲩h鲪j鲫j鲬y鲭q鲮l鲯q鲰z鲱f鲲k鲳c鲴g鲵n鲶n鲷d鲹s鲺s諦d鲻z鲼f諭y鲽d鲾b鲿c鳀t諼x謄t鳁w鳂w鳄e謖s鳅q鳆f謾m譏j鳇h鳈q鳉j鳊b變b鳌a鳍q鳎t鳏g鳐y鳑p讞y鳓l鳔b鳕x鳗m鳘m鳙y鳚w鳜g鳝s鳟z鳠h鳡g鳢l鳣z鳤g鳥n鳦y鳧f豊l鳨l鳪b鳫y鳬f鳭d鳮j鳯f鳰n鳱g鳲s鳳f鳴m鳵b鳶y貞z買m費f貽y鳸h賀h鳹q鳺f鳻b鳼w鳽j鳾s鳿y鴀f鴁y鴂j鴃j鴄p賍z鴅h鴆z鴇b鴈y鴉y鴊z鴋f鴌f賚l賦f賫l鴎o賱y鴏d賲b鴐j鴑r鴒l鴓m鴔f鴕t鴖m鴗l鴘b鴙z鴚g鴛y鴜c鴝q鴞x鴟c鴠d鴡j鴣g鴤z鴥y鴦y鴧y鴨y鴩d鴪y贖s鴬y鴭d赾q鴰g鴱a鴲z鴳y鴴h鴵x鴶j鴷l鴸z鴹y鴺y鴻h鴼l鴽r趒t鴾m鴿g趕g鵁j鵂x鵃z鵄c鵅l鵆q鵇t鵈e趛y鵊j鵋j趢l鵌t鵍h鵎t趦z趬q趶k鵐w鵒y鵓b鵔j鵕j跊m鵖b鵗x鵘j鵙j鵚t鵛j鵜t鵝e鵞e鵟k鵠h鵢s鵣l鵤z跮d鵦l鵧p鵨s鵩f鵪a鵫z鵬p踇m鵭q鵮q鵯b踋j鵰d鵱l鵲q踐j鵳j鵴j鵵t鵶y鵷y踖j鵸q鵹l鵺y鵻z鵼k鵽d踜l鵾k鵿s鶀q踠w鶁j鶂y踤z踨z踼t鶄j鶅z鶆l鶇d蹅c蹆t蹌q蹎d蹏t鶉c鶊g鶋j鶌q鶍y鶎k鶏j鶐s鶑y鶒c鶓m躌w躤j鶖q躪l鶗t鶘h鶙t鶚e躳g躴l躼l軂l軅y軈y軍j鶜m鶝f鶞c鶟t鶠y鶡h鶢y鶣p鶤k鶥m鶦h軛e鶧y軞m軭k軯p軳p鶩w鶪j軸z輇q鶬c鶭f鶮h鶯y鶰y鶱x鶲w輑y鶴h鶵c輜z鶶t鶷x鶸r輠g鶹l鶺j鶻g鶼j輧p輪l輯j輳c輼w鶾h鶿c鷀c鷁y鷂y轋h轌x轓f鷄j鷅l鷆t鷇k鷈t鷉t鷊y鷋t鷌m鷍x鷎g鷏t鷐c鷑j鷒t鷓z轙y鷔a轚j鷕y鷖y轜e鷗o鷘c鷙z鷚l鷛y轣l鷝b鷞s鷟z鷠y鷡w鷢j鷣y鷤t鷥s鷦j鷨h鷩b鷪y鷫s鷬h辀z鷮j辎z鷯l辤c鷱g辧b鷲j鷳x鷴x鷵t鷶m鷷z鷸y鷹y鷺l鷻t鷼x鷽x辭c鷿p鸀z鸁l鸂x鸃y鸄j鸅z鸆y鸇z辴z鸈y鸊p鸋n鸌h辺d鸍m鸎y鸏m鸐d鸑y迀g鸒y鸓l鸔b鸕l鸖h鸗l鸘s鸙y鸚y鸛g鸜q鸝l鸞l鸠j鸢y鸤s鸧c鸨b鸩z鸪g鸫d鸬l鸮x鸰l鸱c鸲q鸴x鸶s鸷z鸸e鸹g鸺x鸻h鸼z鸾l鹀w鹁b鹂l鹄g鹆y鹇x迒h鹈t迓y鹉w鹋m鹌a鹍k迠c鹎b迡c鹐q鹑c迣z迦j迧c鹓y鹔s迶y鹖h鹗e迼j鹘g迿x鹙q鹚c鹛m逌y逘y鹜w鹝y鹞y鹟w鹠l鹡j鹢y鹣j鹥y鹦y鹨l鹩l鹪j逤s鹫j逩b逫z逰y鹬y鹭l鹮h鹯z鹱h鹲m鹳g鹵l鹷l鹸j鹹x鹻j鹼j鹽y鹾c逽n逿d遃y遆t遉z麁c麂j麃b麄c麅p麆z麇j麈z麉j麊m麋b麌y麍l麎c麏j達d麐l違w麑n麒q麔j遚c麕j麖j麗l麘x麙x麚j麛m麜l麝s麞z麟l麠j麢l麣y麤c麥m麧h麨c麩f麪m麫m麬f麭p麮q麯q麰m麱f麲x麳l麴q麵m麶c麷f麸f麹q麺m適s麼m麽m遬s麾h麿m遰d黀z遱l黁n遳c黃h黅j黆g黇t黈t黉h黊h黋k黌h黏n黐c黒h黓y黕d黖x黗t黙m黚q黛d黜c黝y點d黟y黠x黡y黢q黣m黤y黥q黦y黧l黨d黩d黪c黫y邍y黬y邎y黭y黮d邐l黯a黰z黱d黲c黳y黴m黵d黶y黷d黸l黹z黻f黼f黽m邗h黾m黿y鼀c鼁q鼂c鼃w鼄z鼅z鼆m鼇a邞f鼊b鼋y邨c鼌c邩h鼍t邫b邽g鼑d鼒z鼔g鼕d鼘y鼙p鼚c鼛g鼜c鼝y鼞t鼟t鼡n鼢f鼣f鼤w鼥b鼦d鼧t郋x鼨z郌g鼩q鼪s鼫s鼬y鼭s鼮t鼯w鼰j鼱j鼲h鼳j鼴y鼵t鼶s鼷x鼸x郞l鼹y鼺l鼼y鼽q鼾h鼿w齀w齁h齂x齃e齄z齅x郣b郩x齆w齇z齈n齉n齊q郰z郲l齌j齍z齎j齏j齑j齒c齓c齔c齕h齖y齗y齘x郼y齚z齛x齜z齝c齞y齟j齠t齡l齢l齣c齤q齥x鄅y鄋s齧n齨j鄍m齪c齫y齬y齭c鄒z齯n鄖y鄘y鄤m齱z齲q齳y齴y鄧d鄩x鄪b齶e齷w齸y齹c齺z齻d齼c齾y龀c龁h龂y鄳m鄹z龃j龅b龆t龇z龈y龉y龊c龌w龍l龏g鄿q酅x龑y龒l龓l龔g龕k龗l龘d龛k龜g龝q酨z龞b龢h龣j龤x龥y鳩j鳷z鴍w鴢y鴫t鴮w鴯e鵀r鵉l鵏b鵑j鵡w鵥k鶃y鶛j鶨c鶫d鶳s鶽s鷃y鷜l鷧y鷭f鷰y鷾y鸉y鹒g鹕h鹶j鹺c麀y麡q鼈b鼉t鼏m齋z齙b齦y齮y齰z齵y齽j龎p龖d龡c癷b唉a苯b瞓f硸n秎f斗d稥x坊f竕f告g龟g哼h解j擂l罖r芈m羒f羘z羛y羜z羣q羦h羪y羫q羭y羮g羳f羵f羶s羷l翂f裂l聁u膹f艈u漂p骑q砌q雀q蒊h蒶f蕒m蕡f是s伺c毝c苔t虲u蚠f蝊u蟇m衁h衇m衈e衖x衚h衜d衞w衟d衠z衯f衮g袰b袴k呀y訜f子z豶f亂l傯z僨f償c兇x喲Y嗯E堯Y塏K墳F墾K鶕A鶔r鶈q鱬r鱝f魵f髍m馩f馚f饙f餴f颃h霻f霣y雺w隫f闧u闇a閥f閟b間j镪q钿d鐼f鐢f鏦c鏜t鍂p奮f婁l媵y屢l嶠j幀z幗g憤f錖d錆q銽x橥z歸g殛j炷z甯n瘧n瘵z碡z糞f紛f羥q腳j膩n臺t莜y蓿x褸l誒e讜d讠y贁b趀c趓d趙z趸d跀y跥d輊z輒z轒f轕g遧z邚r邤x郬q郺y酳y酼h酽y醆z醈t醓t醔q醦c釀n釂j釈s釋s釒j釔y釕l釗z釙p釠l釢n釤s釥q鉀j釪h釫h釯m釿j鈃x鈄d鈇f鈈b鈎g鈏y鈑b鈖f鈙q鈚p鈝y鈟d鈠y鈨y鈩h鈱m鈵b鈾y鈿d鉅j鉇s鉒z鉔z鉗q鉞y鉡b鉢b鉥s鉧m鉬m鉲k鉳b鉵t鉽s鉾m銁j銃c銅t銍z銐c銑x銕t銗h銘m銠l銡j銣r銤m銨a銫s銬k銰a銱d銹x銼c銾h銿z鋃l鋊y鋍b鋏j鋐h鋒f鋓c鋔w鋜z鋝l鋫l鋱t鋲b鋶l鋻j鋾t錄l錈j錐z錔t錛b錝c錫x錰s錶b鍃h鍅f鍎t鍜x鍝y鍮t鍰h鍴d鎎k鎗q鎚c鎢w鎣y鎥t鏓c鏷p鐍j鐳l鐶h鐺d鑃d鑍y鑙j鑚z鑛k鑭l鑶c钂t钘x铇b锳y镮h镺a镾m閁m閅m閯s閵l闂h闃g闑n闒t闔h闢p闣d陥x陬z陯l韁j韄h頄q順s顑k飠s饁y饓c駁b駄t駯z騘c騤k騫q騵y鰐e鰒f鰳l鰾p鱉b鱓s鳋s鳒j鳛x鹧z鹴s黂f黺f鼖f齩y驀m髊c髨k髵e髶e髼p鬂b鬦d鬫k鬱y魈x魌q魓b魛d魨t魱h鮁b鮗d鮿z鯙c鯉l鯥l鯫z鯸h龐p".IndexOf(text, StringComparison.Ordinal); + if (num < 0) + { + return string.Empty; + } + + return "丂k丄s丅x丆m丏m丒c丗s丢d丠q両l丣y并b丩j丮j丯j丱g丳c丵z丷b丼j乀f乁y讈l乆j乑y乕h乗c乚h乛w乢g乣j乤h乧d乨s乪n乫g乬g乭d乮m乯o乲c乴x乵y乶p乷s乸n乹q乺s乻e乼z乽z乿z亀g亁q乱l亃l亄y亅j亇m亊s亐y亖s亗s亘g亜y亝q亚y亣d亪y亯x亰j亱y亴y亶d亷l亸d亹m亼j亽j亾w仈b仌b仏f仐j仒e仚x仛t仜h仢b仦c仧c仩c仭r仮f仯c仱q仴w仸y仹f仺c仼w仾d伀z伂p伃y伄d伅d伆w伇y伈x汲j伌a伒j伓p伔d伖t伜c伝y伡c伣q伨x伩x伬c伭x伮n伱n伳x伵x伷z伹q伻b伾p伿z佀s佁a佄h佅m伫z布b佉q佊b佋z佌c佒y占z佖b佡x佢q佦s佨b徊h佫h佭x佮g佱f佲m佷h佸h佺q佽c侁s侂t侅g来l侇y侊g侌y侎m侐x侒a侓l侕e仑l侘c侙c侚x侜z侞r侟c価s侢d侤t侫n侭j侰j侱c侲z侳z侴h侣l局j侸s侹t侺s侻t侾x俀t俣y系j俆x俇g俈k俉w俋y俌f俍l俒h俓j俔q俕s俖p俙x俛f侠x俢x俤d俥c俧z俫l俬s俰h俲x俴j俵b俶c俷f俹y俻b俼y俽x俿h伥c倁z倂b倃j倅c俩l倇w倈l仓c倊z个g倎t倐s们m倓t倕c幸x倗p倛q倝g倞j倠s倢j仿f値z倧z伦l倯s倰l倱h倲d倳z倴b倵w倶j倷n倸c倹j倻y倽s倿n偀y偁c偂q偄r偅z偆c伟w偊y偋b偍t偐y偑f偒t偓w偔e偖c偗s偘k偙d偛c偝b偞x偠y偡z偢c偣a偦x偧z偨c偩f偪b偫z偭m偮j偯y偰x偱x偲c偳d侧c侦z偸t偹b咱z偼j伪w傁s傂z傃s傄x傆y傇r傉n傊y傋j傌m傎d傏t傐h杰j傒x傓s傔q傕j伧c傗c伞s备b傚x傛r傜y傝t傞s傟y傠f傡b家j傤z傦g傪c傫l佣y傮z偬z傰p傱s传c伛y债z伤s傹j傼h傽z倾q傿y僀d僁x偻l僃b僄b仅j僆l戮l僈m佥q僊x僋t僌y働d僎z僐s侨q僒j僓t僔z仆p僗l僘c僙g僛q僜d僝c僞w僟j僠b僡h僢c僣t僤d侥j偾f僩x僪y僫e僯l僰b雇g僲x僴x僶m僷y僸j价j僺q僼f僽z僾a僿s仪y儁j侬n儃c亿y当d侩k俭j儊c儌j儍s儎z儏c傧b儑a儓t俦c侪c儖l儗y尽j儙q儚m儛w儜n儝q儞n偿c儠l儢l儣k儤b儥y儦b儧z儨z儩s优y儫h儬q儭c儮l儯t儰w儱l储c儳c儴x儵s儶h俪l罗l儹z傩n傥t俨y儽l儾n兂z凶x兊d兑d兎t兏c児e儿e兖y兘s兟s兡b兤h兦w内n两l兪y兯h兲t兾j兿y冃m冄r円y冇m册c冋j冎g冏j冐m胄z冓g冔x冘y冚k冝y冞s冟s冡m冣z冦k冧l冨f冩x幂m冭t冮g冴y冸p冹f冺m冾q冿j凁s凂m凃t凅g净j凊q冻d凎g凐y凒a凓l凔c凕m凖z凗c凘s凙d凚j凛l凞x凟d凢f凣f凥j処c凧y凨f凩k凪n凬f凮f凯k凲g凴p凷k凾h刄r刅c刉j刋q刌c刏j刐d刓w刔j刕l刜f刞q刟d刡m刢l刣z别b刦j刧j删s刬c刯g刱c刲k刴d刵e刼j刾c刭j剅d剆l则z剈y剉c克k刹c剒c剓l剕f剗c剘q剙c剚z刚g剥b剟d剠q剢d剣j剤j剦y剨h剫d剬z剭w剐g剰s剱j剳d剀k创c剶c铲c剸t剹l剺l剻p剼s剾k劀g划h劄z劅z劆l剧j刘l刽g劋j刿g剑j劎j劏t剂j劒j劔j劕z劖c劗z劘m劙l劚z劜y劤j劥k劦x劧z劮y劯z劰m労l劵j劶k劷y劸w効x劺m匡k劼j劽l勀k劲j勂g勄m勅c勆l勈y勊k勌j勍q勎l勏b勑c勓k勔m动d勖x务w勚y勋x勜w胜s劳l勠l勡p势s积j勥j剿c勧q勨x勪q勫f勬j勭t勮j勯d劢m勲x勳x勴l励l勶c勷x劝q匀y勼j勽b匁m匂x匃g匄g匇y匉p匊j匋t匌g匎e匑g匒d匓j匔g匘n匛j匜y匞j匟k匢h匤q匥f匧q匨z匩k匫h匬y匦g汇h匰d匮k匲l匳l匴s匵d匶j匷j匸x匼k匽y区o卂x卄n卆z卋s卌x卍w卐w协x単d卙j卛s卝g卥x卨x卪j卬a卭q卲s卶c恤x却q卼w卽j卾e厀x厁s厃w厇z厈h厊y厎d厏z厐p厑y厒q厓y厔z厖m厗t厍s厛t厜z厞f厡y厤l厧d厪j厫a厬g厌y厯l厰c厱q厉l厳y厣y厵y厷g厸l厹r厺q厼k厽l厾d叀z参c叄c叅c叆a叇d収s叏g叐b叒r叓l叕z叚j叜s叝j叞w叡r丛c叧g叴q叺c叾d叿h吀m吂m吅x吇z寸c吔y吘o吙h吚y吜c吢q吤j吥b吪e吰h吴w呐n吷x吺d吽h吿g呁j吕l呄g呅w呇q呉w呌j呍h尺c呏s呑t呚h呝e呞s呟j呠p呡w呣m呥r呧d呪z呫t呬x呭y呮q呯p呰z呴g呹y呺x呾d呿q咁x咃t咅p咇b咈f咉y咊h咍h咑d咓w咗z咘b咜t咞x咟h咠q咡e咢e咥d咮z咰s咲x咵k咶g咷t咹e咺x呙g咾l哃t哅x哊y哋d哖n哘x哛p哠h员y哢l哣p哤m哫z哬h哯x哰l哱b哴l哵b哶m哸s哹f哻h哾c唀y唂g唃g呗b含h唈y唊j唋t唌d唍w唎l唒q唓c唕z唖y唗d唘q唙d唚q唜m唝g唞d唟g唡l唥l唦s唨z唩w唫j唭q唲e唴q唵a唶j念n唹y唺t唻l唽x啀a啂g啇d啈h啋c啌q啍z啎w问w啑s啒g啓q啔q啗d啘w啚b啝h哑y启q啠z啢l衔x啨q啩g啫z啯g啰l啱y啲d啳q啴c啹j啺t啽a啿d喅y喆z喌z喍c喎w喐h喒z喓y喕m喖h喗y唤h喛h喞j喠z喡w喢s喣x喤h喥d喦y喨l喩y丧s吃c乔q喭y单c喯p喰c哟y喴w営y喸p喺x喼j喿z嗀h嗁t嗂y嗃h呛q啬s嗈y嗊g嗋x吗m嗏c嗐h嗕r嗗w嗙p呜w嗛q嗞z嗠l嗢w嗧j唢s嗭z嗮s嗰g嗱n嗴q哔b嗸a嗹l嗺z嗻z嗼m嗿t嘂j嘃c嘄j嘅k叹t嘇s嘊a嘋x喽l嘐x嘑h嘒h呕o嘕x啧z尝c嘙p嘚d唛m嘝h嘠g嘡t嘢y嘥s嘦j嘨x哗h嘪m嘫r唠l啸x叽j嘳k哓x嘷h呒m嘺q嘼c嘽t嘾d噀x恶e噂z噃f噄c噅h噆z噇c噈c噉d噊j噋t噏x噐q噑h噒l嘘s噕h噖y噚x噛n噝s噞y噟y哒d噡z噣z哝n哕y噧x噭j噮y嗳a噰y哙k噳y喷p噷x吨d噺h噽p噾y噿z咛n嚁d嚂l嚃t嚄h吓h嚈y嚉d嚊x嚋z哜j嚑x嚔t噜l嚖h嚗b嚘y啮n嚚y嚛h嚜m嚝h嚞z嚟l嚠l嚡x嚢n嚤m咽y呖l嚧l咙l嚩p嚪d嚫c嚬p嚭p向x嚰m嚱x嚲d喾k严y嚵c嘤y嚸d嚹l嚺t嚻x嚽c嚾h嚿h啭z嗫n嚣a囃z冁c囆c囇l呓y囋z苏s囍x囎z囏j囐y嘱z囒l囓n囕r囖l囘h囙y囜n団t囥k囦y囧j囨p囩y囱c囬h囮e囯g囲w図t囶g囷q囸r囼t圀g圁y圂h圅h囵l国g圌c围w圎y圏q圐k圑p园y圆y圔y圕t图t圗t团t圙l圚h圛y圝l圞l圠y圡t圢t圤p圥l圦k圧y圫y圱q圲q圴z圵d圶q圷x圸s圼n圽m圿j坁z坃x坄y坅q坆m坈r坉t坋b坒b坓j坔d坕j坖j坘d坙j坢b坣t坥q坧z坬g坮t垧s坱y坲f坴l坵q坸g坹x坽l坾z坿f垁z垇a垈d垉p垊m垍j垎h垏l垐c垑c垔y垕h垖d垗z垘f垙g垚y垜d垝g垞c垟y垥x垨s垪b垬h垯d垰k垱d垳h垵a垶x垷x垹b垺p垻b垼y垽y垾h垿x埀c埁c埄b埅d埆q埇y埈j埉x埊d埌l埍j埐q埑z埓l埖h埗b埛j埜y埞d垭y埢q埣s埥q埦w埧j埨l埩z埪k埫c埬d埮t埰c埲b埳x埵d执z埻z埼q埾j埿n堁k堃k堄n坚j堈g堉y垩e堌g堎l堏f堐y堒k堓a堔s堖n堗t堘c堚h堛b堜l埚g堟z堢b堣y堥m堦j堧r堨a堩g堫z堬y堭h堮e尧y报b堲j堳m场c堶t堷y堸f堹z堺j堻j堼f堽g堾c碱j塀p塁l塂j塃h塅d塆w塇x塉j块k茔y塎y垲k塐s埘s塓m塕w塖c涂t塙q塚z塛l塜p塝b塟z塠d塡t坞w塣z埙x塦z塧a塨g塩y塪x塭w塮x塯l塰h塱l塲c塳p塴b尘c塶l塷l塸o堑q塺m塻m塼z塽s塿l墂b墄q墆z墇z墈k垫d墋c墌z墍x墎g墏q墐j墒s墔c墕y墖t増z墘q墛w坠z墝q墠s墡s墢f墣p墤k墥t墦f墧q墪d墫c墬d墭s堕d墯d墰t墱d墲w墴h墵t墶d墷y墸z墹j墺a墙q垦k墿y壀p壂d壃j壄y壆x坛t壈l壉j壊h壋d壌r壍q壏x壐x壒a压y壖r壗j垒l圹k垆l壛y壜t壝w坏h垄l壠l壡r坜l壣l壥c壦x壧y壨l坝b壪w壭s壮z壱y売m壴z壵z壷h壸k壶h壻x壼k寿s壾m壿d夀s夁y夃g夅j夆f夈z変b夊s夋q夌l夎c夐x夑x夒n夓x夗y夘w夛d夝q夞y够g夡q梦m夣m夦c夨c夬g夰g夲b夳t夵y夶b夻h夽y夹g夿b奀e奃d奅p奆j奊x奌d奍q奂h奒k奓z奙b奛h奜f奝d奞x奟b奡a奣w奤h奦w奥a奨j奁l夺d奫y奬j奭s奋f奯h奰b奱l奲d奵d奷q奺j奻n奼c奾x奿f妀j妅h妉d妋f妎h妏w妐z妑p妔k妕z妘y妚f妜y妆z妟y妠n妡x妦f妧w妬d妰z妱z你n妴y妵t妶x妷z妸e妺m妼b妽s妿e姀h姁x姂f姄m姅b姇f姈l姉z姌r姗s姎y姏g姕z姖j姙r姛d姞j姟g姠x姡h姢j姤g奸j姧j姩n侄z姫z姭x姮h姯g姰j姱h姲y姳m姴l姵p姶y姷y姸y姺x姼s姽g姾q娀s娂h娋s娍c娎x娏m娐f娒m娔k娕c娖c娗t娙x娚n娱y娝b娞n娡z娢h娤z娦p娧t娨m娪w娫y娬w娭a娮y娯y娰s娳l娵j娷s娸q娹x娺z娻d娽l娾a娿e娄l婃c婄p婅j婇c婈l婋x婌s婍q婎h婏f婐w婑w婒t婓f婔f婖t婗n婘j婙j婛j婜q婝d婞x婟h婠w婡l婣y婤z婥c妇f婨l婩a婫h婬y娅y婮j婯l婰d婱x婳h婸d婹y婻n婼c婽j婾t媀y媁w媂d媃r媄m媅d媆r媇q媈h媉w媊q媋c媌m媍f媎j媏d媐y媓h媔m媕a媖y媗x媘j媙w媜z媞t媟x媠d媢m媣r媤s媥p媦w娲w媨c媩h媫j媬b媭x媮t妫g媰c媱y媴y媶r媷r媹l媺m媻p媪a妈m媿k嫀q嫃z嫄y嫅j嫆r嫇m嫈y嫊s嫋n嫍t嫎p嫏l嫐n嫑b嫓p嫕y妪y嫙x嫚m嫛y嫝k嫞y嫟n嫢g嫤j嫥z嫧z嫨h嫪l嫬z嫭h嫮h嫯a嫰n嫲m嫳p嫴g妩w嫶q嫷t嫸z嫹m嫺x娴x嫼m嫽l嫾l嬀g嬁d嬂z嬄y嬅h嬆x嬇k娆r嬊y婵c娇j嬍m嬎f嬏f嬐x嬑y嬒h嬔f嬕s嬘s嫱q嬚l嬛h袅n嬞d嬠c嫒a嬢n嬣n嬷m嬥t嬦c嬧j嬨c嬩y嫔p嬫r嬬r嬭n嬮y嬯t婴y嬱q嬳y嬵m嬶k婶s嬹x嬺n嬻d嬼l嬽y嬾l嬿y孁l孂j娘n孄l孅x孆y孇s孈x孉h孊m孋l娈l孍y孎z孏l孖m孞x孠s孡t孧y孙s孭m孮c孯q孲y孴n孷l学x孹b孻n孼n孾y孪l宂r宆q宊t宍r宎y宐y宑j宒z宔z宖h実s宧y宨t宩s宬c宭q宫g宯x宱z宲b宷c宺h宻m宼k寀c寁z寃y寈q寉h寊z寋j寍n寎b寏h寑q寔s寕n寖j寗n寘z寙y寚b寛k寜n寠j寝q寣h实s宁n审s寪w写x宽k寭h寯j寱y寲y寳b寴q宠c宝b寷f寽l対d尀p専z尃f将j专z寻x尌s对d导d尐j尒e尓e尗s尙s尛m尞l尟x尠x尡h尣w尦l尨m尩w尪w尫w尭y尮d尯k尰z尲g尳g尵t尶g尴g屃x届j屇t屌d屍s屒z屓x屔n屖x屘m屚l屛p屉t扉f屟x屡l层c屧x屦j屩j屪l屫j属s屭x屰n屲w屳x屴l屵a屶h屷h屸h屻r屼w屽h屾s岀c岃y岄y岆y岇a岉w岊j岋e岏w岒q岓q岕j岝z岞z岠j冈g岤x岥p岦l迢t岨j岮t岯p岰a岲k岴q岶p岹t岺l岻d岼p峀x峂t峃x峅b峆h峇b峈l峉e峌d峍l峎e峏e峐g峑q峓y峔m峕s峖a峗w峘h峚m峛l峜f峝t峞w峟y峠q峢l峧j峩e峫x峬b峯f峱n峲l峳y岘x峵r岛d峷s峸c峹t峺g峼g峡x峿w崀l崁k崄x崅q崈c崉t崊l崋h崌j崃l崏m崐k崑k崒z崓g崕y岗g崘l崚l崜d崝z崟y岽d崡h峥z崣w崥p崨j崪z崫j崬d崯y崰z崱z崲h嵛y崵y崶f崷q崸y崹t崺y崻z崼s崿e嵀z嵁k嵂l嵃y嵄m嵅h嵆j嵈h嵉t嵍w嵎y嵏z岚l嵑k岩y嵓y嵔w嵕z嵖c嵗s嵙k嵚q嵜q嵞t嵟c嵠x嵡w嵢c嵣d嵤r嵥j嵦k嵧l嵨w嵪k嵭b嵮d嵰q嵱y嵲n嵳c嵵s嵶r嵷s嵸z嵹j嵺l嵻k嵼y嵾c嵿d嶀t嵝l嶃z崭z嶅a嶆c岖q嶉w嶊z嶋d嶌d嶍x嶎y嶏p嶐l嶑x嶒c嶓b嶔q嶕j嶖y崂l嶘z嶚l嶛l嶜q嶞t嶟z峤j嶡j嶢y嶣j嶤y嶥j嶦z峄y嶨x嶩n嶫y嶬y嶭n嶮x嶯j嶰x嶱k嶲g嶳d嶴a嶵z嶶w嵘r嶹d岭l嶻j屿y岳y嶾y嶿r巀j巁l巂x巃l巄l巇x巈j巉c巊y岿k巌y巎n巐c漓l峦l巓d巅d巕n巗y巘y巙k巚y巜k巟h巠j巣c巤l巪g巬p巭b巯q巵z巶z巸y卺j巺x巻j巼p巿f帀z帄d帇n帊p帋z帍h帎d帒d帓m帗f帞m帟y帠y帡p帢q帣j帤r帅s帨s帩q帪z师s帬q帯d帰g帲p帐z帴j帵w带d帹s帺q帾z帿h帧z幁z帏w幆y幇b幈p幉d幊g幋p幍t幎m幏j幐t幑h幒z幓s幖b帼g帻z幙m幚b幜j幝c帜z幠h币b幤b幦m幧q幨c蒙m帮b帱c幭m幮c幰x幱l幵j幷b干g几j庁t仄z広g庈q庉d庌y庍b庎j庒z庘y庛c庝t庡y庢z庣t庤z庨x庩t庪g库k庬m庮y庯b庰b庱c庲l庴j庻s庼q庽y庿m廀s厕c厢x廃f厩j廅e廆h廇l厦s廋s廌z廍b廎q廏j廔l廕y廗d廘l廙y厨c廜t厮s廞x庙m厂a庑w废f广a廤k廥k廦b廧q廪l廫l庐l廭j廮y廯x廰t痈y廲l厅t廵x廸d廹p廻h廼n廽h弆j弇y弉z弌y弍e弎s弑s吊d弖h弙w弚t弜j弝b弡j弢t弣f弤d弨c弫z弬y弮j弰s弲x弪j弴d张z弶j强j弸p弻b弽s弾d弿j彁g彂f彃b彄k彅j彇x弹d彉g彋h弥m彍g弯w彏j彑j彔l彚h彛y彜y彞y彟h彠h彣w彦y彧y彨c雕d彮y彯p彲c彴z彵t彶j彸z彺w彽d彾l佛f徃w徆x徍w徎c徏z径j従c徔c徖c徝z从c徟z徕l徢x徣j徤j徥s徧b复f旁p徯x徰z徱p徲t徳d徶b彻c徺j徻h徾m徿l忀x忁b忂q忈r忊d忋g忎r忓h忔y忕s忚x忛f応y忞m忟m忢w忣j忥x忦j忨w忩c忬y忯q忰c忲t忳d忴q忶h忷x忹w忺x忼k怇j怈y怉b怋m怌p怐j怑b怗t怘h怚c怞y怟d怢t怣y怤f怬x怮y怰x怲b怳h怴x怶b怷s怸x怹t怺y怽m恀s恄x恅l恒h恇k恈m恉z恊x恌y恏h恑g恓x恔x恖s恗h恘q恛h恞y恟x恠g恡l耻c恦s恮z恱y恲p恴d恵h恷q恾m悀y悁j悂p悦y悆y悇t悈j悊z悋l悎h悏q悐t悑b悓q悕x悗m悘y悙h悜c悞w悡l悢l悤c悥y悧l悩n悪e悮w悰c悳d怅c闷m悷l悹g悺g凄q悾k悿t惀l惁x惂k惃g惄n惇d惈g惉z惌y惍j惎j惏l惐y惒h惓q惔t惖t惗n啜c惛h惞x惢s惣z惤j惥y惪d恼n恽y惵d惷c惸q恻c惼b惽m惾z惿t愃x愄w愅g愇w愊b愋x愌h愐m愑y愒q愓d愔y愖c愗m愘k愙k爱a惬q愝y愞n愡c愢s愥y悫q愩g愬s愭q愮y愯s愰h愱j愳j怆c愵n愶x恺k愸z愹y愺c愻x愼s愽b忾k慀x慁h慂y栗l慅s慆t殷y慉x态t愠y慏m慐g慒c慓p慔m慖g慗c惨c惭c慛c慜m慞z恸t慠a慡s惯g慤q慥z慦j慩l怄o怂s慬q慭y虑l慯s慱t慲m悭q慴s庆q慸d慹z慺l慻j戚q慽q欲y慿p憀l憁c忧y憃c憄z憅t憆c憇q憈q惫b憌q憍j憏c怜l凭p愦k憓h憕c憖y憗y憘x憙x惮d憛t憞d憟s憠j憢x憣f愤f憥l憦l憪x悯m憭l怃w憯c憰j憱c宪x憳t憴s憵p忆y憸x憹n憺d憻t憼j憽s懀w懁x懃q懄q懅j懆s恳k应y怿y檩l懎s懐h懓a懕y懖g懗x懘c懙y懚y懛d懜m懝a怼d懠q懢l懑m懤c懥z懧n恹y懩y懪b懫z懬k懭k懮y懯f懰l懱m惩c懳h懴c懒l怀h悬x懹r忏c懻j惧j懽h慑s恋l戁n戂m戃t戄j戅g戆g钺y戓g戋j戙d戜d戝z戞j戠z戣k戦z戗q戨g戬j戫y戭y戯x战z戱x戏h户h戸h戹e戺s戻t戼m扂d扄s扅y扆y扊y扏q仂l払f扖h扗z扙z扚d扜y扝y扞h扟s叉c扡t扦q扤w扥d扨s扱c扲q扴j扵y扷b扸x抵d扻z扽d抁y抂k拚p抅j擦c抇h抈y抋q抌d抍z殒y抏w抐n抔p抙p抜b抝a択z抣y抦b抧z抩n抪p抭y抮z抰y抲h抳n曳y抶c抷p抸j抺m抾q拀z拁j拃z抛p拏n拑q拕t拝b拞d拠j拡k拤q拪q拫h拰n拵c拸y拹x拺c拻h挀b挃z挄k挅d挆d挊n挋z挍j挏d挐n挒l挓z挔l挕d挗j挘l挙j挜y挦x挧y挩t挬b挭g挮t挰c挱s挳k挴m挵n挷b挸j挻s挼r挟x挿c捀p捁j捄j捇h捈t捊p捑z捒s捓y捔j捖w捗b捘z捙y捚z捛l捜s捝t捠b捤w捥w捦q舍s捪m扪m捬f捯d捰w卷j捳y捴z捵c捸t捹b捼r捽z捾w捿q掁c扫s抡l掅q掆g掋d掍h掑q掓s掔q掕l掗y挣z掚l挂g掜y掝h掞y掟z采c掦t掫z掯k掱p掲j掵m掶j掹m掻s掽p掿n拣j揁z揂j揃j揅y揇n揈h揊p揋w揌z揑n揓s揔z揕z揗x揘h揙b扬y换h揜y揝z揟x揢k揤j揥d揦l揧l揨c揫j揬t挥h揯g揰c揱x揳x揵q揷c背b揺y揻w揼b揾w搃z搄g构g搇q搈r搉q搊c损s搎s搑r搒b搕k摇y捣d搘z搙n搚l搝q擀g搢j搣m搤e搥c搧s搨t搩j搫p搯t搰h搱z搲w搳h搵w抢q搷t搸z搹e搻n搼q搾z摂s摃g摉s摋s摌c摍s摎n摏c摐c掴g摓f摕d摗s摙l摚c摛c掼g摝l搂l摠z摡g摢h摣z摤q摥t摦h摨z摪j摫g摬y摮a挚z摰n摱m摲c抠k摴c摵s抟t摷j掺c摼k摽b摿y撀g撁q撃j撆p捞l撉d撊x撋r撌g撍z撎y撏x撑c挠n撔h撗h搭d捻n撛l撜z撝h挢j撠j撡c掸d拨b撦c撨x撪b抚f撯z撱w扑p揿q撴d撶h撹j挞t撽j挝w捡j拥y擃n掳l擆z择z擈p擉c击j挡d擌s擏q擑j擓k担d擕x擖y擙a据j擛y擜e擝m挤j擡t擥l擧j擨y擩r擪y擫y拟n擭h擮j摈b拧n搁g掷z擳z扩k擵m擶j撷x擸l擹t摆b擞s撸l擽l扰r攁y攃c摅s攅z撵n攇x攈j攊l攋l攌h攍y攎l隆l攐q攑q攓q拦l攕x撄y攗m搀c撺c携x摄s攞l攟j攠m攡c攒c挛l摊t攦l攧d攨w攩d搅j揽l攭l攰g攱g攲q攳x考k攼g攽b敀p敁d敂k敃m敄w敆h敇c敊c敋g敍x敎j敐c敒s敓d敔y败b叙x敚d敜n敟d敠d敡y敤k敥y敧q敨t敩x敪d敭y敮x敯m敱a敳a敌d敶c数s敹l敺q敻x敼y敽j敾s敿j斀z斁d敛l毙b斄l斅x斆x斈x斉q斊q斍j斎z斏l斒b斔y斓l斖w斘s斚j斝j斞y斠j斢t斣d斦y斨q斪q斩z斱z斲z斴l斵z斶c断d斸z斺c斻h斾p斿l旀m旗q旇p旈l旉f旊f旍j旐z旑y旓s旔j旕e旘z旙f旚p旛f旜z旝k旞s旟y旡j旣j旪x旫t旲t旳d旴x旵c旸y旹s旻m旼m旽t旾c旿w昁b昄b昅j升s昈h昉f昋g昍x昑q昒h昖y昗z昘f昚s昛j昜y昞b昡x昢p昣z昤l昦h昩m昪b昫x昬h昮z昰s昲f昳d昷w昸d昹a昺b昻a昽l昿k晀t时s晄h晅x晆k晈j晋j晊z晍t晎h晐g晑x晘h晙j晛x晜k昼z曦x晠s晢z晣z晥w晧h晩w晪t晫z晭z晱s晳x晵q晸z晹y晻a晼w晽l晿c暀w暁x暃f暅g暆y晕y晖h暊x暋m暍y暏s暐w暒q暓m暕j阳y暙c暚y暛s暜p暞j暟k暠g暡w畅c暣q暤h暥y暦l暩j暪m暂z暬x暯m暰c昵n暲z暳h暵h暶x暷c了l暺t暻j暼p暽l暿x曀y曁j曂h曃d晔y曅y历l昙t曈t晓x曊f曋s曍h曎y曏x曐x曑s曒j曓b曔j曕y暧a曘r曚m曞l曟c旷k曡d曣y曤h曥l曧r曨l曪l曫l晒s曭t曮y曯z曱y曵y曶h书s曺c曽z朁c朂x会g朄y朅q朇b朎l朏k朒g朓t朖l朘j朙m朚h朜t朞j朠y朡z朢w朣t朤l朥l胧l术s朰t朲r朳b朶d朷d朸l朹q朻j朼b朾c朿c杁r杄q杅y圬w杊x杋f杍z杒r杔t杕d杗m杘c杙y杚g杛g杝t杢j杣m杤w杦j杧m杫s杬y杮f东d杴x杶c杸s杹h杺x杻c杽c枀s枂w枃j枅j枆m枊a枍y枎f枏n枑h枒y枓d枔x枖y栀z枛z枟y枠h枡s枤d枦l枩s枬z枮x枱s枲x拐g枹b枺m枻y枼y枽y枾s枿n柀b柂d柅n柆l柇h柈b柉f柊z柋d柌c柍y柎f柕m柖s柗s柟n柡y柣z柤z柦d柧g柪a柫f柭b柮w柲b栅s柶s柷z柹s柺g査c柼y柾j栁l栂m栃l栄r栆z栍s栐y栒x栔q栕c栘y栙x栚z栛l栜s栞k栟b栠r栢b栣r栤b栥z栦c栧y栨c栫j栬z栮e栯y栰f栱g栴z栵l栶y栺z栻s栿f桇r桋y桍k桏q桒s桖x桘c桙y桚z桛k桝j桞l桟z桪x桬s桭c桮b桯t桰g桱j桲b桳b桵r桸x桹l桺l桻f桼q桽w桾j杆g梀s梂q梄y梇l梈p梉z梊d梋x梌t梎a梐b梑d梒h梕r梖b梘j梙h梚w梛n梜j条t梞j枭x梠l梡k梣c梥s里l梪d梬y梮j梱k梲z梴c梶w梷j梸l梹b梺x梻x梼d梽z梾l梿l棁z弃q棅b棆l棈q棊q棌c棎c棏d棐f棑p棓b棔h枨c枣z棙l棛y棜y棝g棞j栋d棡g棢w棤q棥f棦c栈z棨q棩y棪y棫y棬j棭y棯s栖q棳z棴f棶l棷z棸z棻f棽c棾q棿n椀w椂l椄j椆c椇j椈j椉c椊z椌k桠y椑b椓z椔z椕b椖p椗d椙c椚m椛h検j椝g椞x椡d椢g椣d椥z椦q椧m椨f椩g椪p椫s椬y椮s椯d椱f椲h椳w椵j椶z椷j椸y椺x椻y椼y椾j楀y楁h楃w楄p楅b楆y楇g楈x楉r杨y楋l楌y楍b楎h楏k楐j楑k楒s枫f楘m楙m楛h楜h楟t楢y楤c楥x楧y桢z楩p楪d楬j业y楯d楰y楲w楳m楴d极j楶j楺r楻h楽l楾h楿k榁m榃t榅w榊s榋c榌b榎j榏y榑b榒n榓m榖g榗j榚y榝s榞y榟z榠m榡s榢j榣y榤j榥h榩q杩m榬y荣r榯s榰z榲w榳t榵r榶t榸z榹s榺s榼k榽x榾g桤q槂s盘p槄t槅g槆c槇d槈n槉j枪q槏q槑m槒x杠g槕z槖t様y槙d槚j槜z槝d槞l槡s槢x槣j槤l槥h槦r椠q椁g槩g槪g槫t槬h槮s槯c槰p槱y桨j槴h槵h槶g槷n槹g槺k槻g槼g槾m樀d桩z樃l樄c枞c樆l樇x樈q樉s樋t樌g樍z樎s樏l梁l樒m楼l樔c樕s樖k标b樚l樛l樜z樝z枢s樠m樢n样y樤t樥p樦z樧s権q樫j樬c樭j樮y樰x樲e樳x樴z樶z樷c朴p树s桦h樻g橀x橁c橂d橃f橅m橆w桡r橉l橊l桥q橌x橍r橎f橏j橑l橒y橓s橔t橕c橖t橗m橚s橜j橝t橞h机j橠n椭t橣n橤r橦t橧z橪y横h橬q橭g橮l橯l橰g橲x橳s橴z橵z橶j橷d橸j橺x橻c橽t橾s橿j檃y檅h檆s檇z檈x柽c檊g檋j檌z檍y檏p檒f檓h档d檕j檖s檘p檙c檚c檛z桧g檝j檞j檟j檡z检j樯q檤d檥y檦b檧s檨s檭y檮c台t檰m檱q檲t槟b檴h檵j檶q檷n柠n檹y檺g槛j檼y檽n檿y櫀q櫁m櫂z柜g櫄c櫅j櫆k櫇p櫉c櫊g櫋m櫌y櫍z櫎h櫏q櫑l櫒s橹l櫔l櫕c櫖l櫗m櫘h櫙o榈l栉z櫜g椟d橼y栎l櫠f櫡z櫣l櫤j橱c櫦q槠z栌l櫩y枥l櫫z榇c櫭j櫮e櫯s櫰h櫱n櫲y栊l櫵q櫶x櫷g榉j櫹x棂l樱y櫼j櫽y櫾y欀x欁n栏l欅j欆s欇s欈w欉c权q欋q欌z欍j欎y椤l欐l欑c栾l欓d欔q欕e榄l欗l欘z欙l欚l欛b欜n欝y欥y欨x欩c欪c欫q欬k欭y欮j欯x欰x欱h欳k欴l欵k欶s欸a欻c欼c钦q欿k歀k歁k歂c歄g歅y歈y歊x歋y歍w歏j欧o歑h歒t歓h歔x歕p歖x歗x歘x歚s歛l歜c歝y欤y歠c欢h歨b歩b歫j歭z歮s歯c歰s歱z岁s歳s歴l歵z歶y归g歺d歽z歾m殁m殀y殅s殈x殌j殎q殐s殑j殔y殕f殗y残c殙h殜d殝z殟w殠c殢t殣j殇s殥y殦c殧j殨k殩c殚d殬d殓l殡b殰d殱j歼j殶z殸q殹y杀s殻k壳k殽x殾x毁h毃q毄j殴o毇h毈d毉y毊x毋w毎m毐a毑j毘p毚c毞b毟m毠j毢s毣m毤t毦e毧r毨x毩j球q毭d毰p毱j毲d毴b毶s毷m毸s毺s毻t毼h毾t毵s氀l氁m氂m氃t氄r毡z氉s氊z氋m氇l氎d氒j気q氜y氝n氞n氠s气q氥x氢q氩y氭d氱y氲y氶z氷b氹d氺s氻l氼n泛f氿g汃p汄z汅m汈d汋z汌c丸w汏d汑t汒m汓q汖p汘q污w汚w汢t汣j汥z汧q汫j汬j汭r汮j汯h汱t汳b汵g汷z汸f决j汼n汿x沀x沄y沇y沊d沋y沍h沎h沑n没m冲c沗h沘b沚z沜p沝z沞z沠p沢z沨f沬m沯z沰t沴l沵m沶y沷f沺t泀s况k泂j泃j泆y泇j泋h泍b泎z泏c泑y泒g泙p泚c泜z泝s泟c泤s泦j泧y泩s泬j泭f泲j泴g泹d洀p洃h洅z洆c洈w洉h洊j洍s洏e洐x洑f洓s洔z洕y洖w洘k洝a洟y洠s洡l洢y洣m洤q洦p洨x泄x洬s洭k洯q洰j洴p汹x洷z洸g洺m洿w浀q浂y浄j浉s浌p浐c浕j浖l浗q浘w浛h浝m浟y浡b浢d浤h浥y浧y浨l浫h浭g浱c浳y浵t浶l浃j浺c浻j浽s浾c浿p涀x涁s涃k涄p涆h泾j涊n涋t涍x涏t涐e涒t涗s涘s涙l涚s涢y涥h涬x涭s涰c涱z涳n涴w涷d涹w涺j涻s凉l涽h涾t淁q淂d淈g淉g淊y淍z淎p淏h淐c淓f淔z淕l淗j泪l淛z淜p淟t淢y淣n渌l淧m淩l沦l淭q淯y淰n淲b淴h渊y涞l淸q浅j淽z淾y淿b渀b渁y渂w渃r渄f渆y渇k済j渉s渋s渏y渒p渓x渕y渘r涣h减j渞q渟t渢f涡g渧t渨w渪r测c渮h渰y渱h渳m渵m渶y渷y渹q渻s渼m渽z浑h渿n湀g湁c湂e湅l湆q湇q湈m湉t凑c湋w湌c湏h湐p湑x湒j湗f湙y湚y湜s湝j浈z湠t湡y湢b湣m湤s湥t湦s涌c湨j湩d湪t湬q湭q汤s湰l湱h湳n湴b湵y湶q湷z湸l湹c湺x湻c湼n湽z満m溁y溂l溄f溇l沩w溊b溋y溌h溍j溎y溑s溒y溓l溔y溕m准z溗c溙t溚t溛w沟g溞s溠z溡s溣l溤m溦w溨z溩w温w溬q溭z溮s溰a溵y溸s溹s湿s溾a溿p滀c滃w沧c灭m滆g滈h滉h滊q涤d滍z荥x滐j滒g滘j滙h滛y滝l滣c滧y滪y滫x沪h滭b滮b滞z滰j滱k渗s滳s滶a卤l浒h滺y滻c滼f滽y滚g满m漀q渔y漃j漄y漅c漇x漈j漊l漋l漌j漍g漎c漑g漒q漖j漗c漘c漙t沤o漛t漜y漝x漞m漟t漡s汉h涟l漥w漦c漧g漨f渍z漮k漰p涨z漴c漵x漷h渐j漺s漻l漼c漽t浆j潀c潂h潃x潄s潅g潈z潉k潊x潌z潏y潐j泼p潒d潓h洁j潕w潖p潗j潙w潚s潜q潝x泻x潠s潡d润r潥s潧z潨c潩y潪z潫w潬t浔x溃k潱y潳t潵s潶h滗b潹c潻s潽p潾l涠w涩s澁s澂c澃j澅h浇a涝l澊c澋j澏h澐y澑l澒h澓f澔h澕h澖x涧j澘s澙x澛l澝n澞y澟l渑m澢d澣h泽d澥x澦y澨s泶x澪l澫w澬z澭y浍k澯c澰l淀d澲y澴h澷m澸g澺y澻s澼p澽j澾t澿q浊z浓n濄g濅j濆p濇s濈j濊h濋c濌t濍s濎d濏s濐z濓l濔m濖s濗m泞n濙y濚y濜j濝q济j濢c濣w涛t濥y濦y濧d濨c濩h濪q滥l濬j濭a潍w滨b濲g濳q濴y濶k濷f濹b溅j濻w泺p濽z滤l濿l瀁y瀂l瀃s瀄z滢y渎d瀈h瀊p渖s瀌b瀍c瀎m浏l瀒s瀓c瀔g濒b瀖h瀗x泸l瀙q瀜r沥l瀞j潇x潆y瀡s瀢w瀤h瀥x瀦z泷l濑l瀩d瀪f瀫h瀬l瀭s瀮l瀯y瀱j潋l瀳j瀴y瀶l瀷y瀸j瀺c瀻d瀼r瀽j澜l瀿f灀s灁y灂z沣f滠s灅l灆l灇c灈q灉y灊q灋f灍j灎y灐y洒s灒z灓l灔y灖m灗s滩t灙d灚j灛c灏h灟z灠l灡l灢n湾w滦l灥x灦x灧y灨g灪y灮g灱x灲x灳h灴h灷z灹z灺x灻c灾z炁q炂z炄n炆w炇p炈y炋p炌k炐p炑m炘x炚g炛g炞b炟d炠x炡z炢z炣k炤z炥f炦b炧x炨x炩l炪z炰p炴y炵t炶s为w炿z烄j烅x烆h烇q烉h烋x烌x烍x烎y乌w烐z烑y烒s烓w烔t烕m烖z烗k烚x烜x烝z烠h烡g烢c烣h烥c烰f烱j烲x烳p烃t烵z烶t烸h烺l烻y烼x烾c烿r焀h焁x焂s焃h焄x焅k焆j焇x焈x焋z焌j焍d焎x焏j焒l焔y焗j焛l焜k焝h焞t焟x焠c无m焢h焣j焤f焥w焧z焨f焩p焪q焫r焬x焮x焳j焴y焵g焸x焹g焻c焼s焽x焾n焿g煀q煁c煂h煃k煄z煆x煇h煈f炼l煋x煍j煏b煐y煑z炜w煓t煔s煕x煖n煗n煘c烟y煚j煛j煝m煟w煠y煡j茕q煣r焕h烦f煪q煫s炀y煭l煯j煰z煱g煴y煵n煶s煷h煹g煻t煼c煾e煿b熀h熁x熂x熃w熅y熆h熇h熈x熉y熋x熌s熍q熎y熐m熑l荧y熓w熕g熖y炝q熚b熛b熜c熝l熞j熡l熢p熣s熤y熥t熦j熧z熩h熪y熫z熭w熮l熯h熰o热r熲j熴k熶c熷z熸j熺x熻x熼y熽x炽c熿h燀c烨y燂t焰y燅x燆q燇j灯d炖d燊s燋q燍s燏y磷l燑t烧s燖x燗l燘m烫t燚y燛j焖m燝z营y燡y燣l燤t灿c燨x燩q燪z燫l烛z燯l燱y燲x燳z烩h燵t燶n燷l燸x燺h燻x烬j燽c焘d燿y爀h爂b爃r爄l爅m爇r爈l爉l爊a爋x爌k烁s爎l爏l炉l爑j爒l爓y爔x爕x爖l爘c爚y烂l爜c爞c爟g爡c爢m爣t爤l爦l爧l爩y爫z争z爮p爯c爲w爳h爴j爷y爼z尔e牀c牁k牂z牃d牄q牅y牉p牊c牋j牎c牏y牐z牑b牓b牕c牗y牍d牚c牜n牞j它t牣r牤m牥f牨g瘪b牫g牬b牭s牰y牱k牳m牶q牷q牸z牻m牼k牵q犂l犃p犅g犆z犇b犈q犉c犌j犎f犐k犑j犓c犔x犕b荦l犗j犘m犙s犚w犁l犜d犝t犞q犠x犡l犊d犣l犤b犥p犦b牺x犨c犩w犪k犫c犮q犱j犲c犳z犵g犺k犻p犼h犽y犾y犿h状z狅k狆z狇m狉p狊j狋y狌s狏t狑l狓p狕y狖y狘x狛b狜k狝x狟h狢h狣z狤j狥x狦s狧t狪t狫l狵m狶x狭x狈b狾z狿y猀s猂h猄j猅p猆f猇x猈b猉q猋b猌y猐q猑k猒y猔z猘z狰z猚y猟l猠c猣z猤g猦f猧w猨y猭c猯t猰y猲h猳j猵p犹y猺y狲s猼b獀s獁m獂h獃d狱y狮s獆h獇q獈y獊c獋h獌m奖j獏m獑c獓a獔h獕s獘b獙b獚h獛p獜l獝x獞t獟y獡s獢x獣s獤d獥j獦g獧j独d獩h狯k猃x猕m狞n獱b获h獳r獴m猎l獶n犷g兽s獭t献x獽r獿n猡l玁x玂q玃j玅m兹c玈l玊s玌q玍g玏l玐b玒h玓d玔c玕g玗y玘q玙y玚c玜h玝w玞f玠j玡y玣b玤b玥y玦j玧m珏j玪j玬d玭p玱q玴y玵a玶p玸f玹x玼c玽g玾j玿s珁c珃r珄s珅s珆y珇z珋l珌b珎z珒j珓j珔j珕l珖g珗x珘z珚y珛x珜y珝x珟s珡q珢y珣x珤b珦x珨x珪g珫c珬x佩p珯l珰d珱y珳w珴e珵c珶d珷w珸w珹c珺j珻m珼b珽t现x珿c琀h琁x琂y琄x琇x琈f琋x琌l琍l琎j琑s琒f琓w琔d琕p琖z琗c琘m琙y琜l琝m琞s琟w琠t琡s琣b琤c琧e琩c琫b琭l琯g琱d琲b琷q琸z琹q珐f琻j琽d琾j珲h瑀y瑂m瑃c瑄x瑅t瑆x瑇d瑈r瑉m玮w瑌r瑍h瑎x瑏c瑐j瑑z瑒y瑓l瑔q瑖d瑝h瑠l瑡s瑢r琐s瑶y瑥w瑧z瑨j莹y玛m瑫t瑬l瑮l琅l瑱t瑲q瑳c瑴j瑵z瑹t瑺c瑻k瑼z瑽c瑿y璂q璄j璅s璆q璈a琏l璌y璍y璏z璑w璒d璓x璔z璕x璖q璗d璘l璙l璚j璛s璝g璟j璡j璢l玑j璤h璥j瑷a璪z璫d璬j璭g璮t璯h环h璱s璲s璳t璴c璵y璶j璷l璸b璹s璻z璼l玺x璾j璿x瓀r瓁w瓂g瓃l瓄d瓅l瓆z瓇r瓈l瓉z琼q瓋t瓌g瓍s瓎l珑l瓐l瓑l瓓l璎y瓕m镶x瓗q瓘g瓙d瓒z瓛h瓝b瓟b瓡z瓥l瓨x瓩q瓪b瓫p瓬f瓭d瓳h瓵y瓸b瓹j瓺c瓻c瓽d瓾w甀z甁p甂b甃z甅l甆c甈q甉x甊l甋d瓯o甎z甐l甒w甔d瓮w甖y甗y甛t甝h甞c甠q甡s产c産c甤r甧s甪l甮f甴y甶f甹p甼t甽z甿m畁b畂m畃x畆m畇y畉f畊g畍j畐f畑t畒m畓d畕j畖w畗d畘n亩m畟j畠t畡g毕b畣d畤z畨f畩y画h畲s畭s畮m畯j异y畱l畳d畵h畷z畺j畻c畽t畾l疀c疁l疂d疄l疅j畴c疈p疉d叠d疌j疍d疎s疐z疓n疕b疘g疛z疞x疢c疦j疧q疨y疩c疭z疶x疷z疺f疻z痱f痀g痁d痆n痋t痌t痎j痏w痐h痑t痓z痗m痉j痚x痜t痝m酸s痡f痥d痩s痬y痮z痯g麻m痵j痶t痷a痸c痹b痻m痽d痾k瘂y瘄c瘆s瘇z瘈z瘉y疯f疡y瘎c瘏t瘑g瘒w痪h瘔k瘖y瘚j瘜x瘝g瘗y疮c瘣h疟n瘨d瘬z瘮s瘯c瘱y瘲z瘶s瘷s瘘l瘻l瘽q疗l癄q癅l痨l痫x癈f瘅d癋h癎x癏g癐g癑n癒y癓w癕y癗l疠l癙s癚d癛l癝l癠j痴c痒y疖j症z癧l癨h癞l癪j癣x瘿y瘾y瘫t癫d癳l癴l癵l癶b癹b発f发f癿q皀b皁z皃m皅p皉c皊l皌m皍j皏p皐g皒e皔h皕b皗c皘q皑a皛x皜h皝h皞h皟z皠c皡h皢x皣y皥h皦j皧a皨x皩h皪l皫p皬h皭j皯g疱p皳q皵q皶z皷g皲j皹j皱z隳h皼g皽z皾d盀q盁y盃b啊a阿a埃a挨a哎a哀a癌a蔼a矮a艾a碍a隘a鞍a氨a安a俺a按a暗a岸a胺a案a肮a昂a盎a凹a敖a熬a翱a袄a傲a懊a澳a芭b捌b扒b叭b吧b笆b八b疤b巴b拔b跋b靶b把b耙b霸b罢b爸b白b柏b百b佰b拜b稗b斑b班b搬b扳b般b颁b板b版b扮b拌b伴b瓣b半b办b绊b邦b梆b榜b膀b绑b棒b磅b蚌b镑b傍b谤b苞b胞b包b褒b盄d盇h盉h盋b盌w盓y盕f盙f盚q盗d盝l盏z盠l盢x监j盦a卢l盨x盩z荡d盫a盬g盭l盰g盳m盵q盷x盺x盻x盽f盿m眀m眂s眃y眅p眆f眊m県x眎s眏y眐z眑y眒s眓h眔d眕z眖k眗j眘s眛m眜m眝z眞z眡s眤n眦z眧c眪b眫p眬l眮t眰d眱d眲n眳m眴s眹z眻y眽m众z眿m睂m睄s睅h睆h睈c睉c睊j睋e睌m睍x睎x困k睒s睓t睔g睕w睖l睗s睘q睙l睁z薄b雹b保b堡b饱b抱b暴b豹b鲍b爆b杯b碑b悲b卑b北b辈b贝b钡b倍b焙b被b奔b本b笨b崩b绷b甭b泵b蹦b迸b逼b鼻b比b鄙b笔b彼b碧b蓖b蔽b毖b庇b闭b敝b弊b必b辟b壁b臂b避b陛b鞭b边b编b贬b扁b便b变b卞b辨b辩b辫b遍b彪b膘b表b鳖b憋b彬b斌b宾b兵b冰b柄b丙b秉b饼b炳b睝l睐l睟s睠j睤b睧h睩l睾g睭z睮y睯h睰m睱x睲x睳h睴h睵z睶c睷j睸m睺h睻x睼t瞁x瞂f瞃w瞆g眯m瞉k瞊d瞋c瞐m瞕z瞖y瞗d瞘k瞙m瞚s瞛c瞜l瞝c瞒m瞡g瞣w瞤s瞦x瞨p瞫s瞮c瞯x瞱y瞲x瞴w瞶g瞷j瞸y瞹a瞺h睑j瞾z矀w矁c矂s矃n矄x矅y矆h矈m矉p矋l矌k矎x矏m矐h矑l矒m矔g矕m矖x矘t矙k瞩z矝j矞y矟s矠z矡j矤s病b玻b菠b播b钵b波b博b勃b搏b铂b箔b伯b帛b舶b脖b膊b渤b泊b驳b捕b卜b哺b补b埠b不b步b簿b部b怖b猜c裁c材c才c财c睬c踩c彩c菜c蔡c餐c蚕c苍c舱c藏c操c糙c槽c曹c草c策c蹭c插c茬c茶c查c碴c搽c察c岔c差c诧c拆c柴c豺c蝉c馋c谗c缠c阐c颤c昌c猖c矦h矨y矪z矫j矰z矱y矴d矵q矷z矹w矺z矻k矼g砃d砄j砅l砆f砇m砈e砊k砋z砎j砏p砐e砓z砕s砙w砛j砞m砠j砡y砢l砤t砨e砪m砫z砮n砯p砱l炮b砳l砵b砶p砽y砿k硁k硂q朱z硄k硆e硈q硉l硊w硋a硍k硏y硑p硓l硔h硘q硙w硚q硛c硜k硞q硟c硠l硣x硖x硥m硦l硧y砗c硩c砚y硰s硱k硲y硳z硴h硵l硶c硹s硺z硻k硽y硾z硿k碀c碁q碂z碃q常c长c肠c敞c唱c倡c超c抄c钞c朝c嘲c潮c巢c吵c炒c车c扯c撤c掣c澈c郴c臣c辰c晨c忱c沉c陈c趁c衬c称c城c橙c成c呈c乘c程c澄c诚c承c逞c骋c秤c持c匙c池c迟c弛c驰c齿c侈c赤c翅c斥c充c虫c崇c抽c酬c踌c稠c愁c筹c仇c绸c瞅c丑c臭c初c出c躇c锄c雏c滁c除c楚c碄l碅j碆b碈m碊j碋h碏x碒y碔w碕q碖l碙n碝r碞y碠d碢t碤y碦k碨w硕s碪z碫d碬x砀d碮t碯n碵t碶q碷d碸f确q码m碿s磀e磃s磄t磆h磇p磈k磌t磍x磎x磏l磑w磒y磓d磖l磗z磘y砖z磛c磜q磝a磞p磟l磠l磡k磢c碜c磤y磥l磦b碛q磩z磪c磫z磮l矶j磱l磳z磵j磶x磸d磹d磻b磼j硗q磾d磿l礀j礂x礄q礆j礇y礈z礉h礊k礋z礌l础c矗c搐c触c处c揣c川c穿c椽c船c喘c串c窗c幢c床c闯c吹c炊c捶c锤c垂c春c椿c醇c唇c淳c纯c蠢c戳c绰c疵c茨c磁c雌c辞c慈c瓷c词c此c刺c赐c次c聪c葱c匆c粗c醋c簇c促c蹿c篡c窜c摧c崔c催c脆c瘁c粹c淬c翠c村c存c磋c撮c搓c措c挫c错c达d答d瘩d打d大d呆a歹d傣d戴d殆d代d贷d袋d待d逮d礍j礏y礐q礑d礒y礔p礕p礖y礗p礘e礚k礛j礜y礝r礟p礠c礡b礢y礣m礥x矿k礧l礨l礩z砺l砾l矾f礭q礮p礯y礰l砻l礲l礳m礵s礷j礸c礹y礽r礿y祂t祃m祄x祆x只z祊b祋d祌z祎y祏s佑y祑z祒t祔f秘b祙m祡c祣l祤y祦w祩z祪g祫x祬z祮g祰g祲j祳s祴g祵k祹t祻g祼g祽z祾l禄l禂d禃z禆b禇c禉y禋y禌z祸h禐y禑x禒x怠d耽d丹d郸d胆d旦d氮d但d淡d诞d蛋d党d刀d蹈d倒d祷d到d稻d悼d道d德d得d的d蹬d登d等d瞪d凳d邓d堤d低d滴d迪d笛d狄d翟d嫡d底d地d蒂d第d帝d弟d递d缔d颠d掂d滇d碘d点d典d靛d电d佃d甸d店d惦d奠d殿d碉d叼d凋d刁d掉d钓d调d跌d爹d碟d蝶d迭d谍d禓s禔z禕y禖m禗s禘d禙b禛z禜y禝j禞g禟t禠s禡m禢t禣f禤x禥q御y禨j禩s禅c禫d禬g禭s礼l禯n祢m禲l禴y禵t禶z禷l禸r禼x秃t秂r秄z秅c秇y籼x秊n秌q秏h秐y秓z秔j秖z秗y秙k秚b秛p秜n秝l秞y秠p秡b秥n秨z秪d秬j秮h秱t秲s秳h秴h秵y秶z秷z秹r秺d秼z秾n秿f稁g稄x税s稇k秆g稉j稊t稌s稏y稐l稑l稒g稓z稕z稖b稘j稙z稛k棱l丁d盯d叮d钉d顶d鼎d锭d定d订d冬d董d懂d侗d恫d洞d兜d抖d陡d豆d逗d痘d都d督d毒d读d堵d睹d赌d杜d镀d肚d度d渡d妒d端d短d锻d段d缎d堆d队d墩d蹲c敦d顿d囤d钝d盾d遁d掇d哆d多d垛d躲d朵d跺d舵d剁d惰d蛾e峨e鹅e俄e额e讹e娥e厄e扼e遏e鄂e饿e恩e而e耳e饵e洱e二e稝p禀b稡z稢y稤l稦y稧x稨b稩j稪f稫p稭j种c稯z稰x稲d稴x稵z稶y稺z稾g谷g穁r穂s穃r穄j穅k穇c穈m穊j穋l稣s颖y穏w穐q穒k穓y穔h穕q穖j穘x穚j穛z穜t穝z穞l穟s穑s秽h穣r穤n穥y穦p穧j穨t稳w穪c穬k穭l穮b穯s穱z穲l穳c穵w穻y穼s穽j穾y窂l窅y窇b窉b窊w窋z窌j窎d窏w窐w窓c窔y窙x窚c窛k窞d窡z窢h贰e罚f筏f伐f乏f阀f法f藩f帆f番f翻f樊f钒f繁f凡f反f返f范f贩f犯f饭f芳f方f肪f房f防f妨f访f纺f放f菲f非f啡f飞f肥f匪f诽f吠f肺f沸f费f芬f酚f吩f氛f分f纷f焚f汾f粉f份f忿f粪f丰f封f蜂f峰f锋f风f烽f逢f冯f缝f讽f奉f凤f否f敷f肤f孵f扶f拂b辐f幅f氟f符f伏f俘f服f窣s窤g窝w洼w窫y穷q窑y窰y窱t窲c窴t窵d窭j窷l窸x窹w窥k窻c窼c窾k竀c竁c竂l竃z窍q竆q窦d竈z竉l窃q竍s竎f竏q竐c竑h竒q竓h竔s竗m竘q竚z竛l竜l竝b竡b竢s竤h竧j竩y竫j竬q竮p竰l竱z竲c竳d竴c竵w竞j竷k竻l竼p竾c笀m笁z笂w笅j笇s笉q笌y笍z笎y笐h笒c笓b笖y笗d笘s笚d笜z笝n笟g笡q笢m笣b笧c笩f笭l浮f涪f福f袱f弗f甫f辅f俯f釜f斧f脯f腑f府f腐f赴f副f覆f赋f傅f付f阜f父f腹f负f富f讣f附f缚f咐f噶g嘎g该g改g概g钙g盖g溉g甘g柑g竿g肝g赶g感g敢g赣g钢g缸g肛g纲g港g篙g皋g高g膏g羔g糕g搞g镐g稿g哥g歌g戈g鸽g胳g疙g割g革g葛g格g蛤g阁g隔g铬g各g给g根g跟g耕g更g庚g羹g笯n笰f笲f笴g笵f笶s笷m笹t筇q笽m笿l筀g筁q筂c筃y筄y筈k筊j笋s筎r筓j筕h筗z筙l筜d筞c筟f筡t筣l筤l筥j筦g笕j筨h筩t筪x筫z筬c筭s筯z筰z筶g筸g筺k筼y筽o筿x箁p箂l箃z箅b箆b箉g箊y笺j箌z箎c筝z箑s箒z箓l箖l箘q箙f箚z箛g箞q箟j箠c箣c箤z箥p箮x箯b箰s箲x箳p箵x箶h箷s箹y箺c箻l箼w箽d箾s箿j节j篂x篃m埂g耿g梗g工g攻g功g恭g龚g供g躬g公g弓g巩g汞g拱g贡g共g钩g勾g苟g狗g垢g购g辜g菇g咕g箍g估g沽g孤g姑g鼓g古g蛊g骨g股g故g顾g固g刮g瓜g寡g褂g乖g怪g棺g关g官g冠g观g管g馆g罐g灌g贯g光g逛g瑰g规g圭g硅g闺g轨g鬼g诡g癸g桂g跪g贵g辊g棍g锅g郭g果g裹g过g哈h篅c篈f筑z篊h箧q篍q篎m篏q篐g篔y篕h篖t篗y篘c篛r篜z篞n篟q筱x篢l篣p笃d篧z篨c筛s篫z篬q篭l篯j篰b篲h筚b篴z篵c篶y篸c篹s篺p篽y篿t箦z簁s簂g簃y簄h簅c簆k簈p簉z簊j篓l簎c簐n蓑s簒c簓s簔s簕l簗z簘x簙b簚m簛s簜d簝l箪d簠f简j簢m篑k簤d簥j簨z簩l箫x簬l簭s簮z簯q簰p簱q簲p簳g簴j簵l簶l檐y簹d簺s簻z簼g签q帘l籂s骸h孩h海h氦h亥h害h骇h酣h憨h邯h韩h涵h寒h函h喊h罕h翰h撼h捍h旱h憾h悍h焊h汗h夯b杭h航h壕h嚎h豪h毫b郝h好h耗h号h浩h呵h喝h荷h菏h核h禾h和h何h合g盒h貉h阂h河h涸h赫h褐h鹤h贺h嘿h黑h痕h很h狠h恨h亨h衡h轰h哄h烘h虹h鸿h洪h宏h弘h红g喉h侯h猴h吼h厚h候h后h呼h乎h忽h瑚h葫h胡h蝴h狐h糊h湖h篮l籄k籅y籆y籇h籈z籉t籊t籎y籏q藤t籑z籒z籓f籔s籕z籖q籗z籘t籙l籚l籛j箨t籝y籞y籁l笼l籡s籢l籣l龠y仠g籦z籧j籨l笾b簖d籫z篱l籭s箩l籯y籰y籱z吁x籵f籶s籷z籸s籹n籺h籾n籿c粀z粁q粂z粃b粄b粅w粆s粇k粈r粊b粋c粌y粍z佂z粏t粐h佹g侀x粔j粖y粙z粚c粛s粠h侼b侽n粣c粦l粨b粩l粫e粬q粭h粯x粰f粴l粤y粶l粷j粸q粺b粻z弧h虎h唬h护h互h花h华h猾h滑h化h话h槐h淮h桓h还h缓h患h豢h宦h幻h荒h慌h黄h磺h蝗h簧h皇h凰h惶h煌h晃h幌h恍h谎h灰h辉h徽h恢h蛔h回h悔h慧h卉h惠h晦h贿h讳h诲h绘h荤h昏h婚h魂h混h豁h活h伙h火h或h惑h霍h货h圾j基j畸j稽j箕j粿g糀h倄y糃t糄b糆m糉z糋j糎l糏x糐f糑n糒b糓g糔x糘j糚z糁s糡j馍m糣s糤s糥n糦x粮l糩k糪b糫h糬s糭z糮x糱n粝l糳z籴d糵n粜t糷l糹s糺j糼g糽z纠j纪j紁c纣z紃x约y纡y纥g纨w纫r纹w紌q纳n紎z紏t纽n紑f紒j纾s纰p紖z纱s紘h纸z级j纭y紝r紞d紟j紣c紥z紦h紨f紩z紪q紬c紭h扎z细x绂f绁x绅s紴b紵z紶q偟h肌j饥j迹j激j讥j鸡j姬j绩j缉j吉j棘j辑j籍j集j及j急j疾j即j嫉j脊j己j蓟j技j偤y冀j季j伎j祭j悸j寄j寂j计j记j既j忌j际j妓j继j嘉j枷j佳j加j荚j颊j贾g甲j钾j假j稼j架j驾j嫁j尖j间j煎j兼j肩j艰j缄j茧j柬j硷j剪j荐j鉴j践j贱j见j键j箭j件j紷l紸z绍s绀g紻y绋f紽t紾z绐d绌c絁s终z弦x组z絅j絇q絈m絉s絊z絋k経j絍r绗h絏x傶q结j絑z絒c傸c絓g絔b绝j絖k絗h絘c絙h絚g绦t絜j絝k绞j絟q絠g络l绚x絣p絤x絥f絧t绒r絩t絪y絫l絬x絭j絯g絰d统t丝s絴x絵h絶j絸j绢j絺c絻w絼z絽l絿q綀s綂t绡x綄h綅q绠g綇x绨t綉x綊x綋h綌x綍f綎t绥s綐d捆k綒f经j綔h綕z綖y綗j綘f健j舰j饯j建j僵j姜j江j疆j蒋j讲j匠j酱j降j蕉j椒j礁j焦j胶j交j郊j骄j嚼j铰j脚j狡j角j饺j缴j教j酵j轿j较j叫j窖j揭j接j皆j秸j街j阶j截j劫j桔j捷j睫j竭j姐j戒j藉j芥g界j借j介j疥j诫j巾j筋j斤j金j兠d今j津j襟j紧j锦j谨j进j靳j禁j近j浸j継j続x综z綝c缍d綟l绿l綡j绻q綤s綧z綨j綩w綪q綫x绶s维w綯t绾w网w綳b缀z綶g綷c纶g绺l绮q绽z綼b绫l绵m緀q緁j緂t緃z绲g緅z緆x缁z緈x緉l绯f緌r緍m緎y総z緐f緑l绪x緓y緔s緕z緖x缃x缂k线x緜m緟c缗m緢m缘y緤x緥b缌s緧q緪g緫z缅m緭w緮f纬w緰x缑g缈m緳x练l緵z缏b緷g緸y缇t緺g荆j兢j茎j睛j晶j鲸j京j惊j精j粳j井j警j景j颈g静j境j敬j镜j靖j竟j炯j窘j揪j究j玖j韭j久j灸j九j酒j救j旧j臼j舅j咎j就j疚j鞠j拘j狙j疽j居j驹j菊j咀j矩j举j沮j聚j拒j巨j具j距j剏c踞j锯j俱j句g炬j捐j鹃j娟j倦j眷j撅j攫j抉j掘j倔j爵j觉j诀j均j菌j钧j军j君j峻j致z緼w緽c緾c緿d縀x縁y縂z縃x縄s縅o縆g縇s萦y缙j缢y缒z縌n縍b縎g縏p绉z缣j縒c縓q縔s縕y縖x縗c縘x縙r縜y缜z缟g缛r縠h縡z縢t县x縤s縥z縦z縧t縨h縩c縪b縬c缡l缩s演y縰x纵z缧l纤q缦m絷z缕l縸m缥p縺l縼x总z縿s繀s繂l繄y缫s缪m襁q繉s繊x繋j繌z繍x繎r繐s缯z繓z织z缮s繖s繗l繘j繙f缭l繛c繜z勩y俊j竣j浚j郡j骏j喀k咖g卡k咯g开k揩k楷j慨k刊k堪k勘k坎k砍k看k康k慷k糠k扛g抗k亢g炕k拷k烤k靠k坷k苛k柯k棵k磕k颗k科k咳h可k渴k刻k客k课k肯k啃k坑k吭h空k恐k孔k控k口k扣k寇k枯k哭k窟k苦k酷k裤k夸k垮k挎k跨k胯k筷k快k款k筐k狂k框k眶k亏k盔k葵k奎k魁k傀g绕r繟c繠r绣x缋h繣h繤z繥x繦q繧w繨d绳s繬s繮j缳h缲q繱c繲x繴b繵d繶y繷n繸s绎y繺s繻x缤b缱q繿l纀p纁x纃z纅y纆m纇l缬x纉z纩k续x累l纎x纐k纑l纒c缨y厠c纗z缵z纙l缆l纝l纞l纮h纴r纻z纼z绖d绤x绬y绹t缊w缐x缞c缷x缹f缻f缼q缾p罀z罁g罃y罆g罇z罉c罊q罋w罂y罍l罎t罏l罒w罓w馈k愧k坤k昆k括g廓k阔k垃l拉l喇l蜡l腊l辣l啦l莱l赖l蓝l婪l阑l兰l谰l览l榔l狼l廊l郎l朗l浪l牢l老l佬l姥l酪l烙l勒l雷l镭l蕾l磊l儡l肋l类l楞l冷l厘l梨l黎l狸l离l理l李l鲤l莉l荔l吏l丽l利l傈l例l俐l罙s罛g罜z罝j罞m罠m罣g罤t罥j罦f罧s罫g罬z罭y罯a罳s骂m罶l罸f罺c呩s罻w罼b罽j罿c羀l羂j羃m羄z罴p羇j羁j羉l羍d羏y羐l羑y羓b羕y羖g羗q羙m羠y羢r羟q羡x义y羬x羱y羴s羺n羾h翀c翃h翆c翇f翈x翉b翋l翍p翏l翐z翑q习x翓x翖x翗k翙h翚h翛x翜s翝h翞j翢d翣s哷l痢l立l粒l隶l力l璃l哩l联l莲l连l镰l廉l脸l链i啅z粱l良l辆l量l晾l亮l谅l撩l聊l僚l燎l寥l辽l潦l撂l镣l廖l料l列l烈l劣l琳l林l霖l临l邻l鳞l淋l赁l啙z啛c吝l拎l玲l菱l零l龄l铃l伶l羚l凌l灵l陵l领l另l令l溜l琉l榴l硫l馏l留l瘤l流l柳l六l龙l聋l窿l翤c翧x翨c翪z翫w翬h翯h翲p翴l翵h翶a翷l翸p翘q翽h翾x翿d耂l耇g耈g耉g耊d耎n耏n耓t耚p嗘j耝q耞j耟j耡c耣l耤j耫z耧l耭j耮l耯h耰y耲h耴y耹q耺y耼d耾h聀z聄z聅c聇z聈y聉w聏e聐y聑t聓x聕h圣s聗l聙j聛b聜d聝g闻w聟x聠p聡c聢x聣n聤t聥j聦c聧k聨l聫l聬w聭k聮l声s耸s聴t聩k聂n职z聸d聍n聺q聻n嘓g聼t听t拢l陇l漏l陋l芦l颅l虏l鲁l麓l碌l露l路l赂l鹿l潞l录l陆l驴l铝l旅l履l氯l律l率l卵l掠l略l轮l噵d论l萝l螺l逻l锣l骡l裸g落l洛l嚍j骆l嚒m蚂m马m嘛m埋m买m麦m卖m迈m脉m馒m蛮m蔓m曼m慢m漫m肁z肂s肃s肈z肊y肍q肎k肏c肐g肑d囄l肔c肕r肗r肙y肞c肣q肦b肧p肨p肬y肰r肳w肵q肶p肹x肻k胅d胇b胈b胉b胊q坺f垀h胏z胐f胒n胓p胔z胕f胘x胟m胠q胢q胣c胦y胮p胵c胷x胹e埱c埶y胾z胿g脀z脁t脃c脄m胁x脇x脋x脌n脕w脗w脙x胫j脜r脝h脟l脠s脡t脢m脤s脥j塈x睃s脨c修x脪x脱t脭c脮n脰d脳n脴p脵g脷l胀z脼l脽s脿b谩m芒m墽q茫m盲m氓m壔d忙m莽m猫m茅m锚m毛m矛m铆m卯m茂m冒m帽m貌m贸m么m玫m枚m梅m酶m霉m煤m眉m媒m镁m每m美m昧m寐m妹m媚m门m萌m檬m盟m锰m猛m孟m醚m靡m糜m迷m谜m米m觅m泌b蜜m密m棉m眠m冕m免m勉m娩m面m苗m妌j描m瞄m藐m秒m妛c妭b渺m妙m蔑m民m抿m皿m敏m姃z闽m明m螟m鸣m铭m名m命m谬m摸m腀l腁p腂g腃j腄c腅d腇n腉n腍r肾s腏z腒j腖d腗p腘g腛w腜m腝r腞z娊x脶l腢o腣d腤a脑n腨s腪y肿z腬r腯t腲w腵j婂m腶d腷b膁q腽w膄s膅t膆s膇z膉y膋l膌j膍p膎x膐l膒o膓c膔l膕g膖p膗c膙j膞z膟l膡y膢l膤x膥c膧t腻n膫l膬c膭k膮x媑z膯t膰f膱z膲j膴h媝q媡l膵c膶r膷x膸s膼z脍k脓n臄j臅c臇j臈l臋t脐q臎c膑b臐x臑n嫿h嬃x嬓j臓z摹m蘑m模m嬜x膜m磨m摩m嬟y魔m抹m末m莫m墨m默m沫m漠m寞m陌m谋m牟m某m拇m牡m姆m母m墓m暮m幕m募m慕m木m目m孨z睦m牧m穆m拿n哪n孶z钠n那n娜n氖n乃n奶n耐n奈n南n男n难n囊n闹n淖n呢n馁n嫩n能n妮n霓n倪n泥n尼n匿n逆n溺n蔫n拈n年n碾n酿n鸟d尿n捏n孽n镊n镍n涅n您n凝n臔x臕b臖x臗k臙y胪l臛h臜z臝l臞q脏z脔l臡n臢z卧w臦g臩g臫j臮j臯g臰c岅b臲n臵g臶j臷d臸z臹x岟y臽x臿c舃x与y兴x岪f岾z峊f舎s舏j舑t峣y舓s舕t铺p舗p峮q舘g舙h舚t舝x舠d舤f舥p舦t舧f舩c舮l舲l舼q峾y舽p艀f艁z艂f艃l艅y艆l艊b艌n艍j艎h艐k艑b艒m艓d艔d嵽d嶈q艖c艗y艛l艜d艝s艞y艠d艡d艢q舣y艥j艧h艩q牛m扭n钮n嶪y农n弄l奴n努n怒n女n暖n虐n挪n懦n糯n诺n哦e巆y鸥o藕o偶o巏q啪p趴p爬p帕p怕p琶p拍p排p牌p徘p湃p派p攀p潘f磐p盼p畔p判p叛p乓p庞p耪p胖p咆p刨b袍p跑p泡p呸p胚p培p裴p赔p陪p配p沛b盆p砰p抨p烹p澎p彭p蓬p棚p硼p篷p膨p朋p鹏p捧p碰p坯p砒p霹p批p披p劈p琵p毗p艪l舻l艬c艭s艵p艶y艳y艹a艻l艼d幯j庅m庺s芃p芅y芆c芇m廐j芉g芌y苄b芓z芔h芕s弐e芚t芛w芞q芠w芢r芣f芧x芲l弞s芵j芶g芺a刍c芼m芿r苀h苂c苃y苅y苆s苉p苐d苖d苙l苝b苢y苎z苨n苩p苪b苬x苭y苮x苰h徛j苲z徦j苳d苵d苶n苸h苺m苼s苽g苾b苿w徴z茀f徸z茊z茋z茐c茒y茓x茖g茘l忇l茙r茝c茞c怓n茠h茡z茢l茣w茤j茥g茦c茩h茪g茮j怱c茷f茻m茽z啤p脾p疲p皮p匹p恎d痞p僻p屁p譬p篇p偏p片p骗p恜c飘p瓢p票p撇p瞥p拼p频p贫p品p聘p乒p坪p苹p萍p平p瓶p评p屏b坡p颇p婆p破p魄b迫p粕p剖p莆p葡p菩p蒲p埔b圃b普p浦p谱p曝b瀑b期j欺q妻q七q漆q柒q沏q其j棋q奇j歧q畦q崎q愂b齐j祈q祁q起q岂q乞q企q契q器q迄q汽q泣q愪y讫q掐q茾q茿z荁h荂f荄g荅d愲g荈c荋e荌a荍q荎c荓p荕j荖l荗s荘z荙d荝c荢z慃y荰d荱w荲l荳d荴f荵r荶y荹b荺y荾s荿c莀c莁w莂b莃x莄g莇z莈m庄z莋z莌t莏s莐c莑p莔m莕x憉p莗c莙j莚y莝c莟h莡c莣w莤s莥n莦s憜d苋x莬w莭j莮n憡c莯m莵t莻n莾m莿c菂d菃q菄d菆c菈l菉l菋w菍n菎k菐p菑z菒g菓g菕l菗c憿j菙c菚z菛m菞l菢b菣q菤j菦q菧d菨j菫j懏j菬q菭t恰q洽q钎q铅q千q懡m迁q仟q谦q乾q黔q钱q钳q前q遣q谴q嵌k欠q歉q腔q羌q蔷q橇q锹q敲q悄q瞧q巧q鞘q撬q峭q俏q切q茄j且j怯q戵q侵q亲q秦q琴q勤q芹q擒q禽q沁q青q轻q卿q清q擎q晴q氰q情q顷q请q秋q丘q邱q求q囚q抯z酋q泅q趋q蛆q曲q躯q屈q驱q渠q菮g菳q庵a菵w拲g菶b菷z菺j挌g菼t菾t菿d萀h萅c苌c萈h萉f萐s萒y萓y萔t萕q萖w萗c萙k萚t萛j萞b萟y萠p萡b萢p萣d萩q萪k萫x万m萭y萮y萯f萰l萲x萳n萴c掤b莴w萶c萷s萹b萺m萻a萾y葀k葁j葂m葃z葄z葅z葇r葈x叶x葊a葋q葌j葍f葎l葏j葐p荭h葓h搮l摀w葕x葖t葘z葝q葞m葟h葠s葢g葤z摖q葥q苇w葧b葨w葪j葮d药y葰j葲q葴z摾j葹s葻l葼z取q娶q龋q趣q去q圈j颧q醛q泉q全q痊q拳q犬q券q撧j缺q炔g瘸q鹊q榷q裙q群q然r燃r冉r染r瓤r壤r攘r嚷r让r饶r惹r壬r擟m仁r人r忍r韧r任r认r刃r妊r扔r仍r日r戎r茸r擿t攂l蓉r融r熔r溶r攚w容r冗r揉r柔r肉r茹r蠕r儒r孺r如r辱r乳r汝r攺y入r褥r软r阮r蕊r瑞r锐r闰r若r弱r撒s萨s腮s鳃s塞s赛s三s叁s葽y葾y葿m蒀y蒁s蒃z蒄g蒅s蒆x蒍w蒏y蒐s蒑y蒒s蒓c莳s蒕y蒖z蒘r蒚l蒛q蒝y莅l蒟j蒠x蒢c蒣x蒤t蒥l蒦h蒧d蒨q蒩z蒪p蒫c蒬y蒭c蒮y蒰p蒱p蒳n斮z蒵x斳q蒷y蒻r蒾m荪s蓂m蓃s蓅l蓆x蓇g蓈l蓌c蓎t蓏l蓒x蓔y蓕g蓗z蓘g蓙g蓚t蓛c蓜p蓞d蓡s旤h蓢l蓤l蓧d蓨t蓩m蓪t蓫z蓭a苁c蓱p蓲q蓳j蓴c蓵j蓶w蓷t蓸c蓹y蓺y蓻z荜b蓾l蔀b蔁z蔂l散s桑s嗓s搔s骚s嫂s瑟s色s森s僧s莎s砂s沙s傻s啥s晇x煞s珊s苫s杉s山s煽s衫s闪s陕s擅s赡s膳s善s汕s晬z扇s商s赏s晌s上s尚s裳c梢s捎s稍s芍s勺s韶s少s哨s邵s奢s赊s蛇s舌s赦s射s涉s社s设s砷s申s呻s伸s身s深s娠s神s沈s暎y暔n甚s慎s生s甥s牲s蔃q暭h蔄m蔅y蔇j蔈b蔉g蔊h蔋d蔍l蔎s蔏s蔐d蔒h蔕d蔖c蔘s蔙x蔛h蔜a曌z曗y曻s蒌l蔠z蔢p蔤m茑n蔧h蔩y蔪j荫y蔮g蔯c蔰h蔱s蔲k蔳q枈p蔵z蔶z蔾l蔿w蕀j荨q蕂s蕄m蕅o蒇c蕇d蕋r蕌l蕍y荞q蕏z蕐h蕑j芸y蕔b莸y蕗l柛s荛r蕚e蕛t蕜f蕝j蕟f蕠r柨b蒉k蕣s蕥y蕦x蕧f柸b芜w蕫d蕬s萧x栭e蕯s蕰y蕱s蕳j蕵s蕶l蓣y蕸x桗d蕼s蕽n蕿x薀y薁y桜y省s盛c剩s失s施s诗s尸s虱s十s石d拾s什s梍z食s蚀s识s史s矢s使s屎s驶s始s式s示s士s世s梫q柿s事s拭s誓s逝s嗜s噬s适s棃l仕s侍s释s棇c饰s氏s市f恃s室s视s试s收s手s首s守s授s售s受s椃h瘦s蔬s梳s殊s抒s输s叔s舒s淑s椘c疏s赎s孰s熟s薯s暑s曙s署s蜀s黍s鼠s述s束s戍s竖s墅s庶s漱s薂x薃h楕t楖j楡y荟h薉h薋c芗x薍w薎m薐l薒c薓s薕l薖k榐z薗y薘d薙t薚t薝z薞s槀g薠f薡d薢x薣g薥s槁g薫x薬y薭b薱d槗q薲p薳y薴n薵c薶m薸p荠j薻z薼c薽z薾e薿n藀y藂c藃x藄q藅f藆j藇x藈k藊b藋d藌m荩j槸y藑q藒q藔l藖x藗s樐l藘l藙y藚x藛x艺y藞l藟l藠j藡d藢z藣b藦m藧h藨b薮s藫t藬t藭q藮q藯w藰l樼z樿s藲o藳g藴y藵b苈l藸z橩q恕s刷s耍s摔s衰c甩s栓s拴s霜s双s爽s谁s水s睡s吮s瞬s顺s舜s说s朔s斯s撕s嘶s思s私s司s死s肆s寺s嗣s四s似s饲s巳s松s颂s送s宋s讼s诵s搜s艘s嗽s酥s俗s素s檪l速s粟s僳s塑s溯s宿s诉s蒜s算s檾q櫈d櫐l虽s隋s随s髓s碎s穗s遂s隧s祟s櫢s梭s櫴l唆s索s锁s所s櫿y塌t他t她t欃c塔d蔺l欟g欦q藽q藾l歞e蘁w蘂r歬q殏q蕲q蘈t蘉m蕴y蘌y蘍x蘎j蘏j蘐x殭j毥x蘔j蘕p蘖n蘘r蘙y藓x蘛y蘜j蘝l蔹l蘟y蘠q蘡y茏l蘣t蘤h蘥y蘦l蘨y蘪m蘫l蘬g蘮j蘯d蘰k蘱l蘲l蘳h蘴f蘵z蘶w蘷k汦z汻h沕m蓠l蘻j蘽l蘾h虀j虁k泈z泘h虃j泿y虄s虅t洜l虇q虈x虉y虊l虋m虌b虒s虓x虖h虗x虘c虙f虚x虝h虠j浰l虡j虣b浲f虤y虥z虦z涖l涶t虩x虪s蹋t踏t胎t淃j抬t泰t酞t太t汰t坍t贪t檀t痰t潭t谭t谈t坦t毯t袒t碳t探t炭t渜n塘t搪t堂t棠t膛t唐t糖t倘c躺t淌t趟t掏t滔t萄t桃t逃t淘t陶t讨t套t特t腾t疼t誊t梯t湕j剔t踢t锑t提d题t蹄t啼t体t替t嚏t惕t涕t剃t湲y天t添t填t田t甜t恬t舔t腆t挑t眺t跳t贴t铁f帖t虭d虯q虰d虳j虴z虵s溳y滖s滜z滵m虸z蚃x蚄f漐z蚆b蚇c蚈q蚉w蚎y蚏y蚐j蚑q蚒t漹y潎p蚖y潣m蚗j蚘h蚙q蚚q蚛z蚞m蚟w蚡f蚢h蚥f蚦r蚫b澚a蚮d蚲p蚳c蚷j蚸l蚹f蚻z蚼g蚽p蚾p蛁d蛂b蛃b蛈t蛌t蛍y蛒g蛓c蛕h蛖m蛗f蛚l蛜y蛝x蛠l濵b蛡y蛢p蛣j濸c蛥s蛦y蛧w蛨m蛪q蛫g蛬q瀀y瀇w瀐j蛵x蛶j蛷q蛱j蜕t蛼c蛽b蛿h蜁x蜄s蜅f蚬x蜋l蜌b蜎y蜏y蜐j蜑d蜔d蜖h汀t廷t停t亭t庭t挺t艇t通t桐t酮t瞳t同t铜t彤t童t桶t灜y捅t筒t痛t偷t投t头t灩y透t凸t突t徒t途t屠t土t吐t兔t湍t推t颓t腿t褪t退t吞t屯t臀t拖t托t炏y鸵t陀t驮d炓l炗g驼t妥t拓t唾t挖w哇w蛙w娃w瓦w袜w炲t歪w外w豌w玩w顽w烷w完w碗w挽w晚w皖w惋w宛w烞p婉w腕w汪w王w烮l亡w枉w往w旺w望w忘w妄w威w蜙z蜛j蜝q蜟y蜠j蜤s蜦l蜧l蜨d蜪t蜫k蜬h蜭h蜯b蜰f蜲w蜳d蜵y蜶s蜸q蜹r蜺n蜼w蜽l蝀d蝁e蝂b蝃d蝄w蝅c焭q蝆y焲y蝋l蝍j蝏t焷p蝐m蝑x蝒m蝔j蝘y蝚r蝛w蝜f蝝y蝞m蝟w蝡r蝢x虾h蝧y蝨s蝩c蝪t蝫z蝬z蝭d蝯y蝱m蝲l蝳d蝵q蝷l蜗w蝹y蝺q蝿y螀j螁b蛳s螆c螇x螉w螊l螌b螎r螏j螑x螒h螔y螕b螖h螘y螙d螛h螜h螝g螠y螡w萤y螣t螤z巍w微w危w韦w违w桅w唯w惟w萎w委w尾w未w蔚w味w畏w胃w喂w魏w位w渭w谓w尉w慰w卫w瘟w蚊w文w燰w吻w紊w嗡w翁w爁l我w斡w握w沃w巫w钨w诬w屋w爗y爙r梧w吾w武w爠q五w捂w午w爥z舞w伍w侮w戊w雾w晤w物w勿w悟w误w昔x熙x析x西x硒x矽x晰x嘻x吸x牔b锡x螥c螦s螧q螩d螪s螮d螰l螱w螲d螴c螶q螷p螸y螹c蝼l螼q螾y螿j蟁w蟂x蟃w蛰z蟅z蝈g蟉l蟌c蟍l蟎m蟏x蟐c蟔m蟕z蟖s蟗q蟘t蟙z蟚p蟜j蟝q蟞b蟟l蟡g蟢x虮j蟤z蟦f蟧l蟨j蟩j蟫y狔n狚d蛲n猍l蟰x猏j蟱w蟳x蟴s蟵c蛏c蟷d蟸l蟺s蚁y蟼j蟽d蟿q蠀c蠁x蠂s蠄q蝇y虿c蠇l蠈z蠉x蠋z猽m蠌z蠍x蠎m蠏x蛴q蝾r蠒j蚝h獉z蠗z蠘j蠙b蠚h蠜f蠝l蠞j蠠m蛎l稀x息x希x悉x膝x夕x惜x熄x烯x溪x汐x犀x檄x袭x席x媳x喜x铣x洗x獹l隙x瞎x匣x霞x辖x暇x下x夏x掀x锨x先x仙x鲜x咸x贤x舷x闲x涎x嫌x显x险x腺x馅x陷x限x相x香x箱x襄x湘x乡x翔x祥x详x想x响x享x项x巷h橡x像x象x硝x霄x削x哮x销x消x宵x淆x蠤q蠥n蠦l蠧d蠨x蠩z蠪l蠫l蠬l蠭f蠮y蠯p蠰n蠳y蠴s蠵x瑊j瑘y蠸q瑦w瑸b璊m蠽j蠾z蠿z璠f衃p衆z衉k衋x衎k衏y衐q衑l衒x衕t衘x衦g衧y衪y甇y衱j衳z衴d衵r衶z衸j衺x衻r衼z袀j袃c袆h袇r畄l袉t袊l袌p袎y袏z袐b袑s袓j袔h袕x袗z袘y袙p畞m袚f畧l袝f袟z袠z袡r袣y袥t袦n袧g袨x袩z袪q小x孝x校j肖x疜x笑x效x楔x些x歇x疪b蝎x鞋x邪x斜x谐x械x卸x蟹x懈x痟x谢x屑x薪x痭b芯x锌x欣x辛x新x忻x心x信x衅x星x腥x猩x惺x刑x型x形x邢x行h醒x杏x性x姓x兄x胸x瘹d匈x雄x癁f熊x休x羞x朽x嗅x癊y锈x秀x袖x墟x戌q需x须x徐x许x蓄x酗x癦m旭x序x畜c絮n婿x轩x喧x宣x旋x玄x袬y袮m袯b袲c袳c袵r袶p袸j袹b袺j袻e袽r袿g裀y裃k裄h裇x裈k裋s裌j裍k裐j裑s裓g裖z裗l裚j裛y装z裞s裠q裦b裧c裩k裪t裫y裬l裭c裮c裯c裲l裵p裶f裷y裺y裻d制z裿y褀q褁g褃k褄q褅t褆t褈c褉x褋d褌k褍d褎x褏x褑y褔f褕y褖t褗y褘h褜p褝d褞y褟t褠g褢h褣r褤y褦n褧j褨s褩b褬s褭n褮y褯j褱h裢l褵l褷s选x眩x靴x薛x穴x雪x血x熏x循x旬x询x驯x巡x殉x汛x训x讯x逊x迅x押y鸦y鸭y丫y芽y牙y蚜y崖y衙y涯y雅y盶y讶y焉y阉y淹y盐y研y蜒y延y言y颜y阎y炎y沿y奄y掩y眼y衍y堰y燕y雁y唁y宴y谚y验y殃y央y鸯y秧y佯y羊y洋y氧y仰y养y漾y邀y腰y妖y褛l褹y褺d亵x褼x褽w褾b褿c襀j襃b襅q襆f襇j襈z襉j襊c襋j襌d襍z襎f襏b襑x襒b襓r襔m襕l襗z襘g襙c襚s襛n襜c裣l裆d襡s襢z襣b褴l襥f襧z襨t襩s襫s襭x襮b襰l襱l襳x襴l襵z襶d襷t襸z襹s襺j襼y襽l襾y覀y覂f覄f覅f覇b覉j瞈w覊j瞏q覌g覍b覎y瞔z覐j覑p覒m覔m覕p覗s觇c覙z覚j覛m覜t覝l覞y覟z覠j觋x遥y谣y姚y咬y舀y要y矊m耀y椰y噎y耶y野y冶y也y页y掖y腋y夜y液y一y壹y医y揖y铱y依y伊y衣y颐y夷y遗w移y胰y疑y沂y宜y姨y彝y椅y倚y已y乙y矣y以y抑y易y邑y屹g役y臆y逸y肄y疫y亦y裔y意y毅y益y溢y诣y议y谊y译y翼y翌y茵y因y音y阴y姻y吟y银y淫y寅y饮y尹y引y隐y覢s覣w覤x覥t觎y覧l矲b覨e覩d觊j覭m覮y觏g覰q覱z觐j観g覴d覵j覶l觑q覸j硡h硢y覻q覼l覾s觌d觃y觍t觕c觗z觘c觙j觛d觝d觟h觠q觡g觢s觤g觧j觨h觩q觪x觬n觭j觮j觰z觱b觲x觞s觵g觯z觷x觹x觺y觻l觼j觽x觾y觿x訁y訄q訅q訆j訉f訋d讧h訍c訏x讦j訑y訒r訔y讪s訙x訚y印y英y鹰y迎y赢y盈y影y硬y映y臃y庸y雍y踊y蛹y咏y碐l泳y永y恿y勇y用y幽y悠y尤y由y邮y铀y油y游y酉y碻q有y碽g友y磂l右y釉y诱y又y幼y迂y淤y于y盂y榆y虞y愚y舆y余y俞y逾y鱼y愉y渝y磭c隅y予y磰s雨y禹y宇y语y羽y玉y域y礃z芋y郁y遇y喻y峪y愈y育y誉y訞y訠s訡y讷n訦c訧y訨z訩x訫x訬c訮y訯s訰z訲y訳y訵c诃h訷s訸h訹x诊z注z证z訽g訿z詀z诂g詂f詃j詄d詅l诋d詇y詊p詋z詌g詍y讵j詏y诈z詑t诒y詓q诏z詖b礶g祍r祘s诎q詙b詚d诅z詜t詝z詟z诩x祱s詤h詥h祶d詧c詨x詪h诟g禈h诠q詯h诘j祯z詴w诜s詶c詷t詸m詻l诙h詽y詾x诖g誀e浴y寓y裕y预y豫y驭y鸳y冤y元y垣y袁y原y援y辕y猿y源y远y苑y愿y怨y院y曰y越y跃y钥y月y阅y耘y云y郧y陨y允y运y酝y韵y孕y匝z砸z杂z栽z哉z宰z载z再z在z赞z赃z葬z遭z糟z凿z藻z早z澡z蚤z躁z噪z造z皂z灶z燥z责z贼z怎z增z憎z曾c赠z喳c秢l渣z札z轧g誁b誂t誃y诔l诛z诓k誈w誋j志z誏l誐e诳k诶a誔t誖b誗c誙k诮q誛q誜s誝a誟x誢x诰g誧b誩j誫z説s読d誮h誯c誱j誳q誴c誵x谇s誷w誸x誺c誻t誾y諀p諁z谄c諃c谆z諅j諆j諈z诿w諊j諌d诤z諎z诹z諐q诼z諓j諔c諕x谂s諘b諙h諚b谀y諝x稬n谝p諟s諠x諡s诨h諣h稸x穉z铡z闸z眨z榨z咋z穙p乍z炸z摘z斋z穠n宅z窄z寨z瞻z詹z粘n沾z辗z展z蘸z站z湛z樟z章z彰z漳z掌z杖z丈z账z仗z瘴z障z招z昭z找z沼z赵z照z罩z兆z肇z召s遮z折s哲z辙z者z锗z蔗z这z浙z珍z斟z真z甄z砧z臻z贞z针z枕z疹z震z振z镇z阵z蒸z征z怔z整z拯z正z政z谔e諥z谛d諨f諩p諪t谏j諬q谕y谘z諯z諰x諲y谙a諴x諵n谌c诸z諹y諻h谖x窧z窽k谒y竌c竨d竪s诌z謆s謈b謉k謋h謌g謍y謏x竸j谧m謑x謒q謓c谑x謕t谡s謘c谥s謜y謞h謡y謢l謣y謤b謥c謧l谟m謩m謪s谪z謭j謮z謯j謰l謱l讴o謴g謵x謶z謷a謸a謺z謻y謼h謽j謿c譀h譂c譃x譄z譅s郑z芝z枝z支z吱z蜘z知z肢z脂z汁z之z直z植z殖s值z址z指z止z趾z旨z至z置z峙s智z秩z稚z质z炙z痔z治z窒z中z盅z筳t筴c忠z钟z衷z重c仲z舟z箈t周z州z洲z粥y轴z肘z帚z咒z宙z骤z珠z株z蛛z猪z逐z竹z煮z拄z主z著z柱z助z蛀z贮z铸z譆x譇z譈d譊n譋l譌e谲j譐z譑j譒b譓h譔z譕w谮z譗z谯q譛j譝s譞x譠t譡d譢s譣x譤j譥j譧z譨n譩y谵z譭h譮h譱s譲r譳r篒y譶t譸z譹h譺a譻y譼j譾j譿h讁z讂j讃z讄l讅s讆w讇c讉y讋z篻p讌y讍e雠c讏w讐c讑y讔y谶c讗x讘n讙h讛y谠d讝z谳y讟d讬t讱r讻x诇x诐b诪z谉s谞x住z祝z驻z抓z爪z拽z转z撰z赚z篆z撞z椎c锥z追z赘z捉z拙z卓z桌z琢z茁z酌z啄z着z灼z咨z资z姿z滋z淄z孜z紫z仔z籽z滓z自z字z鬃z棕z踪z宗z邹z走z奏z揍z租z足z卒c族z祖z阻z钻z纂z嘴z醉z最z罪z尊z遵z昨z左z佐z柞z做z作z坐z座z谸q谹h谺x谻j谼h谽h谾h谿x豀x豂l豃h豄d豅l豋d豍b豏x豑z豒z豓y籋n豖c豗h豘t豙y豛y豜j豝b豞h豟e豠c豣j豤k豥g豦j豧f豨x豩b豭j豮f豯x豰h豱w豲h豴d豵z豷y豻a豼p豽n豾p豿g貀n貁y貃m貄s貆h貇k貈h貋a貎n貏b貐y貑j貒t貕x貖y貗j貙c貚t貛h貜j粎m粓g貟y粡t貣d貤y貦w貭z亍c丌q兀w丐g廿n卅s丕p丞c鬲g孬n噩e丨g禺o丿p匕b乇z夭a爻y卮z氐d囟x胤y馗k毓y鼗t丶z亟j鼐n乜m亓q孛b嘏g厝c厥j靥y赝y匚f叵p糂s匾b赜z卦g卣y刂d刈y刎w刳k剌l糛t剞j剡s剜w蒯k剽p劂j劁q劐h劓y冂j罔w亻r仃d仉z仨s糿g仫m仞r仳p伢y佤w仵w伉k紤j佞n佧k攸y佚y佝g貮e贳s貱b赀z貵p貹s贶k絾c綛k綥q緛r繏x繑q繝j纄p纋y纕x纚l缿x翄c翭h翺a耛s聎t肒h肸x胋t胑z胻h脦t脺c脻j腟c臒w臤q臱m舋x舿k艕b芀t芁q芖z苚y茟y茰y莍q菻l萂h萿k葔h蔝m蔨j蔴m蕮x薆a莶x藱h藼x蘀t蘃r蘒q蘓s蘹h虂l虆l虨b虶y虷h蚅e蚔q蚭n蚿x蛯e蝖x螐w螚n蟭j蠷q蠺c衂n衭f袛d袾z襂s襐x覫p覹w觓q訤x詉n詗x詺m誎c誽n諽g諿x謃x謟t謲c譍y譪a譵d貥h乩j仡g斛h颢h銮l乊h乥h夫f兓j兙s兛q兝g兞h兣g兺b囻g坟f妢f婲h孒j尅k屗u岎f帉f幥z幩f弅f徚u怭b怾g慙c昐f晲n曢u朆f朌f朑u朩p枌f梤f乐l橨f檂n欂b毜c毮s洂y涜d颍y澵z炃f炍p炾h烪u焑y焺s燌f燓f燞u燢x獖f瓧s瓰f瓱m瓲t瓼l榛z彳c夔k璞p丟d並b丬p乂y乄s亙g亞y亠t亳b仝t伋j伕f伲n伽g佇z佈b佔z佗t佘s佟t佪h佴e併b佶j佻t佼j佾y侃k來l侉k侏z侑y侔m侖l侶l侷j俅q俎z俑y俚l俜p俟q俠x俳p俸f俾b倆l倉c個g倌g倏s們m倖x倜t倣f倥k倨j倩q倫l倬z倭w倮l偃y偈j偉w偌r偎w偕x側c偵z偺z偽w傖c傘s備b傢j傭y傳c傴y債z傷s傺c傾q僂l僅j僇l僉q僑q僕p僖x僥j僦j僬j僭j僮t僱g價j儀y儂n億y儅d儆j儇x儉j儋d儐b儔c儕c儘j優y儲c儷l儸l儺n儻t儼y兌d兒e兕s兗y內n兩l兮x冊c冑z冖m冢z冥m冪m冫b冱h冼x冽l凇s凈j凍d凫f凱k凵k凼d刖y別b刪s剄j則z剋k剎c剛g剝b剮g剴k創c剷c劃h劇j劉l劊g劌g劍j劑j劬q劭s劻k劾h勁j勐m動d勗x務w勛x勝s勞l勢s勣j勦j勰x勱m勵l勹b勻y匍p匏p匐f匭g匯h匱k區q協x卟b卩j卹x卻q厙s厭y厲l厴y厶s參c叟s叢c叨d叩k叱c叻l吆y吋c吒z吖y吡b吣q吲y吳w吶n呂l呃e呋f呎c呔d呤l呦y呱g呲c呶n呷g咂z咄d咔k咚d咝s咣g咤c咦y咧l咩m咪m咫z咭j咻x咼w咿y哂s哌p哏g哐k哚d哞m員y哧c哳z哽g哿g唄b唅h唏x唑z唔m唣z唧j唪f唰s唳l唷y唸n唼s唿h啁z啉l問w啐c啕t啖d啞y啟q啣x啵b啶d啷l啻c啾j喁y喃n喈j喋z喏n喑y喔o喙h喚h喟k喪s喫c喬q單d喱l喵m嗄a嗆q嗇s嗉s嗌y嗍s嗎m嗑k嗒d嗔c嗖s嗚w嗝g嗟j嗤c嗥h嗦s嗨h嗪q嗬h嗲d嗵t嗶b嗷a嗾s嘀d嘁q嘈c嘌p嘍l嘔o嘖z嘜m嘞l嘟d嘣b嘧m嘩h嘬c嘭p嘮l嘯x嘰j嘵x嘸f嘹l噁e噌c噍j噓x噔d噗p噘j噙q噠d噢o噤j噥n噦y噫a噯a噱j噲k噴p噸d噹d噻s噼p嚀n嚅r嚆h嚇h嚌j嚐c嚓c嚙n嚥y嚦l嚨l嚮x嚯h嚳k嚴y囀z囁n囂x囅c囈y囉l囌s囑z囔n囗w囝j囡n囪c囫h囹l囿y圄y圇l圉y圊q國g圍w園y圓y圖t圜h圩w圪g圮p圯y圳z圻q坂b坌b坨t坩g坫d坭n坰j坳a坶m坻c坼c垅l垌d垓g垠y垡f垤d垴n埏s埒l埕c埝n埡y埤p埭d埯a埴z執z埸y埽s堀k堅j堇j堊e堋b堍t堙y堞d堠h報b場c堿j塄l塊k塋y塍c塒s塗t塢w塤x塥g塬y塵c塹q塾s墀c墁m墉y墊d墑d墚l墜z墮d墻q壅y壇t壑h壓y壙k壚l壞h壟l壢l壩b壯z壺h壽s夂z夙s夠g夢m夤y夥h夼k夾j奐h奕y奘z奚x奧a奪d妁s妃f妍y妗j妝z妞n妣b妤y妯z妲d妳n妾q姊z姍s姒s姘p姝s姣j姦j姪z姹c娉p娌l娑s娓w娛y娣d娼c婀e婊b婕j婢b婦f婧j婭y婷t婺w媛y媧w媯g媲p媸c媼a媽m媾g嫖p嫗y嫘l嫜z嫠l嫣y嫦c嫫m嫵w嫻x嬈r嬉x嬌j嬖b嬗s嬙q嬝n嬡a嬤m嬰y嬲n嬴y嬸s孀s孃n孑j孓j孚f孢b孥n孫s孱c孳z學x孿l宀m宄g宓m宕d宥y宮g宸c寢q寤w實s寧n審s寫x寬k寮l寰h寵c寶b將j專z尋x對d尕g尜g尢w尥l尬g尷g尻k屄b屆j屐j屙e屜t屣x層c屬s屮c屺q岈y岌j岍q岐q岑c岙a岜b岡g岢k岣g岧t岫x岬j岱d岵h岷m峁m峋x峒d峴x島d峽x崆k崍l崗g崙l崛j崞g崠d崢z崤x崦y崧s崮g崴w崽z崾y嵇j嵊s嵋m嵒y嵩s嵫z嵬w嵯c嵴j嶁l嶂z嶄z嶇q嶗l嶙l嶷n嶸r嶺l嶼y嶽y巋k巑c巔d巖y巛c巰q巹j巽x帑t帔p帙z帥s帳z帶d帷w幃w幄w幔m幘z幛z幞f幟z幡f幣b幪m幫b幬c幹g幺y幾j庀p庂z庋g庖p庠x庥x庫k庳b庹t庾y廁c廂x廄j廈s廑q廒a廚c廛c廝s廡w廢f廣g廨x廩l廬l廱y廳t廴y廾g弁b弈y弋y弒s弔d弩n弭m弳j張z強q弼b彀g彆b彈d彊q彌m彎w彐x彖t彗h彘z彙h彡s彥y彫d彷f彿f徂c徇x徉y後h徑j徙x徜c從c徠l徨h復f徫w徬p徭y徵z徹c徼j忉d忐t忑t忒t忖c忝t忡c忤w忪s忭b忮z忸n怊c怍z怏y怙h怛d怡y怦p怩n怫f怵c恁n恂x恆h恙y恚h恝j恣z恥c恧n恪k悃k悅y悌t悒y悖b悚s悛q悝k悱f悴c悵c悶m悻x悽q惆c惘w惙c惝t惡e惱n惲y惴z惻c愀q愆q愍m愎b愕e愛a愜q愣l愨q愫s愴c愾k慄l慇y慊q態t慍y慘c慚c慝t慟t慣g慫s慮l慵y慶q慼q慾y憂y憊b憐l憑p憔q憚d憝d憧c憩q憫m憬j憲x憶y憷c懇k應y懋m懌y懍l懔l懞m懟d懣m懨y懲c懵m懶l懷h懺c懼j懾s懿y戀l戇g戉y戔j戕q戛j戟j戡k戢j戤g戥d戧q戩j戰z戲x戶h戽h戾l扃j扈h扌s扐c扠c扢g扺z抃b抆w抎y抴y抻c拊f拋p拗a拮j拶z挈q挲s挶j挹y挾x捃j捋l捨s捩l捫m捭b捱a捺n掃s掄l掊p掎j掙z掛g採c掬j掭t掮q掰b掾y揀j揄y揆k揎x揚y換h揞a揠y揲d揶y揸z揹b搆g搋c搌z損s搖y搗d搛j搟x搠s搡s搦n搴q搶q搿g摁e摑g摒b摜g摞l摟l摭z摯z摳k摶t摺z摻c撈l撓n撙z撚n撟j撣d撥b撫f撲p撻t撾z撿j擁y擄l擇z擊j擐h擔d擗p擘b據j擠j擢z擣d擤x擬n擯b擰n擱g擲z擴k擷x擺b擻s擼l擾r攄s攆n攉h攏l攔l攖y攙c攛c攜x攝s攢z攣l攤t攥z攬l攮n攴p攷k敉m敕c敗b敘x敫j敵d斂l斃b斐f斕l斫z斬z斷d於w旂q旃z旄m旆p旌j旎n旒l旖y旮g旯l旰g昀y昃z昇s昊h昕x昝z昱y昴m昶c晁c時s晉j晏y晗h晝z晞x晟c晡b晷g暄x暈y暉h暌k暘y暝m暨j暫z暱n暸l暹x暾t曄y曆l曇t曉x曖a曛x曜y曠k曩n曬s曷h書s會h朊r朐q朕z朦m朧l朮s杇w杈c杌w杓b杞q杪m東d杲g杳y杵c杷b杼z枇p枋f枘r枙e枰p枳z枵x枸g柁d柃l柘z柚y柝t柢d柩j柰n柵s栝g栩x栲k栳l桀j桁h桄g桉a桊j桎z桕j桫s桴f桷j桿g梃t梏g梓z梔z條t梟x梩q梵f棄q棖c棗z棟d棣d棧z棰c棲q棹z棼f椋l椏y椐j椴d椹s楂c楊y楓f楗j楝l楠n楣m楦x楨z楫j業y楮c楱c極j楸q楹y榀p榍x榕r榘j榙t榧f榪m榫s榭x榮r榱c榻t榿q槃p槊s槌c槍q槎c槓g槧q槨g槭q槲h槳j槿j樁z樂l樅c樑l樓l樗c樘t標b樞s樨x樵q樸p樹s樺h樽z樾y橄g橈r橋q橐t橘j橛j機j橢t橫h檁l檉c檎q檔d檗b檜g檠q檢j檫c檯t檳b檸n檻j櫓l櫚l櫛z櫝d櫟l櫥c櫧z櫨l櫪l櫬c櫳l櫸j櫺l櫻y欄l權q欏l欒l欖l欞l欷x欹q歃s歆x歎t歐o歙s歟y歡h歲s歷l歿m殂c殄t殍p殘c殞y殤s殪y殫d殮l殯b殲j殳s殺s殼k毀h毂g毆o毌g毪m毬q毳c毹s毽j毿s氅c氆p氈z氌l氍q氕p氘d氙x氚c氡d氣q氤y氪k氫q氬y氳y氵s氽c氾f汆c汊c汍w汎f汔q汜s汨m汩g汴b汶w決j沅y沆h沌d沐m沒m沓d沔m沖c沭s沱t沲t泐l泓h泔g泖m泗s泠l泫x泮p泯m泱y洄h洇y洌l洎j洙z洚j洧w洩x洫x洮t洳r洵x洶x洹h浜b浞z浠x浣h浬h浯w浹j浼m涇j涑s涓j涔c涫g涮s涼l涿z淅x淇q淒q淙c淚l淝f淞s淠p淥l淦g淨j淪l淵y淶l淼m渙h渚z減j渥w渦w渫x測c渲x渾h湄m湊c湎m湓p湔j湞z湟h湧y湫j湮y湯t溆x溏t準z溘k溝g溟m溥p溧l溫w溱q溲s溴x溷h溻t溼s溽r滂p滄c滅m滎x滏f滕t滟y滬h滯z滲s滷l滸h滹h滾g滿m漉l漕c漚o漢h漣l漤l漩x漪y漬z漭m漯l漲z漶h漸j漿j潁y潑p潔j潛q潟x潢h潤r潯x潰k潲s潴z潷b潸s潺c潼t澀s澆j澇l澉g澌s澍s澗j澠m澤d澧l澩x澮k澱d澶c澹d濂l濃n濉s濕s濘n濛m濞b濟j濠h濡r濤t濫l濮p濯z濰w濱b濺j濼l瀅y瀆d瀉x瀋s瀏l瀕b瀘l瀚h瀛y瀝l瀟x瀠y瀣x瀧l瀨l瀰m瀲l瀵f瀹y灃f灄s灑s灕l灘t灝h灞b灣w灤l灬h災z炅g炫x炱t為w炻s烀h烊y烏w焐w焓h無w焯c焱y煅d煉l煊x煒w煙y煜y煢q煦x煨w煩f煬y煲b煳h煸b煺t熒y熗q熘l熠y熨y熱r熳m熵s熹x熾c燁y燄y燉d燐l燒s燔f燙t營y燠y燦c燧s燬h燭z燮x燴h燹x燼j燾d爐l爛l爝j爨c爭z爰y爺y爾e爿p牆q牒d牖y牘d牝p牠t牦m牪y牮j牯g牴d牽q牿g犄j犍j犏p犒k犛l犟j犧x犭q犰q犴a犸m狀z狁y狃n狍p狎x狒f狨r狩s狳y狴b狷j狹x狺y狻s狽b猁l猊n猓g猗y猙z猝c猞s猢h猥w猬w猱n猶y猷y猸m猹c猻s獄y獅s獍j獐z獒a獗j獠l獨d獪k獫x獬x獯x獰n獲h獵l獷g獸s獺t獻x獼m獾h玀l玆z玎d玟m玢b玨j玳d玷d珀p珂k珈j珉m珙g珞l珥e珧y珩h珮p現x琊y琚j琛c琥h琦q琨k琪q琬w琮c琰y琺f琿h瑁m瑕x瑗y瑙n瑛y瑜y瑣s瑤y瑩y瑪m瑭t瑯l瑾j璀c璇x璉l璋z璐l璜h璣j璦a璧b璨c璩q璺w璽x瓊q瓏l瓖x瓚z瓞d瓠h瓴l瓿b甍m甏b甑z甓p甙d產c甦s甬y町d甾z畀b畈f畋t畎q畚b畛z畝m畫h畬s異y畹w畼c畿j疃t疊d疋y疒n疔d疝s疣y疬l疰z疳g疴k疸d疿f痂j痃x痄z痍y痖y痙j痣z痤c痦w痧s痲m痳m痼g痿w瘀y瘃z瘊h瘌l瘍y瘐y瘓h瘕j瘙s瘛c瘞y瘠j瘡c瘢b瘥c瘭b瘰l瘳c瘺l瘼m癀h療l癃l癆l癇x癍b癔y癖p癜d癟b癡c癢y癥z癩l癬x癭y癮y癯q癰y癱t癲d發f皈b皎j皓h皙x皚a皤f皰p皴c皸j皺z皻z盍h盜d盞z盡j監j盤p盥g盧l盪d盱x盹d眄m眇m眈d眍k眚s眢y眣d眥z眭s眵c眸m睇d睏k睚y睜z睞l睢s睥b睨n睪y睽k睿r瞀m瞌k瞍s瞑m瞞m瞟p瞠c瞢m瞭l瞰k瞵l瞼j瞽g瞿j矇m矍j矓l矚z矜j矧s矬c矯j矸g砉h砑y砘d砜f砝f砟z砣t砥d砩f砬l砭b砲p砹a砼t硃z硇n硌g硎x硐d硤x硨c硪w硭m硯y碇d碓d碚b碣j碥b碩s碲d碹x碼m磉s磔z磙g磚z磣c磧q磬q磯j磲q磴d礅d礎c礓j礙a礞m礤c礦k礪l礫l礱l礴b礻s祀s祅y祇q祉z祐y祓f祕m祗q祚z祛q祜h祠c祧t祺q祿l禊x禍h禎z禚z禦y禧x禮l禰m禱d禳r禿t秈x秕b秣m秫s秭z稂l稃f稅s稆l稈g稔r稜l稞k稟b種z稱c稷j稹z穀g穌s積j穢h穩w穫h穰r穸x穹q窀z窆b窈y窕t窠k窨x窩w窪w窬y窮q窯y窳y窶j窺k竄c竅q竊q竦s競j竺d竽y笄j笈j笊z笏h笙s笞c笠l笤t笥s笪d笫z笮z笱g笳j笸p笻q筆b筌q筍s筘k筠j筢p筮s筲s筵y筻g箄b箇g箋j箏z箐q箜k箝q箬r箴z箸z節j篁h範f築z篋q篌h篚f篝g篠x篤d篥l篦b篩s篪c篳b篼d篾m簀z簋g簌s簍l簏l簑s簟d簡j簣k簦d簪z簫x簷y簸b簽q簾l籀z籃l籌c籐t籜t籟l籠l籤q籥y籩b籪d籬l籮l籲y粑b粞x粢z粧z粲c粵y粼l粽z糅r糇h糈x糌z糍c糗q糝s糢m糧l糨j糰t糲l糴d糶t糸m糾j紀j紂z約y紅h紇h紈w紉r紋w納n紐n紓s紕p紗s紙z級j紜y紡f紮z細x紱f紲x紳s紹s紺g紼f紿d絀c終z絃x組z絆b絎h結j絛t絞j絡l絢x給g絨r統t絲s絳j絹j綁b綃x綆g綈t綏s綑k經j綜z綞d綠l綢c綣q綦q綬s維w綮q綰w綱g網w綴z綵c綸l綹l綺q綻z綾l綿m緇z緊j緋f緒x緗x緘j緙k線x緝j緞d締d緡m緣y緦s編b緩h緬m緯w緱g緲m練l緶b緹t緻z縈y縉j縊y縐z縑j縚t縛f縝z縞g縟r縣x縫f縭l縮s縯y縱z縳z縴q縵m縶z縷l縹p縻m總z績j繅s繆m繇y繈q繒z織z繕s繚l繞r繡x繢h繩s繪h繫x繭j繯h繰q繳j繹y繼j繾q纈x纊k續x纍l纏c纓y纔c纖x纘z纛d纜l纟s绔k绛j绱s缰j缶f缽b罄q罅x罈t罌y罘f罟g罡g罨y罰f罱l罵m罹l罾z羅l羆p羈j羋m羝d羧s義y羯j羰t羲x羸l羼c羿y翊y翎l習x翕x翡f翥z翦j翩p翮h翳y翹q耄m耆q耋d耑d耒l耔z耖c耜s耠h耢l耥t耦o耨n耩j耬l耱m耵d耷d聃d聆l聒g聞w聯l聰c聱a聲s聳s聵k聶n職z聹n聽t聾l聿y肅s肓h肜c肟w肫z肭n肱g肴y肷q肼j肽t胂s胍g胗z胛j胝z胥x胨d胩k胬n胭y胱g胲h胴d胼p脅x脈m脎s脒m脘w脛j脞c脣c脧j脩x脫t脬p脲n脹z腈j腌a腎s腓f腙z腚d腠c腡l腦n腧s腩n腫z腭e腱j腴y腸c腼m膂l膃w膈g膚f膠j膦l膪c膺y膻s膽d膾k膿n臁l臉l臊s臌g臍q臏b臚l臟z臠l臧z臨l臬n臾y舁y舂c舄x與y興x舉j舊j舐s舖p舛c舡c舢s舨b舫f舭b舯z舳z舴z舸g舺x舾x艄s艉w艋m艏s艚c艟c艣l艦j艨m艫l艮g艱j艴f艷y艸c艽j艿n芄w芊q芎x芏d芑q芘b芙f芟s芡q芤k芨j芩q芪q芫y芮r芰j芴w芷z芻c芾f苊e苒r苓l苕s苘q苜m苠m苡y苣j苤p苧n苴j苷g苻f茆m茇b茈c茉m茌c茍j茗m茚y茛g茜q茭j茯f茱z茲z茳j茴h茺c茼t荀x荃q荇x荊j荏r荑t荬m荸b荻d荼a荽s莊z莒j莓m莘s莛t莞g莠y莢j莧x莨l莩f莪e莰k莺y莼c菀w菁j菅j菔f菖c菘s菝b菟t菡h菥x華h菰g菸y菹z菽s萁q萃c萆b萊l萋q萏d萑h萘n萜t萬w萱x萵w萸y萼e葆b葉y葑f葙x葚r葜q葦w葩p葭j葯y葳w葶t葷h葸x葺q蒈k蒎p蒔s蒗l蒞l蒡b蒴s蒺j蒼c蒽e蒿h蓁z蓊w蓋g蓍s蓐r蓓b蓥y蓦m蓯c蓰x蓼l蓽b蔌s蔔b蔟c蔣j蔥c蔦n蔭y蔸d蔻k蕁q蕃f蕈x蕎q蕓y蕕y蕖q蕙h蕞z蕢k蕤r蕨j蕩d蕪w蕭x蕷y蕹w蕺j蕻h薅h薇w薈h薊j薌x薏y薑j薔q薜b薟x薤x薦j薧h薨h薩s薰x薷r薹t薺j藁g藍l藎j藜l藝y藥y藪s藶l藷s藹a藺l藿h蘄q蘅h蘆l蘇s蘊y蘋p蘗b蘚x蘢l蘧q蘩f蘭l蘺l蘼m蘿l虍h虔q處c虛x虜l號h虢g虧k虬q虺h虻m蚋r蚍p蚓y蚣g蚧j蚨f蚩c蚪d蚯q蚰y蚱z蚴y蚵k蚶h蚺r蛄g蛅z蛉l蛐q蛑m蛘y蛞k蛟j蛩q蛭z蛸s蛺j蛻t蜃s蜆x蜇z蜈w蜉f蜊l蜍c蜓t蜚f蜞q蜢m蜣q蜥x蜩t蜮y蜱p蜷q蜻q蜾g蜿w蝌k蝓y蝕s蝙b蝠f蝣y蝤q蝥m蝦x蝮f蝰k蝸w蝻n蝽c螂l螃p螅x螋s螓q螗t螞m螢y螨m螫s螬c螭c螯a螳t螵p螻l螽z蟀s蟄z蟆m蟈g蟊m蟋x蟑z蟒m蟓x蟠p蟥h蟪h蟬c蟮s蟯n蟲c蟶c蟻y蟾c蠃l蠅y蠆c蠊l蠐q蠑r蠓m蠔h蠖h蠛m蠟l蠡l蠣l蠱g蠲j蠹d蠼q衄n術s衛w衝c衢q衤y衩c衲n衹z衽r衾q衿j袂m袈j袞g袢p袤m袷q裊n裎c裏l裒p裘q補b裝z裟s裡l裥j裨b裰d裱b裼t製z裾j複f褊b褓b褙b褚c褡d褫c褰q褲k褳l褶z褻x襝l襞b襠d襤l襦r襪w襬b襯c襻p覃q見j規g覓m視s覘c覡x覦y親q覬j覯g覲j覷q覺j覽l覿d觖j觚g觜z觥g觫s觳h觴s觶z觸c訂d訃f訇h訊x訌h討t訐j訓x訕s訖q託t記j訛e訝y訢x訣j訪f設s許x訴s診z註z証z訾z詁g詆d詈l詎j詐z詒y詔z評p詘q詛z詞c詠y詡x詢x詣y詩s詫c詭g詮q話h該g詵s詼h誅z誆k誇k誌z認r誑k誕d誘y誚q語y誠c誡j誣w誤w誥g誨h誰s課k誶s誼y調d諂c諄z談t諉w請q諍z諏z諑z諒l論l諗s諛y諜d諞p諢h諤e諧x諫j諮z諱h諳a諶c諷f諸z諺y諾n謀m謁y謂w謅z謇j謊h謎m謐m謔x謗b謙q謚s講j謝x謠y謦q謨m謫z謬m謳o謹j譁h證z譎j譖z識s譙q譚t譜p譟z譫z譯y議y譴q護h譽y讀d讎c讒c讓r讕l讖c讚z谫j豇j豈q豉c豎s豐f豔y豕s豚t豬z豳b豸z貂d貅x貊m貍l貓m貔p貘m貝b負f財c貢g貧p貨h販f貪t貫g責z貯z貰s貲z貳e貴g貶b貸d貺k貼t貾c貿m賁b賂l賃l賄h賅g賆p資z賈j賉x賊z賋j賌g賎j賏y賐j賑z賒s賓b賔b賕q賖s賗c賘z賙z賛z賜c賝c賞s賟t賠p賡g賢x賣m賤j賥s賧d賨c賩c質z賬z賭d賮j賯x賰c賳z賴l賵f賶c賷j賸s賹y賺z賻f購g賽s賾z賿l贀y贂c贃w贄z贅z贆b贇y贈z贉d贊z贋y贌p贍s贎w贏y贐j贑g贒x贓z贔b贕d贗y贘s贙x贚l贛g贜z贠y贲b贻y贽z赅g赆j赇q赈z赉l赍j赑b赒z赓g赕d赗f赙f赟y赥x赧n赨t赩x赪c赬c赭z赮x赯t赱z赲l赳j赸s赹q赺y赻x赼z赽j赿c趂c趃d趄j趆d趇x趈z趉j趌j趍q趎c趏g趐x趑z趔l趖s趗c趘x趚s趜j趝j趞q趠c趡c趤d趥q趧t趨q趩c趪h趫q趭j趮z趯t趰e趱z趲z趵b趷k趹j趺f趻c趼j趽f趿t跁b跂q跄q跅t跆t跇y跈j跉l跍k跎t跏j跐c跒q跓z跔j跕d跖z跗f跘p跙j跚s跛b跜n跞l跠y跡j跢d跣x跤j跦z跧q跩z跫q跬k跭x跰p跱z跲j跴c跶d跷q跸b跹x跻j跼j跽j跾s跿t踀c踁j踂n踃x踄b踅x踆c踈s踉l踍q踎m踑j踒w踓w踔c踕j踗n踘j踙n踚l踛l踝h踟c踡j踣b踥q踦q踧c踫p踬z踭z踮d踯z踰y踱d踲d踳c踴y踵z踶d踷z踸c踹c踺j踻g踽j踾f踿c蹀d蹁p蹂r蹃n蹇j蹉c蹊q蹍n蹐j蹑n蹒p蹓l蹔z蹕b蹖c蹗l蹘l蹙c蹚t蹛d蹜s蹝x蹞k蹟j蹠z蹡q蹢d蹣p蹤z蹥l蹧z蹨n蹩b蹪t蹫j蹮x蹯f蹰c蹱z蹳b蹴c蹵c蹶g蹷j蹸l蹹t蹺q蹻q蹼p蹽l蹾d躀g躂d躃b躄b躅z躆j躈q躉d躊c躋j躍y躎n躏l躐l躑z躒l躓z躔c躕c躖d躗w躘l躙l躚x躛w躜z躝l躞x躟r躠s躡n躢t躣q躥c躦z躧x躨k躩j躭d躮s躰t躱d躵h躶l躷a躸j躹j躻w躽y躾x躿k軀q軁l軃d軄z軆t軇d軉y車c軋y軌g軎w軏y軐x軑d軒x軓f軔r軕s軖k軗s軘t軙c軚d軜n軝q軟r軠k軡q転z軣h軤h軥q軦k軧d軨l軩d軪a軫z軬f軮y軰b軱g軲g軴z軵r軶e軷b軹z軺y軻k軼y軽q軾s軿p輀e輁g輂j較j輄g輅l輆k輈z載z輋s輌l輍y輎s輏y輐w輓w輔f輕q輖z輗n輘l輙z輚z輛l輝h輞w輟c輡k輢y輣p輤q輥g輦n輨g輩b輫p輬l輭r輮r輰y輱x輲c輴c輵g輶y輷h輸s輹f輺z輻f輽f輾z輿y轀w轁t轂g轃z轄x轅y轆l轇j轈c轉z轊w轍z轎j轏z轐b轑l轔l轖s轗k轘h俁y係x轛d轝y轞j轟h轠l轡p轢l倀c轤l轥l轪d轫r轭e轱g轲k轳l轵z轶y轷h轸z轹l轺y轼s轾z辁q辂l辄z辇n辋w辌l辍c辏c辒w辔p辘l傑j辚l辝c辠z辡b辢l辥x辦b辪x辬b辮b辯b農n辳n辵c辶c辷y辸r辻s込y辿c迃y迆y迉q迊b迋w迌t迍z迏d儈k迕w迖d迗e迚d迤y迥j迨d迩e迬z迮z迯t迱y迲k迳j迴h迵d迺n迻y凜l迾l逄p逅h逇d逈j逋b逍x逎q逑q逓d逕j逖t這z逜w逡q連l逥h逦l逧g逨l逪c逬b逭h逯l週z進j逳y逴c逵k逶w逷t逹d逺y勸q遀s遄c遅c遈s遊y運y遌e過g遐x遑h遒q遖n遘g遙y遛l遜x遝t遞d遟c遠y遡s遢t遤h遦g遨a遪c遫c遯d遲c咴h遴l遶r遷q選x遹y遺y遻e遼l遽j遾s邁m邂x邃s還h邅z邆t邇e邈m邉b邊b邋l邌l邏l邒t邔q邕y邖s邘y邙m邛q邜x邝k邟k邠b邡f邥s邧y邬w邭j邰t邲b邳p邴b邶b邷w邸d邺y邼k邾z邿s喹k郀k郂g郃h郄q郅z郆j郇x郈h郉x郍n嗩s郏j郐k郒l郓y郔y郕c郖d郗x郘l郙f嘆t郚w郛f郜g郟j郠g嘗c郢y郤x郥b郦l郪q郫p郮z郯t郱p郳n郵y郶b郷x郹j郻q郾y郿m鄀r鄁b鄃s鄄j鄆y鄇h鄈k鄉x嚕l鄊x鄌t鄎x鄏r鄐c鄑z鄓y嚶y鄔w鄕x鄗h鄚m鄛c鄜f鄝l鄞y鄟z鄠h鄡q鄢y鄣z鄥q鄦x鄨b鄫z鄬w鄭z鄮m鄯s鄰l鄱p鄲d鄴y鄵c鄶k鄷f鄸m團t鄺k鄻l鄼z鄽c鄾y酀y酁c酂z酃l酄h酆f酇z酈l酊d酎z酏y酐g酑y酓y酔z酕m酖d酘d酙z酛k酜f酟t酠q酡t酢c酤g酦f垸y酧c酩m酫c酭y酯z酰x酲c酴t酹l酺p酻z堝g酾s醀w醁l醂l醃y醄t醅p醊z醌k醍t醎x醏d醐h醑x醕c醖y醗p醘k醙s醜c醝c醞y醟y醠a醡z醢h醣t醤j醥p醧y醨l醩z醪l醫y醬j醭b醮j醯x醰t醱p醲n醳y醴l醵j醶y醷y醸n醹r醺x醻c醼y醽l醾m醿m墼j釁x釃s釄m釅y壎x釆b壘l釐l釓g釖d奩l釘d釚q釛h針z釞z釟b釡f釣d釦k釧c釨z釩f釬h釭g釮q釰r釱d釲s釳x釴y釵c釶s釷t釸x釹n釺q釻q釼r釽p釾y鈀b鈁f鈂c鈅y鈆q嬋c鈉n鈊x鈋e鈌j鈍d嬪p孌l鈐q鈒s鈓r鈔c鈕n鈗y鈘y鈛g鈜h鈞j導d鈡z鈢x鈣g鈤r鈥h鈦t鈧k屝f鈫q鈬d鈭z鈮n鈯t鈰s鈲g鈳k鈴l屨j鈶s鈷g鈸b鈹p鈺y鈻s鈼z鈽b鉁z鉂s鉃s鉄t鉆z鉈t鉉x鉊z鉋b鉌h鉍b鉎s鉏c鉐s鉑b鉓c鉕p鉖t鉘f鉙z鉚m鉛q鉜f鉝l鉟p鉠y鉣j鉤g鉦z崳y鉨x鉩x鉪d鉫j鉭t鉮s嵐l鉯y鉰s鉱k鉶x鉷h鉸j鉹c鉺e鉻g鉼b嶝d鉿h嶧y銀y銂z巒l銄x銆m銇l銈j銉y銊x銋r銌z師s銎q銏s銒x銓q銔p銖z銙k銚d銛x銜x銝x銞j銟c銢p銥y銦y銧g銩d銪y銭q銯k銲h銳r銴s廟m銵k廠c銶q銷x銸z銺z銻t鋀t鋁l鋂m鋄w鋅x鋆y鋇b鋈w鋉s鋋c鋌t鋎h鋑j鋕z鋖s鋗x鋘h鋙w鋚t鋛k忄x鋞x鋟q鋠s鋡h鋢l鋣y鋤c鋥z鋦j鋧x鋨e鋩m鋪p鋬p鋭r鋮c鋯g鋰l鋳z鋴z鋵t惚h鋷z鋸j鋹c鋺y愷k鋼g鋽d鋿s錀l慪o錁k錂l錃p慳q憒k錅l憮w錇p錉m錊z錋p錌a錍p錎x錏y懸x錑l錒a錓k錕k錗n錘c錙z錚z錜n錞c錟t錠d錡q錢q錣z錤j錥y錦j錧g錨m錩c錪t捲j錬l錭d錮g錯c錱z録l錳m錴l錵h錷g錸l錹k錺k錻b揮h錼n錽w錾z錿h鍀d鍁x鍄l鍆m鍇k鍈y鍉c鍊l鍋g鍌x鍍d鍏w鍐z鍑f鍒r鍓j鍔e鍕j鍖c鍗t撐c鍘z鍙h鍚y撖h鍛d撘d撢d撳q擋d鍞k鍟s鍠h鍡w鍢f鍣z鍤c鍥q鍦s鍧h鍨k鍩n鍪m鍫q鍬q鍭h鍯c鍱y鍲m鍳j鍵j鍶s鍷k鍸h鍹x攪j鍺z鍻j鍼z鍽b鍾z鍿z鎀x鎁y鎂m鎃p鎄a鎅j鎆q鎇m鎈c鎉d鎊b攵p鎋x鎌l鎍s數s鎏l鎐y鎑y鎒n鎓w鎔r鎕t鎖s鎘g鎙s鎛b鎜p鎝d鎞b鎟s鎠g鎡z鎤h暢c鎦l鎧k鎨s鎩s鎪s鎫w鎬g鎭z鎮z鎯l鎰y鎱y鎲t鎳n鎴x鎵j鎶g鎷m鎸j鎹k鎺h鎻s鎼x鎽f鎾w鎿n鏀l鏁s鏂o鏃z鏄t鏅x鏆g鏇x鏈l鏉s鏊a鏋m鏌m鏍l鏎b鏏w鏐l鏑d鏒s鏔y鏕l鏖a鏗k鏘q鏙c鏚q鏛s鏝m鏞y鏟c鏠f鏡j鏢b鏣s鏤l鏥x枧j枴g柙x鏧l鏨z鏩j鏪c鏫l鏬x鏭x鏮k鏯s鏰b鏱z鏲q鏳z鏴l鏵h鏶j鏸h鏹q鏺p鏻l鏼s鏽x鏾s鏿c鐀g鐁s鐂l鐃n鐄h鐅p鐆s鐇f鐈q鐉q鐊x鐋t鐌x鐎j鐏z鐐l鐑q鐒l鐓d鐔x鐕z鐖j鐗j鐘z鐙d鐚y鐛y鐜d鐝j鐞n鐟z鐠p鐡t鐣c鐤d鐥s鐦k鐧j鐨f鐩s鐪l鐫j鐬h鐭y鐮l鐯z鐰q鐱j鐲z鐴b鐵t榦g鐷y鐸d鐹g鐻j鐽d鐾b鐿y鑀a構g鑁z鑂x槔g鑄z鑅h鑆z鑇j鑈n鑉h鑊h鑋q鑌b樣y鑎g鑏n鑐x鑑j鑒j鑓y鑔c鑕z鑖m鑗l鑘l檑l鑜s鑝p檣q鑞l鑟d鑠s鑡c鑢l鑣b鑤b鑥l鑦x鑧k鑨l鑩e櫃g鑪l鑫x鑬j櫞y鑮b鑯j鑰y鑱c鑲x鑳j鑴x鑵g鑷n鑸l鑹c鑺q鑻p鑼l鑽z鑾l鑿z钀n钁j欽q钃z钄l钅j钆g钇y钊z钋p钌l钍t钏c钐s钑s钔m钕n钖y钗c钚b钛t钜j钣b钤q钪k钫f钬h钭t钯b钰y钲z钴g钶k钷p钸b钹b钼m钽t铄s铈s铉x铊s铋b铌n铍p铎d铏x铐k铑l铒e铓m铔y铕y铖c铗j铘y铙n铚z铛c铞d铟y铠k铢z汙w铤d铥d铦x铧h铨q铩s铪h铫d铮z铯s铳c铴t铵a铷r铹l铻w铼l铽t铿k锂l锃z锆g锇e锉c锊l锍l锎k锏j況k锒l锓q锔j锕a锖q锘n锛b锜q锝d锞k锟k锠c锢g锧z锩j锪h锫p锬t锱z锲q锴k锵q锶s锷e锸c锺z锼s锽h锾h锿a镂l镃z镄f镅m镆m镈b镉g镋t镌j镎n镏l镒y镓j镔b镕r镖b镗t镘m镙l镚b镛y镝d镞z镟x镠l镡t镢j镤p淺q镥l镦d镧l镨p镩c镫d镬h镯z镱y镲c镳b镴l镵c長c镸c镹j溈w镻d镼j镽l門m閂s閃s閄h滌d閆y閇b閈h閉b閊c開k閌k閍b閎h閏r閐s閑x閒x漁y閔m閕x閖l閗d閘z閙n閚z閛p閜x閝l閞b閠r閡h関g閣g閤g閦c閧h閨g閩m閪s閫k閬l閭l閮t閰j閱y閲y潿w閳c閴q閶c閷s閸k閹y閺w閻y閼e閽h閾y閿w闀h闁b濁z闄y闅w闆b闈w闉y闊k闋q闌l闍d闎q闏p闐t濾l闓k瀾l闕q闖c闗g闘d闙q闚k闛t關g闝p闞k闟x闠h闡c闤h闥t闦w闩s闫y闬h闱w闳h闵m闶k闼t闾l闿k阃k阄j阆l阇d阈y阊c阋x阌w阍h阏e阒q阓h阕q阖h阗t阘t阙j阚k阛h阝f阞l阠x烴t阡q阢w阣y阤t阥y阦y阧d阨a阩s阪b阫p阬k阭y阯z阰p阱j阷c阸e阹q阺d阼z阽d阾l陁t陂b陃b陉x陊d煥h陎s陏d陑e陒g陓y陔g陖j陗q陘x陙c陚w陜x陝s陞s陟z陠p陣z陦d陧n陫f燈d燜m陭q陮d爍s陰y陱j陲c陳c陴p陸l陹s険x陻y陼z陽y陾r陿x隀c隁y隂y隃y隄d隇w隈w隉n隊d隌a牾w隍h階j隑g隒y隓h犋j隕y隖w隗k隚t際j犖l隝d隞a犢d隟x隠y隡s隢r隣l隤t隥d隦p隨s隩a險x隬n隭e隮j隯d隰x隱y隲z隴l隵x隷l隸l隹z隺h隻z隼s隽j隿y雂q雃j雈h雉z雊g雋j雎j雐h雑z雒l雓y雔c雖s雗h雘h雙s雚g雛c雜z雝y雞j雟g雡l離l難n雤y雥z雦c雧j雩y雫n雬f獎j雭s雮m雯w雰f雱p雲y雳l雴c雵y雸a獮x電d雼d雽h雿d霁j霂m霃c霅z霆t霈p霊l霋q霌z霎s霏f霐h霑z霒y霔s霕t霗l霘d霙y霚w霛l霝l霟h霠y霡m霢m霤l霥m霦b霧w霨w霩k霪y霫x霬y霭a霮d霯t霰s霱y霳l霴d霵j霶p霷y霺w霼x瑋w霽j霾m霿m靀m靁l靂l靃h靄a靅f靆d靇l靈l靉a靊f靋l靌b靍h靎h靏h靐b璁c靑q靓j靔t靕z靗c靘q靚j靜j靝t靟f靣m靤p靦m靧h靨y靪d靫c靬j靭r靮d靯d靰w靱r靲q靵n靷y環h靸s靹n靺m靻z靼d靽b靾x靿y鞀t鞁b瓔y鞂j鞃h鞄p鞅y鞆b鞇y鞈g鞉t鞊j鞌a鞎h鞏g鞐k鞑d鞒q鞓t甌o鞔m鞕b鞖s鞗t鞙x鞚k甕w鞛b鞜t鞝s鞞b鞟k鞡l鞢x鞣r鞤b鞥e鞦q鞧q鞨h鞩q鞪m鞫j鞬j鞮d鞯j鞰w鞱t鞲g鞳t鞴b鞵x畢b鞶p鞷g鞸b鞹k鞺t鞻l鞼g當d鞽q鞾x鞿j韀j疇c韂c韃d韅x韆q韇d韈w韉j韊l痠s韋w韌r韍f韎m韏q韐g韑w韒q韓h韔c韕k痺b韖r韗y韘s韙w韚g韛b韜t韝g瘋f韞y韟g韠b韡w韢s韣d韤w韥d韨f韪w韫y韬t韮j韯x韰x韱x韲j韴z韷l韸p韹h韺y韻y韼p韽a韾y響x頀h頁y頂d頃q項x癉d頇h須x頉y癘l頊x頋e頌s頍k頎q頏h癤j預y頑w頒b頓d頔d頕d頖p頗p領l頙c頚j頛l頜h頝q頞e頟e頠w頡j頢k頣s頤y頥y頦k頧d頨y頩p頪l頫f頬j頭t頮h頯k頰j頱l頲t頳c頴y頵j頶h頷h頸j頹t頺t頻b頼l頽t頾z頿z顀c顁d顂l顃t顄h顅q顆k顇c顈j顉q顊y顋s題t額e顎e顏y眙y顐w顒y顓z顔y顕x顖x顗y願y顙s顚d顛d顜j眾z顝k類l顟l顠p顡w顢m顣c顤y顥h顦q顧g顨x顩y顪h顫c顬r顭m顮b顯x顰p瞇m顱l顲l顳n顴q顸h顼x颀q颉j颋t颌g颎j颏k颒h颔h颕y颙y颚e颛z颞n颟m颡s颣l颥r颦p風f颩d颪g颫f颬x颭z颮b颯s颰b颱t颲l颳g颴x颵x颶j颷b颸s颹w颺y颻y颼s颽k颾s颿f飀l飁x飂l飃p飄p飅l飆b飇b飈b飉l飊b飋s飌f飍x飏y飐z飑b飒s飓j飔s飕s飖y砦z飗l飙b飚b飛f飜f飝f飡c飢j飣d飤s飥t飦z飧s飨x飩t飪r飫y飬y飭c飮y飯f飰f飱s碭d飲y飳z飴y飵z飶b飷j確q飸t飹b飺c飻t飼s飽b飾s飿d餀h餁r餂t餃j餄h餅b餆y餇t餈c餉x養y餋j餌e磽q餍y餎l餏x餑b餒n餓e餔b餕j餖d餗s餘y餙s餚y餛h餜g餝s餞j餟c餠b餡x餢b餣y餤d餥f餦z餧w館g餩e餪n餫y餬h餭h餮t餯h餰j餱h餲a餳x礬f餵w餶g餷c餸s餹t餺b餻g餼x餽k餾l餿s饀t饂w饃m饄t饅m饆b饇y饈x饉j饊s饋k饌z饍s饎x饏d饐y饑j饒r饔y饕t饖w饗x饘z饚h饛m饜y饝m饞c饟x饠l饡z饢n饣s饤d饦t饧x饨t禪s饩x饪r饫y饬c饳d饴y饷x饸h饹g饻x饽b饾d馀y馂j馃g馄h馇c馉g馊s馌y馎b馐x馑j馓s馔z馕n馘g馛b馜n馝b馞b馟t馠h馡f馢j馣a馤a馥f馦x馧y馨x穎y穡s馪p馫x馬m馭y馮f馯h馰d馱t馲t馳c馴x馵z馶z馷p馸x馹r馺s馻y馼w馽z馾d馿l駀y竇d駂b駃j筅x駅y駆q駇w駈q駉j駊p駋z駌y筧j駍p駎z駏j駐z駑n駒j駓p駔z駕j駖l駗z駘t駙f駚y駛s駜b駝t駞t駟s箢y駠l駡m駢p駣t駤z駥r駦t駧d駨x駩q駪s駫j駬e駭h駮b簞d駰y駱l駲z駳d駴h駵l駶j駷s駸q駹m駺l駻h駼t駽x駾t駿j騀e騁c騂x騃a騄l騅z騆z騇s騈p騉k騊t騋l騌z騍k騎q騏q騐y騑f騒s験y騔g騕y騖w騗p騙p騚q騛f紆y騜h騝q騞h騟y騠t騡q騢x騣z純c騥r騦s騧g騨t絕j騩g騪s騬c騭z騮l騯p騰t騱x騲c騳d騴y綽c緄g騶z騷s騸s騹q騺z騻s騼l騽x騾l騿z縋z驁a驂c驃p驄c驅q縲l驆b驇z驈y驉x驊h驋b驌s驍x驎l驏z驐d繃b驑l驒t驓c驔d驕j驖t驗y驘l驙z驚j驛y驜y繽b驝t驞p驟z驠y驡l驢l驣t驤x驥j驦s驧j驨x驩h驪l驫b驲r驵z驷s驸f驺z驽n驿y骀d骁x骃y骅h骈p骉b骊l骍x骎q骐q骒k骓z骔z骕s骖c骘z骙k罷b骛w骜a骝l骞q骟s骠b骢c骣c骥j骦s骧x骩w骪w骫w骬y骭g骮y骯a骰t骱j骲b骳b骴c骵t羨x骶d骷k骹q骺h骻k骼g骽t骾g骿p羻q髀b髁k髂q髃y髄s髅l髆b髇x髈b髉b髋k髌b髎l髏l聖s髐x髑d髒z體t髕b髖k髗l髙g髚q髛k髜q髝l髞s髟b髠k髡k髢d肀y髣f髤x髥r髦m髧d髩b髪f髫t髬p髭z髮f髯r髰t胙z髱b髲b髳m髴f髷q髸g髹x髺k髻j髽z髾s髿s鬀t鬁l膣z鬄t鬅p鬆s鬇z鬈q鬉z鬊s鬋j鬌d鬍h鬎l鬏j鬐q鬑l鬒z鬓b鬔p鬕m鬖s鬗m鬘m鬙s鬚x鬛l鬜q鬝q鬞n鬟h鬠k臘l鬡n鬢b鬣l鬤r鬥d臥w鬧n鬨h鬩x鬪d鬬d鬭d鬮j鬯c鬰y艙c鬳y艤y鬴f鬵z鬶g鬷z鬸l鬹g鬺s鬻y鬽m鬾j鬿q魀g魃b魅m魆x魇y芐h魉l魊y魋t魍w魎l魐g魑c魒p魕j魖x魗c魘y魙z魚y荮z莖j魜r魝j魞a魟h魠t魡d魢j魣x魤e魥j魦s魧h菪d菴a魩m萇c魪j魫s魬b魭y魮p魯l魰w葒h魲l魳z魴f魶n魷y魸n魹m魺h魻x魼q魽h魾p魿l鮀t蒹j蓀s鮂q鮃p鮄f鮅b鮆c鮇w鮈j鮉d蓮l鮊b鮋y鮌g鮍p鮎n鮏x鮐t鮑b蔆l鮒f鮓z鮔j鮕g鮖s蔞l蕆c蕘r鮘c鮙t鮚j鮛s鮜h鮝x鮞e鮟a鮠w鮡z鮢z鮣y鮤l鮥l鮦t鮧y鮨y鮩b鮪w鮫j鮬k鮭g鮮x鮯g鮰h鮱l鮲m鮳k鮴x鮵t鮶j鮷t鮸m鮹s鮺z鮻s鮼q鮽y鮾n鯀g鯁g鯂s鯃w鯄q鯅s鯆p鯇h鯈t蘞l鯊s鯋s鯌k鯍m鯎c鯏l虼g鯐s鯑k鯒y鯓s鯔z鯕q鯖q鯗x鯘n鯚j鯛d鯜q鯝g鯞z鯟d鯠l鯡f鯢n鯣y鯤k蜴y鯦j鯧c鯨j鯩l鯪l螄s螈y鯬l鯭m鯮z鯯z鯰n鯱x鯲d鯳s鯴s鯵s鯶h鯷t鯹x鯺z鯻l鯼z鯽j鯾b鯿b鰀h蟛p鰁q鰂z鰃w蟣j鰄w鰅y鰆c鰇r鰈d鰉h鰊l鰋y鰌q鰍q鰎j鰏b蠶c鰑y蠻m衊m鰓s鰔j鰕x鰖t鰗h鰘s鰙r鰚h鰛w鰜j鰝h鰞w鰟p鰠s鰡l鰢m鰣s鰤s鰥g鰦z鰧t鰨t鰩y鰪e鰫y鰬q鰭q鰮w鰯r鰰h鰱l鰲a袼g裉k鰴h鰵m鰶j鰷t鰸q鰹j鰺s鰻m鰼x鰽q鰿j鱀j鱁z鱂j鱃x鱄z鱅y鱆z鱇k鱈x襖a襲x鱊y鱋q鱌x鱍b鱎j鱏x鱐s鱑h鱒z覈h觀g觔j鱔s鱕f鱖g鱗l鱘x鱙y鱚x鱛a鱜k鱞g鱟h鱠k鱡z鱢s鱣z鱤g鱥g計j鱦y鱧l鱨c鱩h鱪s鱫a訟s訥n訶h鱭j鱮x鱯h鱰s鱱l鱲l鱳l鱴m鱵z鱶x鱷e鱸l鱹g鱺l鱻x鱽d鱾j試s鱿y鲀t鲂f詬g鲃b鲄h鲅b詰j鲆p鲇n詳x鲈l鲉y鲊z鲋f詿g鲌b鲎h誄l鲏p鲐t鲑g鲒j鲓k鲔w鲕e鲖t鲗z鲘h鲙k鲚j鲛j鲝z鲞x鲟x誦s鲠g鲡l說s鲢l鲣j鲥s鲦t誹f鲧g鲨s鲩h鲪j鲫j鲬y鲭q鲮l鲯q鲰z鲱f鲲k鲳c鲴g鲵n鲶n鲷d鲹s鲺s諦d鲻z鲼f諭y鲽d鲾b鲿c鳀t諼x謄t鳁w鳂w鳄e謖s鳅q鳆f謾m譏j鳇h鳈q鳉j鳊b變b鳌a鳍q鳎t鳏g鳐y鳑p讞y鳓l鳔b鳕x鳗m鳘m鳙y鳚w鳜g鳝s鳟z鳠h鳡g鳢l鳣z鳤g鳥n鳦y鳧f豊l鳨l鳪b鳫y鳬f鳭d鳮j鳯f鳰n鳱g鳲s鳳f鳴m鳵b鳶y貞z買m費f貽y鳸h賀h鳹q鳺f鳻b鳼w鳽j鳾s鳿y鴀f鴁y鴂j鴃j鴄p賍z鴅h鴆z鴇b鴈y鴉y鴊z鴋f鴌f賚l賦f賫l鴎o賱y鴏d賲b鴐j鴑r鴒l鴓m鴔f鴕t鴖m鴗l鴘b鴙z鴚g鴛y鴜c鴝q鴞x鴟c鴠d鴡j鴣g鴤z鴥y鴦y鴧y鴨y鴩d鴪y贖s鴬y鴭d赾q鴰g鴱a鴲z鴳y鴴h鴵x鴶j鴷l鴸z鴹y鴺y鴻h鴼l鴽r趒t鴾m鴿g趕g鵁j鵂x鵃z鵄c鵅l鵆q鵇t鵈e趛y鵊j鵋j趢l鵌t鵍h鵎t趦z趬q趶k鵐w鵒y鵓b鵔j鵕j跊m鵖b鵗x鵘j鵙j鵚t鵛j鵜t鵝e鵞e鵟k鵠h鵢s鵣l鵤z跮d鵦l鵧p鵨s鵩f鵪a鵫z鵬p踇m鵭q鵮q鵯b踋j鵰d鵱l鵲q踐j鵳j鵴j鵵t鵶y鵷y踖j鵸q鵹l鵺y鵻z鵼k鵽d踜l鵾k鵿s鶀q踠w鶁j鶂y踤z踨z踼t鶄j鶅z鶆l鶇d蹅c蹆t蹌q蹎d蹏t鶉c鶊g鶋j鶌q鶍y鶎k鶏j鶐s鶑y鶒c鶓m躌w躤j鶖q躪l鶗t鶘h鶙t鶚e躳g躴l躼l軂l軅y軈y軍j鶜m鶝f鶞c鶟t鶠y鶡h鶢y鶣p鶤k鶥m鶦h軛e鶧y軞m軭k軯p軳p鶩w鶪j軸z輇q鶬c鶭f鶮h鶯y鶰y鶱x鶲w輑y鶴h鶵c輜z鶶t鶷x鶸r輠g鶹l鶺j鶻g鶼j輧p輪l輯j輳c輼w鶾h鶿c鷀c鷁y鷂y轋h轌x轓f鷄j鷅l鷆t鷇k鷈t鷉t鷊y鷋t鷌m鷍x鷎g鷏t鷐c鷑j鷒t鷓z轙y鷔a轚j鷕y鷖y轜e鷗o鷘c鷙z鷚l鷛y轣l鷝b鷞s鷟z鷠y鷡w鷢j鷣y鷤t鷥s鷦j鷨h鷩b鷪y鷫s鷬h辀z鷮j辎z鷯l辤c鷱g辧b鷲j鷳x鷴x鷵t鷶m鷷z鷸y鷹y鷺l鷻t鷼x鷽x辭c鷿p鸀z鸁l鸂x鸃y鸄j鸅z鸆y鸇z辴z鸈y鸊p鸋n鸌h辺d鸍m鸎y鸏m鸐d鸑y迀g鸒y鸓l鸔b鸕l鸖h鸗l鸘s鸙y鸚y鸛g鸜q鸝l鸞l鸠j鸢y鸤s鸧c鸨b鸩z鸪g鸫d鸬l鸮x鸰l鸱c鸲q鸴x鸶s鸷z鸸e鸹g鸺x鸻h鸼z鸾l鹀w鹁b鹂l鹄g鹆y鹇x迒h鹈t迓y鹉w鹋m鹌a鹍k迠c鹎b迡c鹐q鹑c迣z迦j迧c鹓y鹔s迶y鹖h鹗e迼j鹘g迿x鹙q鹚c鹛m逌y逘y鹜w鹝y鹞y鹟w鹠l鹡j鹢y鹣j鹥y鹦y鹨l鹩l鹪j逤s鹫j逩b逫z逰y鹬y鹭l鹮h鹯z鹱h鹲m鹳g鹵l鹷l鹸j鹹x鹻j鹼j鹽y鹾c逽n逿d遃y遆t遉z麁c麂j麃b麄c麅p麆z麇j麈z麉j麊m麋b麌y麍l麎c麏j達d麐l違w麑n麒q麔j遚c麕j麖j麗l麘x麙x麚j麛m麜l麝s麞z麟l麠j麢l麣y麤c麥m麧h麨c麩f麪m麫m麬f麭p麮q麯q麰m麱f麲x麳l麴q麵m麶c麷f麸f麹q麺m適s麼m麽m遬s麾h麿m遰d黀z遱l黁n遳c黃h黅j黆g黇t黈t黉h黊h黋k黌h黏n黐c黒h黓y黕d黖x黗t黙m黚q黛d黜c黝y點d黟y黠x黡y黢q黣m黤y黥q黦y黧l黨d黩d黪c黫y邍y黬y邎y黭y黮d邐l黯a黰z黱d黲c黳y黴m黵d黶y黷d黸l黹z黻f黼f黽m邗h黾m黿y鼀c鼁q鼂c鼃w鼄z鼅z鼆m鼇a邞f鼊b鼋y邨c鼌c邩h鼍t邫b邽g鼑d鼒z鼔g鼕d鼘y鼙p鼚c鼛g鼜c鼝y鼞t鼟t鼡n鼢f鼣f鼤w鼥b鼦d鼧t郋x鼨z郌g鼩q鼪s鼫s鼬y鼭s鼮t鼯w鼰j鼱j鼲h鼳j鼴y鼵t鼶s鼷x鼸x郞l鼹y鼺l鼼y鼽q鼾h鼿w齀w齁h齂x齃e齄z齅x郣b郩x齆w齇z齈n齉n齊q郰z郲l齌j齍z齎j齏j齑j齒c齓c齔c齕h齖y齗y齘x郼y齚z齛x齜z齝c齞y齟j齠t齡l齢l齣c齤q齥x鄅y鄋s齧n齨j鄍m齪c齫y齬y齭c鄒z齯n鄖y鄘y鄤m齱z齲q齳y齴y鄧d鄩x鄪b齶e齷w齸y齹c齺z齻d齼c齾y龀c龁h龂y鄳m鄹z龃j龅b龆t龇z龈y龉y龊c龌w龍l龏g鄿q酅x龑y龒l龓l龔g龕k龗l龘d龛k龜g龝q酨z龞b龢h龣j龤x龥y鳩j鳷z鴍w鴢y鴫t鴮w鴯e鵀r鵉l鵏b鵑j鵡w鵥k鶃y鶛j鶨c鶫d鶳s鶽s鷃y鷜l鷧y鷭f鷰y鷾y鸉y鹒g鹕h鹶j鹺c麀y麡q鼈b鼉t鼏m齋z齙b齦y齮y齰z齵y齽j龎p龖d龡c癷b唉a苯b瞓f硸n秎f斗d稥x坊f竕f告g龟g哼h解j擂l罖r芈m羒f羘z羛y羜z羣q羦h羪y羫q羭y羮g羳f羵f羶s羷l翂f裂l聁u膹f艈u漂p骑q砌q雀q蒊h蒶f蕒m蕡f是s伺c毝c苔t虲u蚠f蝊u蟇m衁h衇m衈e衖x衚h衜d衞w衟d衠z衯f衮g袰b袴k呀y訜f子z豶f亂l傯z僨f償c兇x喲Y嗯E堯Y塏K墳F墾K鶕A鶔r鶈q鱬r鱝f魵f髍m馩f馚f饙f餴f颃h霻f霣y雺w隫f闧u闇a閥f閟b間j镪q钿d鐼f鐢f鏦c鏜t鍂p奮f婁l媵y屢l嶠j幀z幗g憤f錖d錆q銽x橥z歸g殛j炷z甯n瘧n瘵z碡z糞f紛f羥q腳j膩n臺t莜y蓿x褸l誒e讜d讠y贁b趀c趓d趙z趸d跀y跥d輊z輒z轒f轕g遧z邚r邤x郬q郺y酳y酼h酽y醆z醈t醓t醔q醦c釀n釂j釈s釋s釒j釔y釕l釗z釙p釠l釢n釤s釥q鉀j釪h釫h釯m釿j鈃x鈄d鈇f鈈b鈎g鈏y鈑b鈖f鈙q鈚p鈝y鈟d鈠y鈨y鈩h鈱m鈵b鈾y鈿d鉅j鉇s鉒z鉔z鉗q鉞y鉡b鉢b鉥s鉧m鉬m鉲k鉳b鉵t鉽s鉾m銁j銃c銅t銍z銐c銑x銕t銗h銘m銠l銡j銣r銤m銨a銫s銬k銰a銱d銹x銼c銾h銿z鋃l鋊y鋍b鋏j鋐h鋒f鋓c鋔w鋜z鋝l鋫l鋱t鋲b鋶l鋻j鋾t錄l錈j錐z錔t錛b錝c錫x錰s錶b鍃h鍅f鍎t鍜x鍝y鍮t鍰h鍴d鎎k鎗q鎚c鎢w鎣y鎥t鏓c鏷p鐍j鐳l鐶h鐺d鑃d鑍y鑙j鑚z鑛k鑭l鑶c钂t钘x铇b锳y镮h镺a镾m閁m閅m閯s閵l闂h闃g闑n闒t闔h闢p闣d陥x陬z陯l韁j韄h頄q順s顑k飠s饁y饓c駁b駄t駯z騘c騤k騫q騵y鰐e鰒f鰳l鰾p鱉b鱓s鳋s鳒j鳛x鹧z鹴s黂f黺f鼖f齩y驀m髊c髨k髵e髶e髼p鬂b鬦d鬫k鬱y魈x魌q魓b魛d魨t魱h鮁b鮗d鮿z鯙c鯉l鯥l鯫z鯸h龐p".Substring(num + 1, 1); + } + // + // 摘要: + // 获取类名 + // + // 参数: + // dbColumnName: + public static string CsharpName(string dbColumnName) + { + if (dbColumnName.Contains("_")) + { + string[] value = (from it in dbColumnName.Split('_') + select FirstUpper(it)).ToArray(); + return string.Join("", value); + } + + return FirstUpper(dbColumnName); + } + // + // 摘要: + // 将值的首字母大写 + // + // 参数: + // value: + // 值 + public static string FirstUpper(string value) + { + string text = value.Substring(0, 1).ToUpper(); + return text + value.Substring(1, value.Length - 1); + } + } +} \ No newline at end of file diff --git a/Infrastructure/Filter.cs b/Infrastructure/Filter.cs new file mode 100644 index 0000000..ae70ce3 --- /dev/null +++ b/Infrastructure/Filter.cs @@ -0,0 +1,25 @@ +namespace Infrastructure +{ + + public class Filter + { + public string Key { get; set; } + public string Value { get; set; } + public string Contrast { get; set; } + + public string Text { get; set; } + } + + public class FilterGroup + { + /// + /// or /and + /// + public string Operation { get; set; } + public Filter[] Filters { get; set; } + public FilterGroup[] Children { get; set; } + } + + + +} \ No newline at end of file diff --git a/Infrastructure/GenerateId.cs b/Infrastructure/GenerateId.cs new file mode 100644 index 0000000..6277b60 --- /dev/null +++ b/Infrastructure/GenerateId.cs @@ -0,0 +1,126 @@ +// *********************************************************************** +// Assembly : FairUtility +// Author : Yubao Li +// Created : 10-13-2015 +// +// Last Modified By : Yubao Li +// Last Modified On : 10-13-2015 +// *********************************************************************** +// +// Copyright (c) . All rights reserved. +// +// 创建唯一ID +// *********************************************************************** + +using System; +using System.Security.Cryptography; + +namespace Infrastructure +{ + public class GenerateId + { + public static string GetGuidHash() + { + return Guid.NewGuid().ToString().GetHashCode().ToString("x"); + } + + /// + /// 生成一个长整型,可以转成19字节长的字符串 + /// + /// System.Int64. + public static long GenerateLong() + { + byte[] buffer = Guid.NewGuid().ToByteArray(); + return BitConverter.ToInt64(buffer, 0); + } + + /// + /// 生成16个字节长度的数据与英文组合串 + /// + public static string GenerateStr() + { + long i = 1; + + foreach (byte b in Guid.NewGuid().ToByteArray()) + { + i *= ((int)b + 1); + } + + return String.Format("{0:x}", i - DateTime.Now.Ticks); + } + + /// + /// 创建11位的英文与数字组合 + /// + /// System.String. + public static string ShortStr() + { + return Convert(GenerateLong()); + } + + /// + /// 唯一订单号生成 + /// + /// + public static string GenerateOrderNumber() + { + string strDateTimeNumber = DateTime.Now.ToString("yyyyMMddHHmmssffff"); + string strRandomResult = NextRandom(1000, 1).ToString("0000"); + + return strDateTimeNumber + strRandomResult; + } + + #region private + + /// + /// 参考:msdn上的RNGCryptoServiceProvider例子 + /// + /// + /// + /// + private static int NextRandom(int numSeeds, int length) + { + // Create a byte array to hold the random value. + byte[] randomNumber = new byte[length]; + // Create a new instance of the RNGCryptoServiceProvider. + RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider(); + // Fill the array with a random value. + rng.GetBytes(randomNumber); + // Convert the byte to an uint value to make the modulus operation easier. + uint randomResult = 0x0; + for (int i = 0; i < length; i++) + { + randomResult |= ((uint) randomNumber[i] << ((length - 1 - i)*8)); + } + + return (int) (randomResult%numSeeds) + 1; + } + + + + static string Seq = "s9LFkgy5RovixI1aOf8UhdY3r4DMplQZJXPqebE0WSjBn7wVzmN2Gc6THCAKut"; + + /// + /// 10进制转换为62进制 + /// + /// + /// + + private static string Convert(long id) + { + if (id < 62) + { + return Seq[(int) id].ToString(); + } + int y = (int) (id%62); + long x = (long) (id/62); + + return Convert(x) + Seq[y]; + } + + #endregion + + + + } +} \ No newline at end of file diff --git a/Infrastructure/Helpers/AESHelper.cs b/Infrastructure/Helpers/AESHelper.cs new file mode 100644 index 0000000..35dad75 --- /dev/null +++ b/Infrastructure/Helpers/AESHelper.cs @@ -0,0 +1,97 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Security.Cryptography; +using System.Text; +using System.Threading.Tasks; + +namespace Infrastructure.Helpers +{ + public class AESHelper + { + // + // 摘要: + // 32位处理key + // + // 参数: + // key: + // 原字节 + private static string GetAesKey(string key) + { + int length = key.Length; + if (length < 32) + { + for (int i = 0; i < 32 - length; i++) + { + key += "0"; + } + + return key; + } + + return key[..32]; + } + + // + // 摘要: + // 使用AES加密字符串 + // + // 参数: + // content: + // 加密内容 + // + // key: + // 秘钥 + // + // vi: + // 偏移量 + // + // 返回结果: + // Base64字符串结果 + public static string AesEncrypt(string content, string key, string vi = "1234567890000000") + { + byte[] bytes = Encoding.UTF8.GetBytes(GetAesKey(key)); + byte[] bytes2 = Encoding.UTF8.GetBytes(vi); + byte[] bytes3 = Encoding.UTF8.GetBytes(content); + SymmetricAlgorithm symmetricAlgorithm = Aes.Create(); + symmetricAlgorithm.IV = bytes2; + symmetricAlgorithm.Key = bytes; + symmetricAlgorithm.Mode = CipherMode.CBC; + symmetricAlgorithm.Padding = PaddingMode.PKCS7; + ICryptoTransform cryptoTransform = symmetricAlgorithm.CreateEncryptor(); + byte[] inArray = cryptoTransform.TransformFinalBlock(bytes3, 0, bytes3.Length); + return Convert.ToBase64String(inArray); + } + + // + // 摘要: + // 使用AES解密字符串 + // + // 参数: + // content: + // 内容 + // + // key: + // 秘钥 + // + // vi: + // 偏移量 + // + // 返回结果: + // UTF8解密结果 + public static string AesDecrypt(string content, string key, string vi = "1234567890000000") + { + byte[] bytes = Encoding.UTF8.GetBytes(GetAesKey(key)); + byte[] bytes2 = Encoding.UTF8.GetBytes(vi); + byte[] array = Convert.FromBase64String(content); + SymmetricAlgorithm symmetricAlgorithm = Aes.Create(); + symmetricAlgorithm.IV = bytes2; + symmetricAlgorithm.Key = bytes; + symmetricAlgorithm.Mode = CipherMode.CBC; + symmetricAlgorithm.Padding = PaddingMode.PKCS7; + ICryptoTransform cryptoTransform = symmetricAlgorithm.CreateDecryptor(); + byte[] bytes3 = cryptoTransform.TransformFinalBlock(array, 0, array.Length); + return Encoding.UTF8.GetString(bytes3); + } + } +} diff --git a/Infrastructure/Helpers/CommonHelper.cs b/Infrastructure/Helpers/CommonHelper.cs new file mode 100644 index 0000000..81752e6 --- /dev/null +++ b/Infrastructure/Helpers/CommonHelper.cs @@ -0,0 +1,122 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Text; + +namespace Infrastructure.Helpers +{ + /// + /// 常用公共类 + /// + public class CommonHelper + { + #region Stopwatch计时器 + /// + /// 计时器开始 + /// + /// + public static Stopwatch TimerStart() + { + Stopwatch watch = new Stopwatch(); + watch.Reset(); + watch.Start(); + return watch; + } + /// + /// 计时器结束 + /// + /// + /// + public static string TimerEnd(Stopwatch watch) + { + watch.Stop(); + double costtime = watch.ElapsedMilliseconds; + return costtime.ToString(); + } + #endregion + + #region 删除数组中的重复项 + /// + /// 删除数组中的重复项 + /// + /// + /// + public static string[] RemoveDup(string[] values) + { + List list = new List(); + for (int i = 0; i < values.Length; i++)//遍历数组成员 + { + if (!list.Contains(values[i])) + { + list.Add(values[i]); + }; + } + return list.ToArray(); + } + #endregion + + #region 自动生成日期编号 + /// + /// 自动生成编号 201008251145409865 + /// + /// + public static string CreateNo() + { + Random random = new Random(); + string strRandom = random.Next(1000, 10000).ToString(); //生成编号 + string code = DateTime.Now.ToString("yyyyMMddHHmmss") + strRandom;//形如 + return code; + } + #endregion + + #region 生成0-9随机数 + /// + /// 生成0-9随机数 + /// + /// 生成长度 + /// + public static string RndNum(int codeNum) + { + StringBuilder sb = new StringBuilder(codeNum); + Random rand = new Random(); + for (int i = 1; i < codeNum + 1; i++) + { + int t = rand.Next(9); + sb.AppendFormat("{0}", t); + } + return sb.ToString(); + + } + #endregion + + #region 删除最后一个字符之后的字符 + /// + /// 删除最后结尾的一个逗号 + /// + public static string DelLastComma(string str) + { + return str.Substring(0, str.LastIndexOf(",")); + } + /// + /// 删除最后结尾的指定字符后的字符 + /// + public static string DelLastChar(string str, string strchar) + { + return str.Substring(0, str.LastIndexOf(strchar)); + } + /// + /// 删除最后结尾的长度 + /// + /// + /// + /// + public static string DelLastLength(string str, int Length) + { + if (string.IsNullOrEmpty(str)) + return ""; + str = str.Substring(0, str.Length - Length); + return str; + } + #endregion + } +} diff --git a/Infrastructure/Helpers/ConfigHelper.cs b/Infrastructure/Helpers/ConfigHelper.cs new file mode 100644 index 0000000..5574f7a --- /dev/null +++ b/Infrastructure/Helpers/ConfigHelper.cs @@ -0,0 +1,23 @@ +using System; +using Enyim.Caching.Configuration; +using Microsoft.Extensions.Configuration; + +namespace Infrastructure.Helpers +{ + public class ConfigHelper + { + public static IConfigurationRoot GetConfigRoot() + { + var configurationBuilder = new ConfigurationBuilder(); + configurationBuilder.SetBasePath(System.IO.Directory.GetCurrentDirectory()) + .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true) + .AddJsonFile( + $"appsettings.{Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") ?? "Development"}.json", + optional: true) + .AddEnvironmentVariables(); + + var configuration = configurationBuilder.Build(); + return configuration; + } + } +} \ No newline at end of file diff --git a/Infrastructure/Helpers/DateTimeHelper.cs b/Infrastructure/Helpers/DateTimeHelper.cs new file mode 100644 index 0000000..07a29ce --- /dev/null +++ b/Infrastructure/Helpers/DateTimeHelper.cs @@ -0,0 +1,42 @@ +using System; + +namespace Infrastructure.Helpers +{ + public class DateTimeHelper + { + public static string FriendlyDate(DateTime? date) + { + if (!date.HasValue) return string.Empty; + + string strDate = date.Value.ToString("yyyy-MM-dd"); + string vDate = string.Empty; + if(DateTime.Now.ToString("yyyy-MM-dd")==strDate) + { + vDate = "今天"; + } + else if (DateTime.Now.AddDays(1).ToString("yyyy-MM-dd") == strDate) + { + vDate = "明天"; + } + else if (DateTime.Now.AddDays(2).ToString("yyyy-MM-dd") == strDate) + { + vDate = "后天"; + } + else if (DateTime.Now.AddDays(-1).ToString("yyyy-MM-dd") == strDate) + { + vDate = "昨天"; + } + else if (DateTime.Now.AddDays(2).ToString("yyyy-MM-dd") == strDate) + { + vDate = "前天"; + } + else + { + vDate = strDate; + } + + return vDate; + } + } +} + diff --git a/Infrastructure/Helpers/Excel/ExcelConfig.cs b/Infrastructure/Helpers/Excel/ExcelConfig.cs new file mode 100644 index 0000000..f7c38b7 --- /dev/null +++ b/Infrastructure/Helpers/Excel/ExcelConfig.cs @@ -0,0 +1,122 @@ +using Infrastructure.Helpers.Excel.Model; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Infrastructure.Helpers.Excel +{ + public class ExcelConfig + { + /// + /// 文件名 + /// + public string FileName { get; set; } + /// + /// 标题 + /// + public string Title { get; set; } + /// + /// 前景色 + /// + public System.Drawing.Color ForeColor { get; set; } + /// + /// 背景色 + /// + public System.Drawing.Color Background { get; set; } + private short _titlepoint; + /// + /// 标题字号 + /// + public short TitlePoint + { + get + { + if (_titlepoint == 0) + { + return 20; + } + else + { + return _titlepoint; + } + } + set { _titlepoint = value; } + } + private short _headpoint; + /// + /// 列头字号 + /// + public short HeadPoint + { + get + { + if (_headpoint == 0) + { + return 10; + } + else + { + return _headpoint; + } + } + set { _headpoint = value; } + } + /// + /// 标题高度 + /// + public short TitleHeight { get; set; } + /// + /// 列标题高度 + /// + public short HeadHeight { get; set; } + private string _titlefont; + /// + /// 标题字体 + /// + public string TitleFont + { + get + { + if (_titlefont == null) + { + return "微软雅黑"; + } + else + { + return _titlefont; + } + } + set { _titlefont = value; } + } + private string _headfont; + /// + /// 列头字体 + /// + public string HeadFont + { + get + { + if (_headfont == null) + { + return "微软雅黑"; + } + else + { + return _headfont; + } + } + set { _headfont = value; } + } + /// + /// 是否按内容长度来适应表格宽度 + /// + public bool IsAllSizeColumn { get; set; } + /// + /// 列设置 + /// + public List ColumnEntity { get; set; } + + } +} diff --git a/Infrastructure/Helpers/Excel/ExcelHelper.cs b/Infrastructure/Helpers/Excel/ExcelHelper.cs new file mode 100644 index 0000000..21a2ff8 --- /dev/null +++ b/Infrastructure/Helpers/Excel/ExcelHelper.cs @@ -0,0 +1,505 @@ +using NPOI.HPSF; +using NPOI.HSSF.UserModel; +using NPOI.SS.UserModel; +using NPOI.XSSF.UserModel; +using System; +using System.Collections.Generic; +using System.Data; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Infrastructure.Helpers.Excel +{ + public class ExcelHelper + { + #region DataTable导出到Excel的MemoryStream + /// + /// DataTable导出到Excel的MemoryStream Export() + /// + /// DataTable数据源 + /// 导出设置包含文件名、标题、列设置 + public static MemoryStream ExportMemoryStream(DataTable dtSource, ExcelConfig excelConfig) + { + Dictionary DataColumnMap = new Dictionary(); + + //DataTable dt = dtSource.Clone(); + //var columns = dt.Columns; + foreach (DataColumn column in dtSource.Columns) + { + bool v = excelConfig.ColumnEntity.Exists(t => t.Column == column.ColumnName); + if (v) + { + DataColumnMap.Add(column.ColumnName, column); + //dtSource.Columns.Remove(column.ColumnName); + } + /*else { + DataColumnMap.Add(column.ColumnName, column); + }*/ + } + + HSSFWorkbook workbook = new HSSFWorkbook(); + ISheet sheet = workbook.CreateSheet(); + #region 右击文件 属性信息 + { + DocumentSummaryInformation dsi = PropertySetFactory.CreateDocumentSummaryInformation(); + dsi.Company = "山东慧创信息科技有限公司"; + workbook.DocumentSummaryInformation = dsi; + + SummaryInformation si = PropertySetFactory.CreateSummaryInformation(); + si.Title = "标题信息"; //填加xls文件标题信息 + si.Subject = "主题信息";//填加文件主题信息 + si.CreateDateTime = System.DateTime.Now; + workbook.SummaryInformation = si; + } + #endregion + + #region 设置标题样式 + ICellStyle headStyle = workbook.CreateCellStyle(); + int[] arrColWidth = new int[excelConfig.ColumnEntity.Count]; + string[] arrColName = new string[excelConfig.ColumnEntity.Count];//列名 + ICellStyle[] arryColumStyle = new ICellStyle[excelConfig.ColumnEntity.Count];//样式表 + headStyle.Alignment = HorizontalAlignment.Center; // ------------------ + if (excelConfig.Background != new System.Drawing.Color()) + { + if (excelConfig.Background != new System.Drawing.Color()) + { + headStyle.FillPattern = FillPattern.SolidForeground; + headStyle.FillForegroundColor = GetXLColour(workbook, excelConfig.Background); + } + } + IFont font = workbook.CreateFont(); + font.FontHeightInPoints = excelConfig.TitlePoint; + if (excelConfig.ForeColor != new System.Drawing.Color()) + { + font.Color = GetXLColour(workbook, excelConfig.ForeColor); + } + font.IsBold = true; + headStyle.SetFont(font); + #endregion + + #region 列头及样式 + ICellStyle cHeadStyle = workbook.CreateCellStyle(); + cHeadStyle.Alignment = HorizontalAlignment.Center; // ------------------ + IFont cfont = workbook.CreateFont(); + cfont.FontHeightInPoints = excelConfig.HeadPoint; + cHeadStyle.SetFont(cfont); + #endregion + + #region 设置内容单元格样式 + int colNum = 0; + foreach (var columnentity in excelConfig.ColumnEntity) + { + //DataColumn item = DataColumnMap[column.Column]; + + + + ICellStyle columnStyle = workbook.CreateCellStyle(); + columnStyle.Alignment = HorizontalAlignment.Center; + //Encoding.GetEncoding(936) + arrColWidth[colNum] = Encoding.UTF8.GetBytes(columnentity.Column).Length; + arrColName[colNum] = columnentity.Column.ToString(); + arrColName[colNum] = columnentity.ExcelColumn; + if (columnentity.Width != 0) + { + arrColWidth[colNum] = columnentity.Width; + } + if (columnentity.Background != new System.Drawing.Color()) + { + if (columnentity.Background != new System.Drawing.Color()) + { + columnStyle.FillPattern = FillPattern.SolidForeground; + columnStyle.FillForegroundColor = GetXLColour(workbook, columnentity.Background); + } + } + if (columnentity.Font != null || columnentity.Point != 0 || columnentity.ForeColor != new System.Drawing.Color()) + { + IFont columnFont = workbook.CreateFont(); + columnFont.FontHeightInPoints = 10; + if (columnentity.Font != null) + { + columnFont.FontName = columnentity.Font; + } + if (columnentity.Point != 0) + { + columnFont.FontHeightInPoints = columnentity.Point; + } + if (columnentity.ForeColor != new System.Drawing.Color()) + { + columnFont.Color = GetXLColour(workbook, columnentity.ForeColor); + } + columnStyle.SetFont(font); + } + columnStyle.Alignment = getAlignment(columnentity.Alignment); + arryColumStyle[colNum] = columnStyle; + + colNum++; + } + if (excelConfig.IsAllSizeColumn) + { + #region 根据列中最长列的长度取得列宽 + for (int i = 0; i < dtSource.Rows.Count; i++) + { + for (int j = 0; j < excelConfig.ColumnEntity.Count; j++) + { + if (arrColWidth[j] != 0) + { + //Encoding.GetEncoding(936) + int intTemp = Encoding.UTF8.GetBytes(dtSource.Rows[i][excelConfig.ColumnEntity[j].Column].ToString()).Length; + if (intTemp > arrColWidth[j]) + { + arrColWidth[j] = intTemp; + } + } + + } + } + #endregion + } + #endregion + + int rowIndex = 0; + + #region 表头及样式 + if (excelConfig.Title != null) + { + IRow headerRow = sheet.CreateRow(rowIndex); + rowIndex++; + if (excelConfig.TitleHeight != 0) + { + headerRow.Height = (short)(excelConfig.TitleHeight * 20); + } + headerRow.HeightInPoints = 25; + headerRow.CreateCell(0).SetCellValue(excelConfig.Title); + headerRow.GetCell(0).CellStyle = headStyle; + if (excelConfig.ColumnEntity.Count > 1) + { + sheet.AddMergedRegion(new NPOI.SS.Util.CellRangeAddress(0, 0, 0, excelConfig.ColumnEntity.Count - 1)); // ------------------ + } + } + #endregion + + #region 列头及样式 + { + IRow headerRow = sheet.CreateRow(rowIndex); + rowIndex++; + colNum = 0; + #region 如果设置了列标题就按列标题定义列头,没定义直接按字段名输出 + foreach (var column in excelConfig.ColumnEntity) + { + headerRow.CreateCell(colNum).SetCellValue(arrColName[colNum]); + headerRow.GetCell(colNum).CellStyle = cHeadStyle; + //设置列宽 + //sheet.SetColumnWidth(column.Ordinal, (arrColWidth[column.Ordinal] + 1) * 256); + + int colWidth = (arrColWidth[colNum] + 1) * 256; + if (colWidth < 255 * 256) + { + sheet.SetColumnWidth(colNum, colWidth < 3000 ? 3000 : colWidth); + } + else + { + sheet.SetColumnWidth(colNum, 6000); + } + colNum++; + } + #endregion + } + #endregion + + ICellStyle dateStyle = workbook.CreateCellStyle(); + IDataFormat format = workbook.CreateDataFormat(); + dateStyle.DataFormat = format.GetFormat("yyyy-mm-dd"); + Dictionary dateStyleMap = new Dictionary(); + dateStyleMap.Add("yyyy-mm-dd", dateStyle); + + + + + foreach (DataRow row in dtSource.Rows) + { + #region 新建表,填充表头,填充列头,样式 + if (rowIndex == 65535) + { + sheet = workbook.CreateSheet(); + rowIndex = 0; + #region 表头及样式 + { + if (excelConfig.Title != null) + { + IRow headerRow = sheet.CreateRow(rowIndex); + rowIndex++; + if (excelConfig.TitleHeight != 0) + { + headerRow.Height = (short)(excelConfig.TitleHeight * 20); + } + headerRow.HeightInPoints = 25; + headerRow.CreateCell(0).SetCellValue(excelConfig.Title); + headerRow.GetCell(0).CellStyle = headStyle; + sheet.AddMergedRegion(new NPOI.SS.Util.CellRangeAddress(0, 0, 0, excelConfig.ColumnEntity.Count - 1)); // ------------------ + } + + } + #endregion + + #region 列头及样式 + { + IRow headerRow = sheet.CreateRow(rowIndex); + rowIndex++; + #region 如果设置了列标题就按列标题定义列头,没定义直接按字段名输出 + colNum = 0; + foreach (var column in excelConfig.ColumnEntity) + { + headerRow.CreateCell(colNum).SetCellValue(arrColName[colNum]); + headerRow.GetCell(colNum).CellStyle = cHeadStyle; + //设置列宽 + sheet.SetColumnWidth(colNum, (arrColWidth[colNum] + 1) * 256); + colNum++; + } + #endregion + } + #endregion + } + #endregion + + + #region 填充内容 + IRow dataRow = sheet.CreateRow(rowIndex); + colNum = 0; + foreach (var columnentity in excelConfig.ColumnEntity) + { + ICell newCell = dataRow.CreateCell(colNum); + newCell.CellStyle = arryColumStyle[colNum]; + string drValue = row[columnentity.Column].ToString(); + + //ColumnModel columnentity = excelConfig.ColumnEntity.Find(t => t.Column == column.ColumnName); + if (!string.IsNullOrEmpty(columnentity.DateFormat)) + { + if (!dateStyleMap.ContainsKey(columnentity.DateFormat)) + { + ICellStyle dateStyle2 = workbook.CreateCellStyle(); + IDataFormat format2 = workbook.CreateDataFormat(); + dateStyle2.DataFormat = format.GetFormat(columnentity.DateFormat); + dateStyleMap.Add(columnentity.DateFormat, dateStyle2); + } + + + SetCell(newCell, dateStyleMap[columnentity.DateFormat], DataColumnMap[columnentity.Column].DataType, drValue); + } + else + { + + SetCell(newCell, dateStyle, DataColumnMap[columnentity.Column].DataType, drValue); + } + + + + colNum++; + } + #endregion + rowIndex++; + } + MemoryStream ms = new MemoryStream(); + workbook.Write(ms); + ms.Flush(); + ms.Position = 0; + return ms; + } + #endregion + #region 设置表格内容 + private static void SetCell(ICell newCell, ICellStyle dateStyle, Type dataType, string drValue) + { + switch (dataType.ToString()) + { + case "System.String"://字符串类型 + newCell.SetCellValue(drValue); + break; + case "System.DateTime"://日期类型 + System.DateTime dateV; + if (System.DateTime.TryParse(drValue, out dateV)) + { + newCell.SetCellValue(dateV); + } + else + { + newCell.SetCellValue(""); + } + newCell.CellStyle = dateStyle;//格式化显示 + break; + case "System.Boolean"://布尔型 + bool boolV = false; + bool.TryParse(drValue, out boolV); + newCell.SetCellValue(boolV); + break; + case "System.Int16"://整型 + case "System.Int32": + case "System.Int64": + case "System.Byte": + int intV = 0; + int.TryParse(drValue, out intV); + newCell.SetCellValue(intV); + break; + case "System.Decimal"://浮点型 + case "System.Double": + double doubV = 0; + double.TryParse(drValue, out doubV); + newCell.SetCellValue(doubV); + break; + case "System.DBNull"://空值处理 + newCell.SetCellValue(""); + break; + default: + newCell.SetCellValue(""); + break; + } + } + #endregion + + #region 从Excel导入 + /// + /// 读取excel ,默认第一行为标头 + /// + /// excel文档路径 + /// + public static DataTable ExcelImport(string strFileName) + { + DataTable dt = new DataTable(); + + ISheet sheet = null; + using (FileStream file = new FileStream(strFileName, FileMode.Open, FileAccess.Read)) + { + if (strFileName.IndexOf(".xlsx") == -1)//2003 + { + HSSFWorkbook hssfworkbook = new HSSFWorkbook(file); + sheet = hssfworkbook.GetSheetAt(0); + } + else//2007 + { + XSSFWorkbook xssfworkbook = new XSSFWorkbook(file); + sheet = xssfworkbook.GetSheetAt(0); + } + } + + System.Collections.IEnumerator rows = sheet.GetRowEnumerator(); + + IRow headerRow = sheet.GetRow(0); + int cellCount = headerRow.LastCellNum; + + for (int j = 0; j < cellCount; j++) + { + ICell cell = headerRow.GetCell(j); + dt.Columns.Add(cell.ToString()); + } + + for (int i = (sheet.FirstRowNum + 1); i <= sheet.LastRowNum; i++) + { + IRow row = sheet.GetRow(i); + DataRow dataRow = dt.NewRow(); + + for (int j = row.FirstCellNum; j < cellCount; j++) + { + if (row.GetCell(j) != null) + dataRow[j] = row.GetCell(j).ToString(); + } + + dt.Rows.Add(dataRow); + } + return dt; + } + /// + /// 读取excel ,默认第一行为标头 + /// + /// 文件数据流 + /// 文件类型 + /// + public static DataTable ExcelImport(Stream fileStream, string flieType) + { + DataTable dt = new DataTable(); + ISheet sheet = null; + if (flieType == ".xls") + { + HSSFWorkbook hssfworkbook = new HSSFWorkbook(fileStream); + sheet = hssfworkbook.GetSheetAt(0); + } + else + { + XSSFWorkbook xssfworkbook = new XSSFWorkbook(fileStream); + sheet = xssfworkbook.GetSheetAt(0); + } + System.Collections.IEnumerator rows = sheet.GetRowEnumerator(); + IRow headerRow = sheet.GetRow(0); + int cellCount = headerRow.LastCellNum; + for (int j = 0; j < cellCount; j++) + { + ICell cell = headerRow.GetCell(j); + dt.Columns.Add(cell.ToString()); + } + for (int i = (sheet.FirstRowNum + 1); i <= sheet.LastRowNum; i++) + { + IRow row = sheet.GetRow(i); + DataRow dataRow = dt.NewRow(); + + for (int j = row.FirstCellNum; j < cellCount; j++) + { + if (row.GetCell(j) != null) + dataRow[j] = row.GetCell(j).ToString(); + } + + dt.Rows.Add(dataRow); + } + return dt; + } + #endregion + + #region RGB颜色转NPOI颜色 + private static short GetXLColour(HSSFWorkbook workbook, System.Drawing.Color SystemColour) + { + short s = 0; + HSSFPalette XlPalette = workbook.GetCustomPalette(); + NPOI.HSSF.Util.HSSFColor XlColour = XlPalette.FindColor(SystemColour.R, SystemColour.G, SystemColour.B); + if (XlColour == null) + { + if (NPOI.HSSF.Record.PaletteRecord.STANDARD_PALETTE_SIZE < 255) + { + XlColour = XlPalette.FindSimilarColor(SystemColour.R, SystemColour.G, SystemColour.B); + s = XlColour.Indexed; + } + + } + else + s = XlColour.Indexed; + return s; + } + #endregion + + #region 设置列的对齐方式 + /// + /// 设置对齐方式 + /// + /// + /// + private static HorizontalAlignment getAlignment(string style) + { + switch (style) + { + case "center": + return HorizontalAlignment.Center; + case "left": + return HorizontalAlignment.Left; + case "right": + return HorizontalAlignment.Right; + case "fill": + return HorizontalAlignment.Fill; + case "justify": + return HorizontalAlignment.Justify; + case "centerselection": + return HorizontalAlignment.CenterSelection; + case "distributed": + return HorizontalAlignment.Distributed; + } + return NPOI.SS.UserModel.HorizontalAlignment.General; + } + + #endregion + } +} diff --git a/Infrastructure/Helpers/Excel/Model/ColumnModel.cs b/Infrastructure/Helpers/Excel/Model/ColumnModel.cs new file mode 100644 index 0000000..c2d73e6 --- /dev/null +++ b/Infrastructure/Helpers/Excel/Model/ColumnModel.cs @@ -0,0 +1,55 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Infrastructure.Helpers.Excel.Model +{ + public class ColumnModel + { + /// + /// 列名 + /// + public string Column { get; set; } + /// + /// Excel列名 + /// + public string ExcelColumn { get; set; } + /// + /// 宽度 + /// + public int Width { get; set; } + /// + /// 前景色 + /// + public System.Drawing.Color ForeColor { get; set; } + /// + /// 背景色 + /// + public System.Drawing.Color Background { get; set; } + /// + /// 字体 + /// + public string Font { get; set; } + /// + /// 字号 + /// + public short Point { get; set; } + /// + ///对齐方式 + ///left 左 + ///center 中间 + ///right 右 + ///fill 填充 + ///justify 两端对齐 + ///centerselection 跨行居中 + ///distributed + /// + public string Alignment { get; set; } + /// + /// 时间格式化 + /// + public string DateFormat { get; set; } + } +} diff --git a/Infrastructure/Helpers/FileHelper.cs b/Infrastructure/Helpers/FileHelper.cs new file mode 100644 index 0000000..238526e --- /dev/null +++ b/Infrastructure/Helpers/FileHelper.cs @@ -0,0 +1,407 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Text.RegularExpressions; +using Infrastructure.Extensions; + +namespace Infrastructure.Helpers +{ + public class FileHelper + { + private static object _filePathObj = new object(); + + /// + /// 通过迭代器读取平面文件行内容(必须是带有\r\n换行的文件,百万行以上的内容读取效率存在问题,适用于100M左右文件,行100W内,超出的会有卡顿) + /// + /// 文件全路径 + /// 分页页数 + /// 分页大小 + /// 是否最后一行向前读取,默认从前向后读取 + /// + public static IEnumerable ReadPageLine(string fullPath, int page, int pageSize, bool seekEnd = false) + { + if (page <= 0) + { + page = 1; + } + fullPath = StringExtension.ReplacePath(fullPath); + var lines = File.ReadLines(fullPath, Encoding.UTF8); + if (seekEnd) + { + int lineCount = lines.Count(); + int linPageCount = (int)Math.Ceiling(lineCount / (pageSize * 1.00)); + //超过总页数,不处理 + if (page > linPageCount) + { + page = 0; + pageSize = 0; + } + else if (page == linPageCount)//最后一页,取最后一页剩下所有的行 + { + pageSize = lineCount - (page - 1) * pageSize; + if (page == 1) + { + page = 0; + } + else + { + page = lines.Count() - page * pageSize; + } + } + else + { + page = lines.Count() - page * pageSize; + } + } + else + { + page = (page - 1) * pageSize; + } + lines = lines.Skip(page).Take(pageSize); + + var enumerator = lines.GetEnumerator(); + int count = 1; + while (enumerator.MoveNext() || count <= pageSize) + { + yield return enumerator.Current; + count++; + } + enumerator.Dispose(); + } + public static bool FileExists(string path) + { + return File.Exists(StringExtension.ReplacePath(path)); + } + + public static string GetCurrentDownLoadPath() + { + return ("Download\\").MapPath(); + } + + public static bool DirectoryExists(string path) + { + return Directory.Exists(StringExtension.ReplacePath(path)); + } + + + public static string Read_File(string fullpath, string filename, string suffix) + { + return ReadFile((fullpath + "\\" + filename + suffix).MapPath()); + } + public static string ReadFile(string fullName) + { + // Encoding code = Encoding.GetEncoding(); //Encoding.GetEncoding("gb2312"); + string temp = fullName.MapPath().ReplacePath(); + string str = ""; + if (!File.Exists(temp)) + { + return str; + } + StreamReader sr = null; + try + { + sr = new StreamReader(temp); + str = sr.ReadToEnd(); // 读取文件 + } + catch { } + sr?.Close(); + sr?.Dispose(); + return str; + } + + + + /// + /// 取后缀名 + /// + /// 文件名 + /// .gif|.html格式 + public static string GetPostfixStr(string filename) + { + int start = filename.LastIndexOf("."); + int length = filename.Length; + string postfix = filename.Substring(start, length - start); + return postfix; + } + + + /// + /// + /// + /// 路径 + /// 文件名 + /// 写入的内容 + /// 是否将内容添加到未尾,默认不添加 + public static void WriteFile(string path, string fileName, string content, bool appendToLast = false) + { + path = StringExtension.ReplacePath(path); + fileName = StringExtension.ReplacePath(fileName); + if (!Directory.Exists(path))//如果不存在就创建file文件夹 + Directory.CreateDirectory(path); + + using (FileStream stream = File.Open(Path.Combine(path, fileName), FileMode.OpenOrCreate, FileAccess.Write)) + { + byte[] by = Encoding.Default.GetBytes(content); + if (appendToLast) + { + stream.Position = stream.Length; + } + else + { + stream.SetLength(0); + } + stream.Write(by, 0, by.Length); + } + } + + + + /// + /// 追加文件 + /// + /// 文件路径 + /// 内容 + public static void FileAdd(string Path, string strings) + { + StreamWriter sw = File.AppendText(StringExtension.ReplacePath(Path)); + sw.Write(strings); + sw.Flush(); + sw.Close(); + sw.Dispose(); + } + + + /// + /// 拷贝文件 + /// + /// 原始文件 + /// 新文件路径 + public static void FileCoppy(string OrignFile, string NewFile) + { + File.Copy(StringExtension.ReplacePath(OrignFile), StringExtension.ReplacePath(NewFile), true); + } + + + /// + /// 删除文件 + /// + /// 路径 + public static void FileDel(string Path) + { + File.Delete(StringExtension.ReplacePath(Path)); + } + + /// + /// 移动文件 + /// + /// 原始路径 + /// 新路径 + public static void FileMove(string OrignFile, string NewFile) + { + File.Move(StringExtension.ReplacePath(OrignFile), StringExtension.ReplacePath(NewFile)); + } + + /// + /// 在当前目录下创建目录 + /// + /// 当前目录 + /// 新目录 + public static void FolderCreate(string OrignFolder, string NewFloder) + { + Directory.SetCurrentDirectory(StringExtension.ReplacePath(OrignFolder)); + Directory.CreateDirectory(StringExtension.ReplacePath(NewFloder)); + } + + /// + /// 创建文件夹 + /// + /// + public static void FolderCreate(string Path) + { + // 判断目标目录是否存在如果不存在则新建之 + if (!Directory.Exists(StringExtension.ReplacePath(Path))) + Directory.CreateDirectory(StringExtension.ReplacePath(Path)); + } + + + public static void FileCreate(string Path) + { + FileInfo CreateFile = new FileInfo(StringExtension.ReplacePath(Path)); //创建文件 + if (!CreateFile.Exists) + { + FileStream FS = CreateFile.Create(); + FS.Close(); + } + } + /// + /// 递归删除文件夹目录及文件 + /// + /// + /// + public static void DeleteFolder(string dir) + { + dir = StringExtension.ReplacePath(dir); + if (Directory.Exists(dir)) //如果存在这个文件夹删除之 + { + foreach (string d in Directory.GetFileSystemEntries(dir)) + { + if (File.Exists(d)) + File.Delete(d); //直接删除其中的文件 + else + DeleteFolder(d); //递归删除子文件夹 + } + Directory.Delete(dir, true); //删除已空文件夹 + } + } + + + /// + /// 指定文件夹下面的所有内容copy到目标文件夹下面 + /// + /// 原始路径 + /// 目标文件夹 + public static void CopyDir(string srcPath, string aimPath) + { + try + { + aimPath = StringExtension.ReplacePath(aimPath); + // 检查目标目录是否以目录分割字符结束如果不是则添加之 + if (aimPath[aimPath.Length - 1] != Path.DirectorySeparatorChar) + aimPath += Path.DirectorySeparatorChar; + // 判断目标目录是否存在如果不存在则新建之 + if (!Directory.Exists(aimPath)) + Directory.CreateDirectory(aimPath); + // 得到源目录的文件列表,该里面是包含文件以及目录路径的一个数组 + //如果你指向copy目标文件下面的文件而不包含目录请使用下面的方法 + //string[] fileList = Directory.GetFiles(srcPath); + string[] fileList = Directory.GetFileSystemEntries(StringExtension.ReplacePath(srcPath)); + //遍历所有的文件和目录 + foreach (string file in fileList) + { + //先当作目录处理如果存在这个目录就递归Copy该目录下面的文件 + + if (Directory.Exists(file)) + CopyDir(file, aimPath + Path.GetFileName(file)); + //否则直接Copy文件 + else + File.Copy(file, aimPath + Path.GetFileName(file), true); + } + } + catch (Exception ee) + { + throw new Exception(ee.ToString()); + } + } + + /// + /// 获取文件夹大小 + /// + /// 文件夹路径 + /// + public static long GetDirectoryLength(string dirPath) + { + dirPath = StringExtension.ReplacePath(dirPath); + if (!Directory.Exists(dirPath)) + return 0; + long len = 0; + DirectoryInfo di = new DirectoryInfo(dirPath); + foreach (FileInfo fi in di.GetFiles()) + { + len += fi.Length; + } + DirectoryInfo[] dis = di.GetDirectories(); + if (dis.Length > 0) + { + for (int i = 0; i < dis.Length; i++) + { + len += GetDirectoryLength(dis[i].FullName); + } + } + return len; + } + + /// + /// 获取指定文件详细属性 + /// + /// 文件详细路径 + /// + public static string GetFileAttibe(string filePath) + { + string str = ""; + filePath = StringExtension.ReplacePath(filePath); + System.IO.FileInfo objFI = new System.IO.FileInfo(filePath); + str += "详细路径:" + objFI.FullName + "
文件名称:" + objFI.Name + "
文件长度:" + objFI.Length.ToString() + "字节
创建时间" + objFI.CreationTime.ToString() + "
最后访问时间:" + objFI.LastAccessTime.ToString() + "
修改时间:" + objFI.LastWriteTime.ToString() + "
所在目录:" + objFI.DirectoryName + "
扩展名:" + objFI.Extension; + return str; + } + + + /// + /// 括号匹配算法 + /// + /// 原始字符串 + /// 左匹配符号 + /// 右匹配符号 + /// + private static string parenthesisMatch(string dataStr, char leftCode, char rightCode) + { + Stack stack = new Stack(); + + string cut_text = ""; + + for (int i = 0; i < dataStr.Length; ++i) + { + char ch = dataStr[i]; + if (stack.Count > 0) + { + cut_text += ch; + } + if (ch == leftCode) + { + stack.Push(ch); + } + if (ch == rightCode) + { + stack.Pop(); + if (0 == stack.Count) + { + return cut_text.Substring(0, cut_text.Length - 1); + } + } + } + return ""; + } + + /// + /// 替换内容(正则 + 括号匹配算法) + /// + /// 文件路径 + /// 追加内容 + public static void RegxAddContentByParenthesis(string path, string addStr) + { + path = StringExtension.ReplacePath(path); + FileStream fs = new FileStream(path, FileMode.Open, FileAccess.Read); + StreamReader sr = new StreamReader(fs); + string originStr = sr.ReadToEnd(); + + string pattern = @"DbContext\s*?({.*)"; + if (Regex.IsMatch(originStr, pattern)) + { + Match match = Regex.Match(originStr, pattern, RegexOptions.Singleline); + string cut_str = parenthesisMatch(match.Groups[1].Value, '{', '}'); // 剪切内容(原内容) + string new_str = cut_str + "\r\n " + addStr + "\r\n"; // 实际更新内容 + originStr = originStr.Replace(cut_str, new_str); + } + + sr.Close(); + fs.Close(); + FileStream fs2 = new FileStream(path, FileMode.Open, FileAccess.Write); + StreamWriter sw = new StreamWriter(fs2); + sw.WriteLine(originStr); + sw.Close(); + fs2.Close(); + } + } +} diff --git a/Infrastructure/Helpers/GenericHelper.cs b/Infrastructure/Helpers/GenericHelper.cs new file mode 100644 index 0000000..5c2fa64 --- /dev/null +++ b/Infrastructure/Helpers/GenericHelper.cs @@ -0,0 +1,76 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +namespace Infrastructure.Helpers +{ + /// + /// List转成Tree + /// 李玉宝新增于2016-10-09 19:54:07 + /// + public static class GenericHelpers + { + /// + /// Generates tree of items from item list + /// + /// + /// Type of item in collection + /// Type of parent_id + /// + /// Collection of items + /// Function extracting item's id + /// Function extracting item's parent_id + /// Root element id + /// + /// Tree of items + public static IEnumerable> GenerateTree( + this IEnumerable collection, + Func idSelector, + Func parentIdSelector, + K rootId = default(K)) + { + foreach (var c in collection.Where(u => + { + var selector = parentIdSelector(u); + return (rootId == null && selector == null) + || (rootId != null && rootId.Equals(selector)); + })) + { + yield return new TreeItem + { + Item = c, + Children = collection.GenerateTree(idSelector, parentIdSelector, idSelector(c)) + }; + } + } + + /// + /// 把数组转为逗号连接的字符串 + /// + /// + /// + /// + public static string ArrayToString(dynamic data, string Str) + { + string resStr = Str; + foreach (var item in data) + { + if (resStr != "") + { + resStr += ","; + } + + if (item is string) + { + resStr += item; + } + else + { + resStr += item.Value; + + } + } + return resStr; + } + } +} \ No newline at end of file diff --git a/Infrastructure/Helpers/HIKOpenAPI.cs b/Infrastructure/Helpers/HIKOpenAPI.cs new file mode 100644 index 0000000..48539ee --- /dev/null +++ b/Infrastructure/Helpers/HIKOpenAPI.cs @@ -0,0 +1,478 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net.Security; +using System.Net; +using System.Security.Cryptography.X509Certificates; +using System.Security.Cryptography; +using System.Text; +using System.Threading.Tasks; +using System.Web; + +namespace Infrastructure.Helpers +{ + public class HIKOpenAPI + { + + /// + /// + /// + /// + /// + /// + /// + /// + /// + public static string HttpPost(string url, string api, string body, string ak, string sk) + { + + var headers = new Dictionary { { "Accept", "*/*" }, { "Content-Type", "application/json" } }; + + var request = new Request(Method.POST_STRING, url, api, ak, sk, 10000) + { + Headers = headers, + SignHeaderPrefixList = null, + Querys = null, + StringBody = body + }; + + var result = DoPoststring(request.Host, request.Path, request.Timeout, request.Headers, request.Querys, request.StringBody, request.SignHeaderPrefixList, request.AppKey, request.AppSecret, false); + return result; + } + + //Post请求方法 + public static string DoPoststring(string host, string path, int connectTimeout, Dictionary headers, + Dictionary querys, string body, List signHeaderPrefixList, string appKey, + string appSecret, bool autoDown) + { + try + { + headers = initialBasicHeader("POST", path, headers, querys, null, signHeaderPrefixList, appKey, + appSecret); + ServicePointManager.ServerCertificateValidationCallback = + RemoteCertificateValidate; + //验证服务器证书回调自动验证 + //ServicePointManager.SecurityProtocol = SecurityProtocolType.Ssl3 | SecurityProtocolType.Tls12 | + // SecurityProtocolType.Tls11 | SecurityProtocolType.Tls; + + HttpWebRequest request = (HttpWebRequest)WebRequest.Create(initUrl(host, path, querys)); + request.KeepAlive = false; + request.ProtocolVersion = HttpVersion.Version10; + //设置HTTP协议的并发连接数 + //ServicePointManager.DefaultConnectionLimit = 512; + //关闭重定向 + request.AllowAutoRedirect = true; + request.Method = "POST"; + request.Timeout = connectTimeout; + string accept = headers["Accept"]; + request.Accept = accept; + string contentType = headers["Content-Type"]; + request.ContentType = contentType; + + foreach (string headerKey in headers.Keys) + { + if (headerKey.Contains("x-ca-")) + { + request.Headers.Add(headerKey + ":" + + (string.IsNullOrWhiteSpace(headers[headerKey]) ? "" : headers[headerKey])); + } + if (headerKey.Equals("tagId")) + { + request.Headers.Add(headerKey + ":" + + (string.IsNullOrWhiteSpace(headers[headerKey]) ? "" : headers[headerKey])); + } + } + + + + if (!string.IsNullOrWhiteSpace(body)) + { + byte[] postBytes = Encoding.UTF8.GetBytes(body); + request.ContentLength = postBytes.Length; + Stream requestStream = request.GetRequestStream(); + + requestStream.Write(postBytes, 0, postBytes.Length); + requestStream.Close(); + } + + + var response = (HttpWebResponse)request.GetResponse(); + var result = ""; + if (response.StatusCode != HttpStatusCode.OK) return result; + using (StreamReader rdr = new StreamReader(response.GetResponseStream())) + { + result = rdr.ReadToEnd(); + } + + return result; + } + catch (Exception ex) + { + return Newtonsoft.Json.JsonConvert.SerializeObject(new { code = "404", msg = ex.Message }); + } + } + + + //获取当时时间戳 + public static long GetTimestamp(DateTime time) + { + System.DateTime startTime = TimeZone.CurrentTimeZone.ToLocalTime(new System.DateTime(1970, 1, 1, 0, 0, 0, 0)); + long t = (time.Ticks - startTime.Ticks) / 10000; //除10000调整为13位 + return t; + + } + + private static bool RemoteCertificateValidate(object sender, X509Certificate cert, X509Chain chain, + SslPolicyErrors error) + { + //为了通过证书验证,总是返回true + return true; + } + + public static Dictionary initialBasicHeader(string method, string path, + Dictionary headers, Dictionary querys, Dictionary bodys, + List signHeaderPrefixList, string appKey, string appSecret) //throws MalformedURLException + { + if (headers == null) + { + headers = new Dictionary(); + } + headers["x-ca-timestamp"] = GetTimestamp(DateTime.Now).ToString(); + headers["x-ca-nonce"] = System.Guid.NewGuid().ToString(); + headers["x-ca-key"] = appKey; + headers["x-ca-signature"] = sign(appSecret, method, path, headers, querys, bodys, signHeaderPrefixList); + + return headers; + //https://223.99.16.253:6114/pic? + //5d00=a202l94-do7b18*35d69188-64fbf0ebc**561===sp**715=7t7662209067=1l3*1513o6*720-=46fe44pi1e9o=0-9e00b0 + //&AccessKeyId=Qrq2LsyWF6GSM0zm&Expires=1677046120&Signature=Bw7dKuV6CX7w3ev56u6jBwRuGwU= + } + + public static string initUrl(string host, string path, Dictionary querys) + //throws UnsupportedEncodingException + { + StringBuilder sbUrl = new StringBuilder(); + sbUrl.Append(host); + if (!string.IsNullOrWhiteSpace(path)) + { + sbUrl.Append(path); + } + + if (null != querys) + { + StringBuilder sbQuery = new StringBuilder(); + + foreach (string queryKey in querys.Keys) + { + if (0 < sbQuery.Length) + { + sbQuery.Append("&"); + } + + if (string.IsNullOrWhiteSpace(queryKey) && !string.IsNullOrWhiteSpace(querys[queryKey])) + { + sbQuery.Append(querys[queryKey]); + } + + if (!string.IsNullOrWhiteSpace(queryKey)) + { + sbQuery.Append(queryKey); + if (!string.IsNullOrWhiteSpace(querys[queryKey])) + { + sbQuery.Append("=").Append(HttpUtility.UrlEncode(querys[queryKey], Encoding.UTF8)); + } + } + } + + if (0 < sbQuery.Length) + { + sbUrl.Append("?").Append(sbQuery); + } + } + return sbUrl.ToString(); + } + + public static string sign(string secret, string method, string path, Dictionary headers, + Dictionary querys, Dictionary bodys, List signHeaderPrefixList) + { + try + { + //return HmacSHA256(buildstringToSign(method, path, headers, querys, bodys, signHeaderPrefixList), secret); + + /*---message里的内容---*/ + //POST + //*/* + //application/json + //x-ca-key:23125513 + //x-ca-nonce:12d28d90-a7c3-45cc-ae6a-0cc8d5e22118 + //x-ca-timestamp:1544495633599 + //artemis/api/resource/v1/org/advance/orgList + string message = buildstringToSign(method, path, headers, querys, bodys, signHeaderPrefixList); + + + + return HmacSHA256(message, secret); + } + catch (Exception ex) + { + return ex.Message; + } + } + + public static string buildstringToSign(string method, string path, Dictionary headers, + Dictionary querys, Dictionary bodys, List signHeaderPrefixList) + { + StringBuilder sb = new StringBuilder(); + sb.Append(method.ToUpper()).Append("\n"); + if (null != headers) + { + if (null != headers["Accept"]) + { + sb.Append((string)headers["Accept"]); + sb.Append("\n"); + } + + if (headers.Keys.Contains("Content-MD5") && null != headers["Content-MD5"]) + { + sb.Append((string)headers["Content-MD5"]); + sb.Append("\n"); + } + + if (null != headers["Content-Type"]) + { + sb.Append((string)headers["Content-Type"]); + sb.Append("\n"); + } + + if (headers.Keys.Contains("Date") && null != headers["Date"]) + { + sb.Append((string)headers["Date"]); + sb.Append("\n"); + } + } + + sb.Append(buildHeaders(headers, signHeaderPrefixList)); + sb.Append(buildResource(path, querys, bodys)); + return sb.ToString(); + } + + public static string buildHeaders(Dictionary headers, List signHeaderPrefixList) + { + StringBuilder sb = new StringBuilder(); + if (null != signHeaderPrefixList) + { + signHeaderPrefixList.Remove("x-ca-signature"); + signHeaderPrefixList.Remove("Accept"); + signHeaderPrefixList.Remove("Content-MD5"); + signHeaderPrefixList.Remove("Content-Type"); + signHeaderPrefixList.Remove("Date"); + signHeaderPrefixList.Sort(); + } + + if (null != headers) + { + Dictionary sortDictionary = new Dictionary(); + sortDictionary = headers; + //按key值升序排序 + var dicSort = from objDic in sortDictionary orderby objDic.Key ascending select objDic; + + StringBuilder signHeadersStringBuilder = new StringBuilder(); + + foreach (KeyValuePair kvp in dicSort) + { + if (kvp.Key.Replace(" ", "").Contains("x-ca-")) + { + sb.Append(kvp.Key + ":"); + if (!string.IsNullOrWhiteSpace(kvp.Value)) + { + sb.Append(kvp.Value); + } + sb.Append("\n"); + if (signHeadersStringBuilder.Length > 0) + { + signHeadersStringBuilder.Append(","); + } + signHeadersStringBuilder.Append(kvp.Key); + } + } + + headers.Add("x-ca-signature-headers", signHeadersStringBuilder.ToString()); + } + + //x-ca-key:23125513 + //x-ca-nonce:12d28d90-a7c3-45cc-ae6a-0cc8d5e22118 + //x-ca-timestamp:1544495633599 + + return sb.ToString(); + } + + public static string buildResource(string path, Dictionary querys, Dictionary bodys) + { + StringBuilder sb = new StringBuilder(); + if (!string.IsNullOrWhiteSpace(path)) + { + sb.Append(path); + } + + Dictionary sortDictionary = new Dictionary(); + if (querys != null) + { + //按key值升序排序 + var dicSort = from objDic in querys orderby objDic.Key ascending select objDic; + foreach (KeyValuePair kvp in dicSort) + { + if (!string.IsNullOrWhiteSpace(kvp.Key)) + { + sortDictionary[kvp.Key] = kvp.Value; + } + } + } + + if (bodys != null) + { + //按key值升序排序 + var dicSort = from objDic in bodys orderby objDic.Key ascending select objDic; + foreach (KeyValuePair kvp in dicSort) + { + if (!string.IsNullOrWhiteSpace(kvp.Key)) + { + sortDictionary[kvp.Key] = kvp.Value; + } + } + } + + StringBuilder sbParam = new StringBuilder(); + + //按key值升序排序 + var dicSortDictionary = from objDic in sortDictionary orderby objDic.Key ascending select objDic; + foreach (KeyValuePair kvp in dicSortDictionary) + { + if (!string.IsNullOrWhiteSpace(kvp.Key)) + { + if (sbParam.Length > 0) + { + sbParam.Append("&"); + } + sbParam.Append(kvp.Key); + if (!string.IsNullOrWhiteSpace(kvp.Value)) + { + sbParam.Append("=").Append(kvp.Value); + } + } + } + + if (0 < sbParam.Length) + { + sb.Append("?"); + sb.Append(sbParam); + } + //artemis/api/resource/v1/org/advance/orgList + return sb.ToString(); + } + + public static string HmacSHA256(string message, string secret) + { + secret = secret ?? ""; + var encoding = new System.Text.UTF8Encoding(); + byte[] keyByte = encoding.GetBytes(secret); + byte[] messageBytes = encoding.GetBytes(message); + using (var hmacsha256 = new HMACSHA256(keyByte)) + { + byte[] hashmessage = hmacsha256.ComputeHash(messageBytes); + return Convert.ToBase64String(hashmessage); + } + } + + public static Dictionary ConvertMapTimeStyle(Dictionary paramMap) + { + Dictionary timeMap = new Dictionary(); + try + { + foreach (string key in paramMap.Keys) + { + + object mapValueObj = paramMap[key]; + Dictionary dic = mapValueObj as Dictionary; + if (dic != null) + { + ConvertMapTimeStyle(dic); + } + //时间格式参数转为ISO8601 + DateTime mapDate; + if (DateTime.TryParse(paramMap[key].ToString(), out mapDate)) + { + string ISO8601time = mapDate.ToString("yyyy-MM-ddTHH:mm:ss.fffzzz"); + timeMap.Add(key, ISO8601time); + } + } + if (timeMap.Count > 0) + { + foreach (string key in timeMap.Keys) + { + paramMap[key] = timeMap[key]; + } + } + return paramMap; + } + catch (Exception ex) + { + return paramMap; + } + } + + + + public enum Method + { + GET, + POST_FORM, + POST_STRING, + POST_BYTES, + PUT_FORM, + PUT_STRING, + PUT_BYTES, + DELETE + } + + public class Request + { + public Request() + { + } + + public Request(Method method, string host, string path, string appKey, string appSecret, int timeout) + { + this.Method = method; + this.Host = host; + this.Path = path; + this.AppKey = appKey; + this.AppSecret = appSecret; + this.Timeout = timeout; + } + + public Method Method { get; set; } + + public string Host { get; set; } + + public string Path { get; set; } + + public string AppKey { get; set; } + + public string AppSecret { get; set; } + + public int Timeout { get; set; } + + public Dictionary Headers { get; set; } + + public Dictionary Querys { get; set; } + + public Dictionary Bodys { get; set; } + + public string StringBody { get; set; } + + public byte[] BytesBody { get; set; } + + public List SignHeaderPrefixList { get; set; } + } + } +} diff --git a/Infrastructure/Helpers/HttpHelper.cs b/Infrastructure/Helpers/HttpHelper.cs new file mode 100644 index 0000000..e80ac54 --- /dev/null +++ b/Infrastructure/Helpers/HttpHelper.cs @@ -0,0 +1,175 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net.Http; +using System.Net.Http.Headers; +using System.Text; + +namespace Infrastructure.Helpers +{ + /// + /// http请求类 + /// + public class HttpHelper + { + private HttpClient _httpClient; + private string _baseIPAddress; + + /// 请求的基础IP,例如:http://192.168.0.33:8080/ + public HttpHelper(string ipaddress = "") + { + this._baseIPAddress = ipaddress; + _httpClient = new HttpClient { BaseAddress = new Uri(_baseIPAddress) }; + } + + /// + /// 创建带用户信息的请求客户端 + /// + /// 用户账号 + /// 用户密码,当WebApi端不要求密码验证时,可传空串 + /// The URI string. + public HttpHelper(string userName, string pwd = "", string uriString = "") + : this(uriString) + { + if (!string.IsNullOrEmpty(userName)) + { + _httpClient.DefaultRequestHeaders.Authorization = CreateBasicCredentials(userName, pwd); + } + } + + /// + /// Get请求数据 + /// /// 最终以url参数的方式提交 + /// yubaolee 2016-3-3 重构与post同样异步调用 + /// + /// 参数字典,可为空 + /// 例如/api/Files/UploadFile + /// + public string Get(Dictionary parameters, string requestUri) + { + if (parameters != null) + { + var strParam = string.Join("&", parameters.Select(o => o.Key + "=" + o.Value)); + requestUri = string.Concat(ConcatURL(requestUri), '?', strParam); + } + else + { + requestUri = ConcatURL(requestUri); + } + + var result = _httpClient.GetStringAsync(requestUri); + return result.Result; + } + + /// + /// Get请求数据 + /// 最终以url参数的方式提交 + /// + /// 参数字典 + /// 例如/api/Files/UploadFile + /// 实体对象 + public T Get(Dictionary parameters, string requestUri) where T : class + { + string jsonString = Get(parameters, requestUri); + if (string.IsNullOrEmpty(jsonString)) + return null; + + return JsonHelper.Instance.Deserialize(jsonString); + } + + /// + /// 以json的方式Post数据 返回string类型 + /// 最终以json的方式放置在http体中 + /// + /// 实体 + /// 例如/api/Files/UploadFile + /// + public string Post(object entity, string requestUri) + { + string request = string.Empty; + if (entity != null) + request = JsonHelper.Instance.Serialize(entity); + HttpContent httpContent = new StringContent(request); + httpContent.Headers.ContentType = new MediaTypeHeaderValue("application/json"); + return Post(requestUri, httpContent); + } + + /// + /// 提交字典类型的数据 + /// 最终以formurlencode的方式放置在http体中 + /// 李玉宝于2016-07-20 19:01:59 + /// + /// System.String. + public string PostDicObj(Dictionary para, string requestUri) + { + Dictionary temp = new Dictionary(); + foreach (var item in para) + { + if (item.Value != null) + { + if (item.Value.GetType().Name.ToLower() != "string") + { + temp.Add(item.Key, JsonHelper.Instance.Serialize(item.Value)); + } + else + { + temp.Add(item.Key, item.Value.ToString()); + } + } + else { + temp.Add(item.Key, ""); + } + } + + return PostDic(temp, requestUri); + } + + /// + /// Post Dic数据 + /// 最终以formurlencode的方式放置在http体中 + /// 李玉宝于2016-07-15 15:28:41 + /// + /// System.String. + public string PostDic(Dictionary temp, string requestUri) + { + HttpContent httpContent = new FormUrlEncodedContent(temp); + httpContent.Headers.ContentType = new MediaTypeHeaderValue("application/x-www-form-urlencoded"); + return Post(requestUri, httpContent); + } + + public string PostByte(byte[] bytes, string requestUrl) + { + HttpContent content = new ByteArrayContent(bytes); + content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream"); + return Post(requestUrl, content); + } + + private string Post(string requestUrl, HttpContent content) + { + var result = _httpClient.PostAsync(ConcatURL(requestUrl), content); + return result.Result.Content.ReadAsStringAsync().Result; + } + + /// + /// 把请求的URL相对路径组合成绝对路径 + /// 李玉宝于2016-07-21 9:54:07 + /// + private string ConcatURL(string requestUrl) + { + return new Uri(_httpClient.BaseAddress, requestUrl).OriginalString; + } + + private AuthenticationHeaderValue CreateBasicCredentials(string userName, string password) + { + string toEncode = userName + ":" + password; + // The current HTTP specification says characters here are ISO-8859-1. + // However, the draft specification for the next version of HTTP indicates this encoding is infrequently + // used in practice and defines behavior only for ASCII. + Encoding encoding = Encoding.GetEncoding("utf-8"); + byte[] toBase64 = encoding.GetBytes(toEncode); + string parameter = Convert.ToBase64String(toBase64); + + return new AuthenticationHeaderValue("Basic", parameter); + } + } +} \ No newline at end of file diff --git a/Infrastructure/Helpers/HttpMethods.cs b/Infrastructure/Helpers/HttpMethods.cs new file mode 100644 index 0000000..86d19f0 --- /dev/null +++ b/Infrastructure/Helpers/HttpMethods.cs @@ -0,0 +1,103 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net.Security; +using System.Net; +using System.Security.Cryptography.X509Certificates; +using System.Text; +using System.Threading.Tasks; +using System.Net.Http.Headers; + +namespace Infrastructure.Helpers +{ + public class HttpMethods + { + // + // 摘要: + // 创建HttpClient + public static HttpClient CreateHttpClient(string url, IDictionary cookies = null) + { + HttpClientHandler httpClientHandler = new HttpClientHandler(); + Uri uri = new Uri(url); + if (cookies != null) + { + foreach (string key in cookies.Keys) + { + string cookieHeader = key + "=" + cookies[key]; + httpClientHandler.CookieContainer.SetCookies(uri, cookieHeader); + } + } + + if (url.StartsWith("https", StringComparison.OrdinalIgnoreCase)) + { + ServicePointManager.ServerCertificateValidationCallback = (RemoteCertificateValidationCallback)Delegate.Combine(ServicePointManager.ServerCertificateValidationCallback, (RemoteCertificateValidationCallback)((object sender, X509Certificate cert, X509Chain chain, SslPolicyErrors sslPolicyErrors) => true)); + return new HttpClient(httpClientHandler); + } + + return new HttpClient(httpClientHandler); + } + + // + // 摘要: + // post 请求 + // + // 参数: + // url: + // 请求地址 + // + // jsonData: + // 请求参数 + public static Task Post(string url, string jsonData) + { + HttpClient httpClient = CreateHttpClient(url); + StringContent stringContent = new StringContent(jsonData); + stringContent.Headers.ContentType = MediaTypeHeaderValue.Parse("application/json"); + return httpClient.PostAsync(url, stringContent).Result.Content.ReadAsStringAsync(); + } + + // + // 摘要: + // post 请求 + // + // 参数: + // url: + // 请求地址 + public static Task Post(string url) + { + HttpClient httpClient = CreateHttpClient(url); + StringContent stringContent = new StringContent(""); + stringContent.Headers.ContentType = MediaTypeHeaderValue.Parse("application/json"); + return httpClient.PostAsync(url, stringContent).Result.Content.ReadAsStringAsync(); + } + + // + // 摘要: + // post 请求 + // + // 参数: + // url: + // 请求地址 + // + // req: + // 请求参数 + public static Task Post(string url, byte[] req) + { + HttpClient httpClient = CreateHttpClient(url); + ByteArrayContent content = new ByteArrayContent(req); + return httpClient.PostAsync(url, content).Result.Content.ReadAsStringAsync(); + } + + // + // 摘要: + // get 请求 + // + // 参数: + // url: + // 请求地址 + public static Task Get(string url) + { + HttpClient httpClient = CreateHttpClient(url); + return httpClient.GetAsync(url).Result.Content.ReadAsStringAsync(); + } + } +} diff --git a/Infrastructure/Helpers/ImageHelper.cs b/Infrastructure/Helpers/ImageHelper.cs new file mode 100644 index 0000000..eefe604 --- /dev/null +++ b/Infrastructure/Helpers/ImageHelper.cs @@ -0,0 +1,34 @@ +using System.Drawing.Imaging; +using Image = System.Drawing.Image; + +namespace Hopetry.App.Common; + +public class ImageHelper +{ + /// + /// 图片转换成字节流 + /// + /// 要转换的Image对象 + /// 转换后返回的字节流 + public static Stream ImgToStream(Image img) + { + byte[] imgByte = ImgToByt(img); + //img.Save(ms, img.RawFormat);//System.Drawing.Imaging.ImageFormat.Jpeg + Stream stream = new MemoryStream(imgByte); + return stream; + } + + /// + /// 图片转换成字节流 + /// + /// 要转换的Image对象 + /// 转换后返回的字节流 + public static byte[] ImgToByt(Image img) + { + MemoryStream ms = new MemoryStream(); + byte[] imagedata = null; + img.Save(ms, ImageFormat.Jpeg); + imagedata = ms.GetBuffer(); + return imagedata; + } +} \ No newline at end of file diff --git a/Infrastructure/Helpers/ImgHelper.cs b/Infrastructure/Helpers/ImgHelper.cs new file mode 100644 index 0000000..6503542 --- /dev/null +++ b/Infrastructure/Helpers/ImgHelper.cs @@ -0,0 +1,50 @@ +// +// Copyright (c) 2019 openauth.net.cn. All rights reserved. +// +// www.cnblogs.com/yubaolee +// 生成缩略图 + +using SixLabors.ImageSharp; +using SixLabors.ImageSharp.Processing; + +namespace Infrastructure.Helpers +{ + public class ImgHelper + { + /// + /// 根据已有图片生成缩略图 + /// 用法:MakeThumbnail(path, tpath, 120, 90, "H"); + /// + /// 源图片路径 + /// 缩略图保存路径 + /// 缩略图的宽度 + /// 缩略图高度 + /// 缩略模式:H:指定高度,宽度按比例处理;W:指定宽度,高度按比例处理;HW按参数指定的高度和宽度 + public static void MakeThumbnail(string originalImagePath, + string thumbnailPath, + int width = 120, int height = 90, string mode = "H") + { + using (var originalImage = Image.Load(originalImagePath)) + { + int towidth = width; //缩略图宽度 + int toheight = height; //缩略图高度 + switch (mode) + { + case "HW": //指定高宽缩放(可能变形) + break; + case "W": //指定宽,高按比例 + toheight = originalImage.Height * width / originalImage.Width; + break; + case "H": //指定高,宽按比例 + towidth = originalImage.Width * height / originalImage.Height; + break; + default: + break; + } + + originalImage.Mutate(x => x.Resize(towidth, toheight)); + originalImage.Save(thumbnailPath); + } + } + } +} \ No newline at end of file diff --git a/Infrastructure/Helpers/Md5.cs b/Infrastructure/Helpers/Md5.cs new file mode 100644 index 0000000..05ae4df --- /dev/null +++ b/Infrastructure/Helpers/Md5.cs @@ -0,0 +1,31 @@ +using System; +using System.Security.Cryptography; +using System.Text; + +namespace Infrastructure.Helpers +{ + public class Md5 + { + public static string Encrypt(string str) + { + + string pwd = String.Empty; + + MD5 md5 = MD5.Create(); + + // 编码UTF8/Unicode  + byte[] s = md5.ComputeHash(Encoding.UTF8.GetBytes(str)); + + // 转换成字符串 + for (int i = 0; i < s.Length; i++) + { + //格式后的字符是小写的字母 + //如果使用大写(X)则格式后的字符是大写字符 + pwd = pwd + s[i].ToString("X"); + + } + + return pwd; + } + } +} diff --git a/Infrastructure/Helpers/Md5Helper.cs b/Infrastructure/Helpers/Md5Helper.cs new file mode 100644 index 0000000..7e4179d --- /dev/null +++ b/Infrastructure/Helpers/Md5Helper.cs @@ -0,0 +1,58 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Security.Cryptography; + +namespace Infrastructure.Helpers +{ + public class Md5Helper + { + // + // 摘要: + // MD5加密 + // + // 参数: + // str: + // 加密字符 + // + // code: + // 加密位数16/32 + public static string Encrypt(string str, int code) + { + string result = string.Empty; + if (code == 16) + { + result = Hash(str).Substring(8, 16); + } + + if (code == 32) + { + result = Hash(str); + } + + return result; + } + + // + // 摘要: + // 32位MD5加密(小写) + // + // 参数: + // input: + // 输入字段 + public static string Hash(string input) + { + MD5CryptoServiceProvider mD5CryptoServiceProvider = new MD5CryptoServiceProvider(); + byte[] array = mD5CryptoServiceProvider.ComputeHash(Encoding.Default.GetBytes(input)); + StringBuilder stringBuilder = new StringBuilder(); + for (int i = 0; i < array.Length; i++) + { + stringBuilder.Append(array[i].ToString("x2")); + } + + return stringBuilder.ToString(); + } + } +} diff --git a/Infrastructure/Helpers/ObjectHelper.cs b/Infrastructure/Helpers/ObjectHelper.cs new file mode 100644 index 0000000..4e169ca --- /dev/null +++ b/Infrastructure/Helpers/ObjectHelper.cs @@ -0,0 +1,65 @@ +// *********************************************************************** +// Assembly : Infrastructure +// Author : Yubao Li +// Created : 11-23-2015 +// +// Last Modified By : Yubao Li +// Last Modified On : 11-23-2015 +// *********************************************************************** +// +// Copyright (c) . All rights reserved. +// +// +//对象COPY/初始化帮助,通常是防止从视图中传过来的对象属性为空,这其赋初始值 +// +// *********************************************************************** + +using System.Reflection; + +namespace Infrastructure.Helpers +{ + public static class ObjectHelper + { + public static T CopyTo(this object source) where T:class, new() + { + var result = new T(); + source.CopyTo(result); + return result; + } + + public static void CopyTo(this object source, T target) + where T : class,new() + { + if (source == null) + return; + + if (target == null) + { + target = new T(); + } + + foreach (var property in target.GetType().GetProperties()) + { + var propertyValue = source.GetType().GetProperty(property.Name).GetValue(source, null); + if (propertyValue != null) + { + if (propertyValue.GetType().IsClass) + { + + } + target.GetType().InvokeMember(property.Name, BindingFlags.SetProperty, null, target, new object[] { propertyValue }); + } + + } + + foreach (var field in target.GetType().GetFields()) + { + var fieldValue = source.GetType().GetField(field.Name).GetValue(source); + if (fieldValue != null) + { + target.GetType().InvokeMember(field.Name, BindingFlags.SetField, null, target, new object[] { fieldValue }); + } + } + } + } +} diff --git a/Infrastructure/Helpers/WordHelper.cs b/Infrastructure/Helpers/WordHelper.cs new file mode 100644 index 0000000..843f3db --- /dev/null +++ b/Infrastructure/Helpers/WordHelper.cs @@ -0,0 +1,347 @@ +using System.Collections; +using System.Text.RegularExpressions; +using NetModular.DocX.Core; + + +namespace Infrastructure.Helpers; + +public class WordHelper +{ + /// + /// 导出word + /// + /// 模板路径 + /// 保存路径 + /// 数据 + public static void Export(string tempFilePath, string outPath, Dictionary data) + { + bool isrewrite = true; // true=覆盖已存在的同名文件,false则反之 + //if (!System.IO.Directory.Exists(outPath)) + //{ + // System.IO.Directory.CreateDirectory(outPath); + //} + File.Copy(tempFilePath, outPath, isrewrite); + //新建一个Word文档,加载Load的方法和Create使用一样。 + using (DocX document = DocX.Load(outPath)) + { + ReplaceDoc(document, data); //普通文本替换 + ReplaceTable(document, data); //表格处理 + ReplaceList(document, data); //文本列表处理 + + document.Save(); //保存 + } + } + + private static void ReplaceDoc(DocX doc, Dictionary data) + { + foreach (var item in doc.Paragraphs) + { + ReplaceParagraph(item, data); + } + } + + private static void ReplaceList(DocX doc, Dictionary data) + { + //一定要在 普通文本替换和表格处理之后 + foreach (var p in doc.Paragraphs) + { + var li = GetListInfo(p, data); + if (li.IsList) + { + var pc = li.PTemp; + for (int i = 0; i < li.Data.Count; i++) + { + var pt = pc.InsertParagraphAfterSelf(p); + if (li.IsDict) + { + pc = ReplaceParagraph(pt, (Dictionary)li.Data[i]); + } + else + { + pc = ReplaceParagraph(pt, li.Data[i]); + } + } + + //删除模板行 + li.PTemp.Remove(false); + } + else + { + //do nonthing + } + } + } + + private static void ReplaceTable(DocX doc, Dictionary data) + { + var tbs = doc.Tables; + foreach (var table in tbs) + { + //需要先判断表格是列表还是表单 + var ti = GetTableInfo(table, data); + if (ti.IsList) + { + for (int i = 0; i < ti.Data.Count; i++) + { + var rt = table.InsertRow(ti.RowTemp); + rt.Height = ti.RowTemp.Height; + rt.MinHeight = ti.RowTemp.MinHeight; + if (ti.IsDict) + { + ReplaceRow(rt, (Dictionary)ti.Data[i]); + } + else + { + ReplaceRow(rt, ti.Data[i]); + } + } + + //删除模板行 + ti.RowTemp.Remove(); + } + else + { + //do nonthing + } + } + } + + private static void ReplaceRow(Row row, Dictionary data) + { + foreach (var cell in row.Cells) + { + foreach (var item in cell.Paragraphs) + { + ReplaceParagraph(item, data); + } + } + } + + private static void ReplaceRow(Row row, object data) + { + foreach (var cell in row.Cells) + { + foreach (var item in cell.Paragraphs) + { + ReplaceParagraph(item, data); + } + } + } + + private static Paragraph ReplaceParagraph(Paragraph p, Dictionary data) + { + Paragraph pr = p; + var ms = GetMatches(p.Text); + var ks = new List(); + var rs = new List(); + foreach (Match m in ms) + { + if (m.Groups.Count > 1) + { + string text = m.Groups[1].Value; + if (text.Contains(".")) + { + var ts = text.Split("."); + text = ts[ts.Length - 1]; + } + + ks.Add(text); + rs.Add(m.Value); + } + } + + bool isCt = data.Any(op => ks.Any(o => o.Contains(op.Key))); + bool isReplace = false; + + if (isCt) + { + if (ks.Count > 1) + { + for (int i = 0; i < ks.Count; i++) + { + if (data.ContainsKey(ks[i])) + { + p.ReplaceText(rs[i], data[ks[i]]?.ToString()); + } + } + } + else if (ks.Count == 1) + { + string text = ks[0]; + if (data.ContainsKey(text)) + { + var ct = data[text]?.ToString(); + var cts = ResolveText(ct); + var pc = p; + foreach (var item2 in cts) + { + if (string.IsNullOrWhiteSpace(item2)) continue; + var pt = pc.InsertParagraphAfterSelf(p); + pt.ReplaceText(rs[0], item2); + pc = pt; + pr = pc; + } + + isReplace = true; + } + } + + if (isReplace) + { + //删除原来段落 + p.Remove(false); + } + } + + return pr; + } + + private static Paragraph ReplaceParagraph(Paragraph p, object data) + { + var ms = GetMatches(p.Text); + var isReplace = false; + Paragraph pr = p; + foreach (Match item1 in ms) + { + if (item1.Groups.Count > 1) + { + string text = item1.Groups[1].Value; + + var ct = data?.ToString(); + var cts = ResolveText(ct); + var pc = p; + foreach (var item2 in cts) + { + var pt = pc.InsertParagraphAfterSelf(p); + pt.ReplaceText(item1.Value, item2); + pc = pt; + pr = pc; + } + + isReplace = true; + } + } + + if (isReplace) + { + //删除原来段落 + p.Remove(false); + } + + return pr; + } + + private static IList ResolveText(string text) + { + if (string.IsNullOrEmpty(text)) return new List(); + text = text.Replace("\r\n", "\n").Replace("\r", "\n"); + return text.Split('\n'); + } + + private static MatchCollection GetMatches(string text) + { + if (string.IsNullOrEmpty(text)) text = ""; + Regex regex = new Regex("[#|\\$]([a-zA-Z0-9_.]+?)[#|\\$]", + RegexOptions.IgnoreCase | RegexOptions.Multiline); + return regex.Matches(text); + } + + /// + /// 只获取列表匹配项 + /// + /// + /// + private static MatchCollection GetListMatches(string text) + { + if (string.IsNullOrEmpty(text)) text = ""; + Regex regex = new Regex("\\$([a-zA-Z0-9_.]+?)\\$", RegexOptions.IgnoreCase | RegexOptions.Multiline); + return regex.Matches(text); + } + + //纯辅助方法 + private class TableInfo + { + public bool IsList { get; set; } + public bool IsDict { get; set; } + public Row RowTemp { get; set; } + public IList Data { get; set; } + } + + //判断表格是列表还是表单 + private static TableInfo GetTableInfo(Table table, Dictionary data) + { + TableInfo result = new TableInfo(); + var r0 = table.Rows[table.Rows.Count - 1]; + var c0 = r0.Cells[r0.Cells.Count - 1]; + var ct = c0.Paragraphs[0].Text; + var ms = GetListMatches(ct); + foreach (Match item in ms) + { + if (item.Groups.Count > 1) + { + string text = item.Groups[1].Value; + if (text.Contains(".")) + { + result.IsDict = true; + text = text.Split('.')[0]; + } + + if (data.ContainsKey(text) && (data[text] is IList)) //判断是否是列表 + { + result.RowTemp = r0; + result.IsList = true; + result.Data = new List(); + foreach (var item1 in (data[text] as IList)) + { + result.Data.Add(item1); + } + + break; + } + } + } + + return result; + } + + private class ListInfo + { + public bool IsList { get; set; } + public bool IsDict { get; set; } + public Paragraph PTemp { get; set; } + public IList Data { get; set; } + } + + private static ListInfo GetListInfo(Paragraph p, Dictionary data) + { + ListInfo result = new ListInfo(); + var ms = GetListMatches(p.Text); + foreach (Match item in ms) + { + if (item.Groups.Count > 1) + { + string text = item.Groups[1].Value; + if (text.Contains(".")) + { + result.IsDict = true; + text = text.Split('.')[0]; + } + + if (data.ContainsKey(text) && (data[text] is IList)) //判断是否是列表 + { + result.PTemp = p; + result.IsList = true; + result.Data = new List(); + foreach (var item1 in (data[text] as IList)) + { + result.Data.Add(item1); + } + + break; + } + } + } + + return result; + } +} \ No newline at end of file diff --git a/Infrastructure/Helpers/XmlHelper.cs b/Infrastructure/Helpers/XmlHelper.cs new file mode 100644 index 0000000..8583287 --- /dev/null +++ b/Infrastructure/Helpers/XmlHelper.cs @@ -0,0 +1,710 @@ +using System.Xml; + +namespace Infrastructure.Helpers +{ + /// + /// XMLHelper参数 + /// + public class XmlParameter + { + private string _name; + private string _innerText; + private string _namespaceOfPrefix; + private AttributeParameter[] _attributes; + + public XmlParameter() { } + public XmlParameter(string name, params AttributeParameter[] attParas) : this(name, null, null, attParas) { } + public XmlParameter(string name, string innerText, params AttributeParameter[] attParas) : this(name, innerText, null, attParas) { } + public XmlParameter(string name, string innerText, string namespaceOfPrefix, params AttributeParameter[] attParas) + { + this._name = name; + this._innerText = innerText; + this._namespaceOfPrefix = namespaceOfPrefix; + this._attributes = attParas; + } + /// + /// 节点名称 + /// + public string Name + { + get { return this._name; } + set { this._name = value; } + } + /// + /// 节点文本 + /// + public string InnerText + { + get { return this._innerText; } + set { this._innerText = value; } + } + /// + /// 节点前缀xmlns声明(命名空间URI) + /// + public string NamespaceOfPrefix + { + get { return this._namespaceOfPrefix; } + set { this._namespaceOfPrefix = value; } + } + /// + /// 节点属性集 + /// + public AttributeParameter[] Attributes + { + get { return this._attributes; } + set { this._attributes = value; } + } + } + + /// + /// 节点属性参数 + /// + public class AttributeParameter + { + private string _name; + private string _value; + + public AttributeParameter() { } + public AttributeParameter(string attributeName, string attributeValue) + { + this._name = attributeName; + this._value = attributeValue; + } + /// + /// 属性名称 + /// + public string Name + { + get { return this._name; } + set { this._name = value; } + } + /// + /// 属性值 + /// + public string Value + { + get { return this._value; } + set { this._value = value; } + } + } + + public class XMLHelper + { + private static string _xPath; + /// + /// xml文件路径 + /// + public static string XmlPath + { + get { return _xPath; } + set { _xPath = value; } + } + private static string _configName = "XmlPath"; + /// + /// 配置文件节点名称,请设置在AppSettings节点下 + /// + public static string ConfigName + { + get { return _configName; } + set { _configName = value; GetConfig(); } + } + /// + /// 从配置文件读取xml路径 + /// + static void GetConfig() + { + //if (string.IsNullOrEmpty(_xPath)) + //{ + // try + // { + // _xPath = ConfigurationManager.AppSettings[_configName]; + // } + // catch { } + //} + } + static XMLHelper() { GetConfig(); } + + #region private AppendChild + /// + /// 添加一个子节点 + /// + /// XmlDocument对象 + /// 父节点 + /// Xml参数 + private static void AppendChild(XmlDocument xDoc, XmlNode parentNode, params XmlParameter[] xlParameter) + { + foreach (XmlParameter xpar in xlParameter) + { + XmlNode newNode = xDoc.CreateNode(XmlNodeType.Element, xpar.Name, null); + string ns = string.IsNullOrEmpty(xpar.NamespaceOfPrefix) ? "" : newNode.GetNamespaceOfPrefix(xpar.NamespaceOfPrefix); + foreach (AttributeParameter attp in xpar.Attributes) + { + XmlNode attr = xDoc.CreateNode(XmlNodeType.Attribute, attp.Name, ns == "" ? null : ns); + attr.Value = attp.Value; + newNode.Attributes.SetNamedItem(attr); + } + newNode.InnerText = xpar.InnerText; + parentNode.AppendChild(newNode); + } + } + #endregion + + #region private AddEveryNode + private static void AddEveryNode(XmlDocument xDoc, XmlNode parentNode, params XmlParameter[] paras) + { + XmlNodeList nlst = xDoc.DocumentElement.ChildNodes; + foreach (XmlNode xns in nlst) + { + if (xns.Name == parentNode.Name) + { + AppendChild(xDoc, xns, paras); + } + else + { + foreach (XmlNode xn in xns) + { + if (xn.Name == parentNode.Name) + { + AppendChild(xDoc, xn, paras); + } + } + } + } + } + #endregion + + #region private GetXmlDom + /// + /// 获得一个XmlDocument对象 + /// + /// + private static XmlDocument GetXmlDom() + { + XmlDocument xdoc = new XmlDocument(); + xdoc.Load(_xPath); + return xdoc; + } + #endregion + + #region CreateXmlFile + /// + /// 创建一个XML文档,成功创建后操作路径将直接指向该文件 + /// + /// 文件物理路径名 + /// 根结点名称 + /// 元素节点名称 + /// XML参数 + public static void CreateXmlFile(string fileName, string rootNode, string elementName, params XmlParameter[] xmlParameter) + { + XmlDocument xDoc = new XmlDocument(); + XmlNode xn = xDoc.CreateXmlDeclaration("1.0", "UTF-8", null); + xDoc.AppendChild(xn); + XmlNode root = xDoc.CreateElement(rootNode); + xDoc.AppendChild(root); + XmlNode ln = xDoc.CreateNode(XmlNodeType.Element, elementName, null); + AppendChild(xDoc, ln, xmlParameter); + root.AppendChild(ln); + try + { + xDoc.Save(fileName); + _xPath = fileName; + } + catch + { + throw; + } + } + /// + /// 创建一个XML文档,成功创建后操作路径将直接指向该文件 + /// + /// 文件物理路径名 + /// xml字符串 + public static void CreateXmlFile(string fileName, string xmlString) + { + XmlDocument xDoc = new XmlDocument(); + try + { + xDoc.LoadXml(xmlString); + xDoc.Save(fileName); + _xPath = fileName; + } + catch { throw; } + } + #endregion + + #region AddNewNode + /// + /// 添加新节点 + /// + /// 新节点的父节点对象 + /// Xml参数对象 + public static void AddNewNode(XmlNode parentNode, params XmlParameter[] xmlParameter) + { + XmlDocument xDoc = GetXmlDom(); + if (parentNode.Name == xDoc.DocumentElement.Name) + { + XmlNode newNode = xDoc.CreateNode(XmlNodeType.Element, xDoc.DocumentElement.ChildNodes[0].Name, null); + AppendChild(xDoc, newNode, xmlParameter); + xDoc.DocumentElement.AppendChild(newNode); + } + else + { + AddEveryNode(xDoc, parentNode, xmlParameter); + } + xDoc.Save(_xPath); + } + /// + /// 添加新节点 + /// + /// XmlDocument对象 + /// 新节点的父节点名称 + /// XML参数对象 + public static void AddNewNode(string parentName, params XmlParameter[] xmlParameter) + { + XmlDocument xDoc = GetXmlDom(); + XmlNode parentNode = GetNode(xDoc, parentName); + if (parentNode == null) return; + if (parentNode.Name == xDoc.DocumentElement.Name) + { + XmlNode newNode = xDoc.CreateNode(XmlNodeType.Element, xDoc.DocumentElement.ChildNodes[0].Name, null); + AppendChild(xDoc, newNode, xmlParameter); + xDoc.DocumentElement.AppendChild(newNode); + } + else + { + AddEveryNode(xDoc, parentNode, xmlParameter); + } + xDoc.Save(_xPath); + } + #endregion + + #region AddAttribute + /// + /// 添加节点属性 + /// + /// 节点对象 + /// 该节点的命名空间URI + /// 新属性名称 + /// 属性值 + public static void AddAttribute(XmlNode node, string namespaceOfPrefix, string attributeName, string attributeValue) + { + XmlDocument xDoc = GetXmlDom(); + string ns = string.IsNullOrEmpty(namespaceOfPrefix) ? "" : node.GetNamespaceOfPrefix(namespaceOfPrefix); + XmlNode xn = xDoc.CreateNode(XmlNodeType.Attribute, attributeName, ns == "" ? null : ns); + xn.Value = attributeValue; + node.Attributes.SetNamedItem(xn); + xDoc.Save(_xPath); + } + /// + /// 添加节点属性 + /// + /// 节点对象 + /// 该节点的命名空间URI + /// 节点属性参数 + public static void AddAttribute(XmlNode node, string namespaceOfPrefix, params AttributeParameter[] attributeParameters) + { + XmlDocument xDoc = GetXmlDom(); + string ns = string.IsNullOrEmpty(namespaceOfPrefix) ? "" : node.GetNamespaceOfPrefix(namespaceOfPrefix); + foreach (AttributeParameter attp in attributeParameters) + { + XmlNode xn = xDoc.CreateNode(XmlNodeType.Attribute, attp.Name, ns == "" ? null : ns); + xn.Value = attp.Value; + node.Attributes.SetNamedItem(xn); + } + xDoc.Save(_xPath); + } + /// + /// 添加节点属性 + /// + /// 节点名称 + /// 该节点的命名空间URI + /// 新属性名称 + /// 属性值 + public static void AddAttribute(string nodeName, string namespaceOfPrefix, string attributeName, string attributeValue) + { + XmlDocument xDoc = GetXmlDom(); + XmlNodeList xlst = xDoc.DocumentElement.ChildNodes; + for (int i = 0; i < xlst.Count; i++) + { + XmlNode node = GetNode(xlst[i], nodeName); + if (node == null) break; + AddAttribute(node, namespaceOfPrefix, attributeName, attributeValue); + } + xDoc.Save(_xPath); + } + /// + /// 添加节点属性 + /// + /// 节点名称 + /// 该节点的命名空间URI + /// 节点属性参数 + public static void AddAttribute(string nodeName, string namespaceOfPrefix, params AttributeParameter[] attributeParameters) + { + XmlDocument xDoc = GetXmlDom(); + XmlNodeList xlst = xDoc.DocumentElement.ChildNodes; + for (int i = 0; i < xlst.Count; i++) + { + XmlNode node = GetNode(xlst[i], nodeName); + if (node == null) break; + AddAttribute(node, namespaceOfPrefix, attributeParameters); + } + xDoc.Save(_xPath); + } + #endregion + + #region GetNode + /// + /// 获取指定节点名称的节点对象 + /// + /// 节点名称 + /// + public static XmlNode GetNode(string nodeName) + { + XmlDocument xDoc = GetXmlDom(); + if (xDoc.DocumentElement.Name == nodeName) return (XmlNode)xDoc.DocumentElement; + XmlNodeList nlst = xDoc.DocumentElement.ChildNodes; + foreach (XmlNode xns in nlst) // 遍历所有子节点 + { + if (xns.Name == nodeName) return xns; + else + { + XmlNode xn = GetNode(xns, nodeName); + if (xn != null) return xn; + } + } + return null; + } + /// + /// 获取指定节点名称的节点对象 + /// + /// 节点对象 + /// 节点名称 + /// + public static XmlNode GetNode(XmlNode node, string nodeName) + { + foreach (XmlNode xn in node) + { + if (xn.Name == nodeName) return xn; + else + { + XmlNode tmp = GetNode(xn, nodeName); + if (tmp != null) return tmp; + } + } + return null; + } + /// + /// 获取指定节点名称的节点对象 + /// + /// 节点索引 + /// 节点名称 + public static XmlNode GetNode(int index, string nodeName) + { + XmlDocument xDoc = GetXmlDom(); + XmlNodeList nlst = xDoc.DocumentElement.ChildNodes; + if (nlst.Count <= index) return null; + if (nlst[index].Name == nodeName) return (XmlNode)nlst[index]; + foreach (XmlNode xn in nlst[index]) + { + return GetNode(xn, nodeName); + } + return null; + } + /// + /// 获取指定节点名称的节点对象 + /// + /// 节点对象 + /// 节点名称 + /// 节点内容 + public static XmlNode GetNode(XmlNode node, string nodeName, string innerText) + { + foreach (XmlNode xn in node) + { + if (xn.Name == nodeName && xn.InnerText == innerText) return xn; + else + { + XmlNode tmp = GetNode(xn, nodeName, innerText); + if (tmp != null) return tmp; + } + } + return null; + } + /// + /// 获取指定节点名称的节点对象 + /// + /// 节点名称 + /// 节点内容 + public static XmlNode GetNode(string nodeName, string innerText) + { + XmlDocument xDoc = GetXmlDom(); + XmlNodeList nlst = xDoc.DocumentElement.ChildNodes; + foreach (XmlNode xns in nlst) // 遍历所有子节点 + { + if (xns.Name == nodeName && xns.InnerText == innerText) return xns; + XmlNode tmp = GetNode(xns, nodeName, innerText); + if (tmp != null) return tmp; + } + return null; + } + /// + /// 获取指定节点名称的节点对象 + /// + /// XML参数 + public static XmlNode GetNode(XmlParameter xmlParameter) + { + return GetNode(xmlParameter.Name, xmlParameter.InnerText); + } + /// + /// 获取指定节点名称的节点对象 + /// + /// 节点对象 + /// XML参数 + public static XmlNode GetNode(XmlNode node, XmlParameter xmlParameter) + { + return GetNode(node, xmlParameter.Name, node.InnerText); + } + #endregion + + #region UpdateNode + private static void UpdateNode(XmlNode node, XmlParameter xmlParameter) + { + node.InnerText = xmlParameter.InnerText; + for (int i = 0; i < xmlParameter.Attributes.Length; i++) + { + for (int j = 0; j < node.Attributes.Count; j++) + { + if (node.Attributes[j].Name == xmlParameter.Attributes[i].Name) + { + node.Attributes[j].Value = xmlParameter.Attributes[i].Value; + } + } + } + } + private static void UpdateNode(XmlNode node, string innerText, AttributeParameter[] attributeParameters) + { + node.InnerText = innerText; + for (int i = 0; i < attributeParameters.Length; i++) + { + for (int j = 0; j < node.Attributes.Count; j++) + { + if (node.Attributes[j].Name == attributeParameters[i].Name) + { + node.Attributes[j].Value = attributeParameters[i].Value; + } + } + } + } + /// + /// 修改节点的内容 + /// + /// 节点索引 + /// XML参数对象 + public static void UpdateNode(int index, XmlParameter xmlParameter) + { + XmlDocument xDoc = GetXmlDom(); + XmlNodeList nlst = xDoc.DocumentElement.ChildNodes; + if (nlst.Count <= index) return; + if (nlst[index].Name == xmlParameter.Name) + { + UpdateNode(nlst[index], xmlParameter); + } + else + { + foreach (XmlNode xn in nlst[index]) + { + XmlNode xnd = GetNode(xn, xmlParameter.Name); + if (xnd != null) + { + UpdateNode(xnd, xmlParameter); + } + } + } + xDoc.Save(_xPath); + } + /// + /// 修改节点的内容 + /// + /// 节点索引 + /// 节点名称 + /// 修改后的内容 + public static void UpdateNode(int index, string nodeName, string newInnerText) + { + XmlDocument xDoc = GetXmlDom(); + XmlNodeList nlst = xDoc.DocumentElement.ChildNodes; + if (nlst.Count <= index) return; + if (nlst[index].Name == nodeName) + { + nlst[index].InnerText = newInnerText; + } + else + { + foreach (XmlNode xn in nlst[index]) + { + XmlNode xnd = GetNode(xn, nodeName); + if (xnd != null) + { + xnd.InnerText = newInnerText; + } + } + } + xDoc.Save(_xPath); + } + /// + /// 修改节点的内容 + /// + /// XmlParameter对象 + /// 修改后的内容 + /// 需要修改的属性 + public static void UpdateNode(XmlParameter xmlParameter, string innerText, params AttributeParameter[] attributeParameters) + { + XmlDocument xDoc = GetXmlDom(); + XmlNodeList nlst = xDoc.DocumentElement.ChildNodes; + foreach (XmlNode xns in nlst) // 遍历所有子节点 + { + if (xns.Name == xmlParameter.Name && xns.InnerText == xmlParameter.InnerText) + { + UpdateNode(xns, innerText, attributeParameters); + break; + } + XmlNode tmp = GetNode(xns, xmlParameter); + if (tmp != null) + { + UpdateNode(tmp, innerText, attributeParameters); + break; + } + } + xDoc.Save(_xPath); + } + #endregion + + #region DeleteNode + /// + /// 删除节点 + /// + /// 节点索引 + public static void DeleteNode(int index) + { + XmlDocument xDoc = GetXmlDom(); + XmlNodeList nlst = xDoc.DocumentElement.ChildNodes; + nlst[index].ParentNode.RemoveChild(nlst[index]); + xDoc.Save(_xPath); + } + /// + /// 删除节点 + /// + /// 需要删除的节点对象 + public static void DeleteNode(params XmlNode[] nodeList) + { + XmlDocument xDoc = GetXmlDom(); + foreach (XmlNode xnl in nodeList) + { + foreach (XmlNode xn in xDoc.DocumentElement.ChildNodes) + { + if (xnl.Equals(xn)) + { + xn.ParentNode.RemoveChild(xn); + break; + } + } + } + xDoc.Save(_xPath); + } + /// + /// 删除节点 + /// + /// XmlDocument对象 + /// 节点名称 + /// 节点内容 + public static void DeleteNode(string nodeName, string nodeText) + { + XmlDocument xDoc = GetXmlDom(); + foreach (XmlNode xn in xDoc.DocumentElement.ChildNodes) + { + if (xn.Name == nodeName) + { + if (xn.InnerText == nodeText) + { + xn.ParentNode.RemoveChild(xn); + return; + } + } + else + { + XmlNode node = GetNode(xn, nodeName); + if (node != null && node.InnerText == nodeText) + { + node.ParentNode.ParentNode.RemoveChild(node.ParentNode); + return; + } + } + } + xDoc.Save(_xPath); + } + #endregion + + #region SetAttribute + /// + /// 修改属性值 + /// + /// 元素对象 + /// 属性参数 + private static void SetAttribute(XmlElement elem, params AttributeParameter[] attps) + { + foreach (AttributeParameter attp in attps) + { + elem.SetAttribute(attp.Name, attp.Value); + } + } + /// + /// 修改属性值 + /// + /// XML参数 + /// 属性参数 + public static void SetAttribute(XmlParameter xmlParameter, params AttributeParameter[] attributeParameters) + { + XmlDocument xDoc = GetXmlDom(); + XmlNodeList nlst = xDoc.DocumentElement.ChildNodes; + foreach (XmlNode xns in nlst) // 遍历所有子节点 + { + if (xns.Name == xmlParameter.Name && xns.InnerText == xmlParameter.InnerText) + { + SetAttribute((XmlElement)xns, attributeParameters); + break; + } + XmlNode tmp = GetNode(xns, xmlParameter); + if (tmp != null) + { + SetAttribute((XmlElement)tmp, attributeParameters); + break; + } + } + xDoc.Save(_xPath); + } + /// + /// 修改属性值 + /// + /// XML参数 + /// 新属性值 + public static void SetAttribute(XmlParameter xmlParameter, string attributeName, string attributeValue) + { + XmlDocument xDoc = GetXmlDom(); + XmlNodeList nlst = xDoc.DocumentElement.ChildNodes; + foreach (XmlNode xns in nlst) // 遍历所有子节点 + { + if (xns.Name == xmlParameter.Name && xns.InnerText == xmlParameter.InnerText) + { + ((XmlElement)xns).SetAttribute(attributeName, attributeValue); + break; + } + XmlNode tmp = GetNode(xns, xmlParameter); + if (tmp != null) + { + ((XmlElement)tmp).SetAttribute(attributeName, attributeValue); + break; + } + } + xDoc.Save(_xPath); + } + #endregion + } +} diff --git a/Infrastructure/Infrastructure.csproj b/Infrastructure/Infrastructure.csproj new file mode 100644 index 0000000..0a9fd22 --- /dev/null +++ b/Infrastructure/Infrastructure.csproj @@ -0,0 +1,39 @@ + + + + net6.0 + enable + + + + bin\Debug\net5.0\Infrastructure.xml + 1701;1702;1591;1573;1572;1570 + + + + + + + + + + + + + + + + + + + + + + + + + PreserveNewest + + + + diff --git a/Infrastructure/Json.cs b/Infrastructure/Json.cs new file mode 100644 index 0000000..94d7572 --- /dev/null +++ b/Infrastructure/Json.cs @@ -0,0 +1,83 @@ +using System.Collections.Generic; +using System.Data; +using Newtonsoft.Json; +using Newtonsoft.Json.Converters; +using Newtonsoft.Json.Linq; + +namespace Infrastructure +{ + /// + /// Json操作 + /// + public static class Json + { + public static object ToJson(this string Json) + { + return Json == null ? null : JsonConvert.DeserializeObject(Json); + } + public static string ToJson(this object obj) + { + var timeConverter = new IsoDateTimeConverter { DateTimeFormat = "yyyy-MM-dd HH:mm:ss" }; + return JsonConvert.SerializeObject(obj, timeConverter); + } + public static string ToJson(this object obj, string datetimeformats) + { + var timeConverter = new IsoDateTimeConverter { DateTimeFormat = datetimeformats }; + return JsonConvert.SerializeObject(obj, timeConverter); + } + public static T ToObject(this string Json) + { + return Json == null ? default(T) : JsonConvert.DeserializeObject(Json); + } + public static List ToList(this string Json) + { + return Json == null ? null : JsonConvert.DeserializeObject>(Json); + } + public static DataTable ToTable(this string Json) + { + return Json == null ? null : JsonConvert.DeserializeObject(Json); + } + public static JObject ToJObject(this string Json) + { + return Json == null ? JObject.Parse("{}") : JObject.Parse(Json.Replace(" ", "")); + } + /// + /// 转换成JToken + /// + /// + /// + public static JToken ToJToken(this string str) + { + try + { + var jToken = (JToken)JsonConvert.DeserializeObject(str); + return jToken; + } + catch (Exception ex) + { + return null; + } + } + + /// + /// 检查请求参数是否异常 + /// + /// + public static void CheckReqIsNull(this object obj) + { + if (obj == null) throw new Exception("数据异常,请检查输入信息是否正确。"); + } + + /// + /// 初始化数据 + /// + /// + /// + /// + public static List InitListData(this List list) + { + if (list == null) list = new List(); + return list; + } + } +} diff --git a/Infrastructure/JsonConverter.cs b/Infrastructure/JsonConverter.cs new file mode 100644 index 0000000..fae1047 --- /dev/null +++ b/Infrastructure/JsonConverter.cs @@ -0,0 +1,61 @@ +// *********************************************************************** +// Assembly : Infrastructure +// Author : Yubao Li +// Created : 09-07-2015 +// +// Last Modified By : Yubao Li +// Last Modified On : 09-07-2015 +// *********************************************************************** +// +// Copyright (c) . All rights reserved. +// +// 解决JSON转换空GUID问题 +// *********************************************************************** + +using System; +using Newtonsoft.Json; + +namespace Infrastructure +{ + public class GuidConverter : JsonConverter + { + public override bool CanConvert(Type objectType) + { + return objectType.IsAssignableFrom(typeof(Guid)); + } + + public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) + { + Guid result = Guid.Empty; + if (reader.Value == null) return result; + Guid.TryParse(reader.Value.ToString(), out result); + return result; + } + + public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) + { + serializer.Serialize(writer, value); + } + } + + public class DecimalConverter : JsonConverter + { + public override bool CanConvert(Type objectType) + { + return objectType.IsAssignableFrom(typeof(decimal)); + } + + public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) + { + decimal result = 0; + if (reader.Value == null) return result; + decimal.TryParse(reader.Value.ToString(), out result); + return result; + } + + public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) + { + serializer.Serialize(writer, value); + } + } +} \ No newline at end of file diff --git a/Infrastructure/JsonHelper.cs b/Infrastructure/JsonHelper.cs new file mode 100644 index 0000000..3a0a2ee --- /dev/null +++ b/Infrastructure/JsonHelper.cs @@ -0,0 +1,55 @@ +// *********************************************************************** +// Assembly : FairUtility +// Author : Yubao Li +// Created : 08-12-2015 +// +// Last Modified By : Yubao Li +// Last Modified On : 08-12-2015 +// *********************************************************************** +// +// Copyright (c) . All rights reserved. +// +// json序列化帮助类 +// *********************************************************************** + +using Newtonsoft.Json; +using Newtonsoft.Json.Converters; + +namespace Infrastructure +{ + public class JsonHelper + { + private static JsonHelper _jsonHelper = new JsonHelper(); + public static JsonHelper Instance { get { return _jsonHelper; } } + + public string Serialize(object obj) + { + return JsonConvert.SerializeObject(obj, new IsoDateTimeConverter { DateTimeFormat = "yyyy-MM-dd HH:mm:ss" }); + } + + public string SerializeByConverter(object obj, params JsonConverter[] converters) + { + return JsonConvert.SerializeObject(obj, converters); + } + + public T Deserialize(string input) + { + return JsonConvert.DeserializeObject(input); + } + + public T DeserializeByConverter(string input,params JsonConverter[] converter) + { + return JsonConvert.DeserializeObject(input, converter); + } + + public T DeserializeBySetting(string input, JsonSerializerSettings settings) + { + return JsonConvert.DeserializeObject(input, settings); + } + + private object NullToEmpty(object obj) + { + return null; + } + } +} \ No newline at end of file diff --git a/Infrastructure/KeyDescription.cs b/Infrastructure/KeyDescription.cs new file mode 100644 index 0000000..9eca976 --- /dev/null +++ b/Infrastructure/KeyDescription.cs @@ -0,0 +1,24 @@ +namespace Infrastructure +{ + public class KeyDescription + { + /// + /// 键值 + /// + public string Key { get; set; } + /// + /// 键的描述 + /// + public string Description { get; set; } + + /// + /// 前端是否显示 + /// + public bool Browsable { get; set; } + + /// + /// 字段类型 + /// + public string Type { get; set; } + } +} diff --git a/Infrastructure/Middleware/IApplicationBuilderExtension.cs b/Infrastructure/Middleware/IApplicationBuilderExtension.cs new file mode 100644 index 0000000..cc98115 --- /dev/null +++ b/Infrastructure/Middleware/IApplicationBuilderExtension.cs @@ -0,0 +1,20 @@ +using Microsoft.AspNetCore.Builder; + +namespace Infrastructure.Middleware +{ + /// + /// + /// + public static class ApplicationBuilderExtension + { + /// + /// 注入日志中间件 + /// + /// + /// + public static IApplicationBuilder UseLogMiddleware(this IApplicationBuilder builder) + { + return builder.UseMiddleware(); + } + } +} \ No newline at end of file diff --git a/Infrastructure/Middleware/RequestResponseLoggingMiddleware.cs b/Infrastructure/Middleware/RequestResponseLoggingMiddleware.cs new file mode 100644 index 0000000..8170759 --- /dev/null +++ b/Infrastructure/Middleware/RequestResponseLoggingMiddleware.cs @@ -0,0 +1,172 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.Logging; + +namespace Infrastructure.Middleware +{ + /// + /// 请求与返回中间件 + /// + public class RequestResponseLoggingMiddleware + { + private readonly RequestDelegate _next; + private readonly ILogger _log; + + /// + /// + /// + public RequestResponseLoggingMiddleware(RequestDelegate next, ILogger log) + { + _next = next; + _log = log; + } + + /// + /// + /// + /// + /// + public async Task Invoke(HttpContext context) + { + #region 这里可以加入正则验证context.Path。 过滤不需要记录日志的api + + //var path = context.Request.Path.ToString().ToLower(); + + //if (path.Contains("/index") || path.Contains("/check") || + // path.Contains("/swagger") || path.Contains("/getsysdatas") || path.Contains("/load")) + //{ + await CatchNext(context); + // return; + //} + + #endregion + + #region 日志 + // 启用耗时 日志记录 + //var stopwatch = new Stopwatch(); + //stopwatch.Start(); + //var logData = new Dictionary(); + //var request = context.Request; + //logData.Add("request.url", request.Path.ToString()); + //logData.Add("request.headers", + // request.Headers.ToDictionary(x => x.Key, v => string.Join(";", v.Value.ToList()))); + //logData.Add("request.method", request.Method); + //logData.Add("request.executeStartTime", DateTimeOffset.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")); + ////追踪实别器 + //logData.Add("traceIdentifier", context.TraceIdentifier); + //// 获取请求body内容 + //if (request.Method.ToLower().Equals("post")) + //{ + // // 启用倒带功能,就可以让 Request.Body 可以再次读取 + // request.EnableBuffering(); + // // 文件上传 记录文件信息 + // if (path.Contains("/upload")) + // { + // var content = string.Join(",", request.Form.Files.Select(item => item.FileName)); + // logData.Add("request.body", $"收到上传文件:{content}"); + // } + // else + // { + // var sr = new StreamReader(request.Body, Encoding.UTF8); + // var content = sr.ReadToEndAsync().Result; + // logData.Add("request.body", content); + // request.Body.Position = 0; + // } + //} + //else if (request.Method.ToLower().Equals("get")) + //{ + // logData.Add("request.body", request.QueryString.Value); + //} + + //// 获取Response.Body内容 + //var originalBodyStream = context.Response.Body; + //using (var responseBody = new MemoryStream()) + //{ + // context.Response.Body = responseBody; + // await CatchNext(context); + // if (!logData.ContainsKey("response.body")) + // { + // logData.Add("response.body", await GetResponse(context.Response)); + // } + + // logData.Add("response.executeEndTime", DateTimeOffset.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")); + // logData.Add("response.statusCode", context.Response.StatusCode); + // await responseBody.CopyToAsync(originalBodyStream); + //} + + //// 响应完成记录时间和存入日志 + //context.Response.OnCompleted(() => + //{ + // try + // { + // stopwatch.Stop(); + // logData.Add("elapsedTime", stopwatch.ElapsedMilliseconds + "ms"); + // var json = JsonHelper.Instance.Serialize(logData); + // _log.LogInformation(json); + // return Task.CompletedTask; + // } + // catch (Exception ex) + // { + // return Task.FromException(ex); + // } + //}); + #endregion + } + + private async Task CatchNext(HttpContext context) + { + var logData = new Dictionary(); + try + { + var request = context.Request; + request.EnableBuffering(); + + // 获取请求body内容 + if (request.Method.ToLower().Equals("post")) + { + // 启用倒带功能,就可以让 Request.Body 可以再次读取 + request.EnableBuffering(); + // 文件上传 记录文件信息 + var sr = new StreamReader(request.Body, Encoding.UTF8); + var content = sr.ReadToEndAsync().Result; + logData.Add("request.body", content); + request.Body.Position = 0; + } + else if (request.Method.ToLower().Equals("get")) + { + logData.Add("request.body", request.QueryString.Value); + } + + await _next(context); + } + catch (Exception ex) + { + _log.LogError(JsonHelper.Instance.Serialize(logData)); + _log.LogError(ex, "系统错误日志,管道捕获"); + context.Response.StatusCode = 200; + context.Response.ContentType = "application/json; charset=utf-8"; + var result = new { code = 500, message = ex.Message ?? "系统错误,请稍后再试" }; + await context.Response.WriteAsync(JsonHelper.Instance.Serialize(result)); + } + } + + /// + /// 获取响应内容 + /// + /// + /// + private static async Task GetResponse(HttpResponse response) + { + response.Body.Seek(0, SeekOrigin.Begin); + var text = await new StreamReader(response.Body).ReadToEndAsync(); + response.Body.Seek(0, SeekOrigin.Begin); + return text; + } + } +} \ No newline at end of file diff --git a/Infrastructure/PredicateBuilder.cs b/Infrastructure/PredicateBuilder.cs new file mode 100644 index 0000000..c277b72 --- /dev/null +++ b/Infrastructure/PredicateBuilder.cs @@ -0,0 +1,28 @@ +using System; +using System.Linq; +using System.Linq.Expressions; + +namespace Infrastructure +{ + public static class PredicateBuilder + { + public static Expression> True() { return f => true; } + public static Expression> False() { return f => false; } + + public static Expression> Or(this Expression> expr1, + Expression> expr2) + { + var invokedExpr = Expression.Invoke(expr2, expr1.Parameters.Cast()); + return Expression.Lambda> + (Expression.OrElse(expr1.Body, invokedExpr), expr1.Parameters); + } + + public static Expression> And(this Expression> expr1, + Expression> expr2) + { + var invokedExpr = Expression.Invoke(expr2, expr1.Parameters.Cast()); + return Expression.Lambda> + (Expression.AndAlso(expr1.Body, invokedExpr), expr1.Parameters); + } + } +} \ No newline at end of file diff --git a/Infrastructure/Provider/PathProvider.cs b/Infrastructure/Provider/PathProvider.cs new file mode 100644 index 0000000..87dbb5f --- /dev/null +++ b/Infrastructure/Provider/PathProvider.cs @@ -0,0 +1,52 @@ +using System.IO; +using Infrastructure.Extensions; +using Infrastructure.Extensions.AutofacManager; +using Microsoft.Extensions.Hosting; + +namespace Infrastructure.Provider +{ + public interface IPathProvider : IDependency + { + string MapPath(string path); + string MapPath(string path, bool rootPath); + IHostEnvironment GetHostingEnvironment(); + } + + public class PathProvider : IPathProvider + { + private IHostEnvironment _hostingEnvironment; + + public PathProvider(IHostEnvironment environment) + { + _hostingEnvironment = environment; + } + public IHostEnvironment GetHostingEnvironment() + { + return _hostingEnvironment; + } + + /// + /// 获取服务器文件路径 + /// + /// + /// + public string MapPath(string path) + { + return MapPath(path, false); + } + /// + /// 获取wwwroot路径 + /// + /// + /// 是否获取wwwroot路径 + /// + public string MapPath(string path, bool rootPath) + { + if (rootPath) + { + return Path.Combine(_hostingEnvironment.ContentRootPath,"wwwroot").ReplacePath(); + } + return Path.Combine(_hostingEnvironment.ContentRootPath, path).ReplacePath(); + } + } +} diff --git a/Infrastructure/Response.cs b/Infrastructure/Response.cs new file mode 100644 index 0000000..2e6bbab --- /dev/null +++ b/Infrastructure/Response.cs @@ -0,0 +1,45 @@ +namespace Infrastructure +{ + public class Response + { + /// + /// 操作消息【当Status不为 200时,显示详细的错误信息】 + /// + public string Message { get; set; } + + /// + /// 操作状态码,200为正常 + /// + public int Code { get; set; } + + public Response() + { + Code = 200; + Message = "操作成功"; + } + } + + public class ResponsePage : Response + { + public int Count { get; set; } + } + + public class PageInfo + { + public T Items { get; set; } + + public int Total { get; set; } + } + + /// + /// WEBAPI通用返回泛型基类 + /// + /// + public class Response : Response + { + /// + /// 回传的结果 + /// + public T Result { get; set; } + } +} diff --git a/Infrastructure/Snowflake/Contract/IIdGenerator.cs b/Infrastructure/Snowflake/Contract/IIdGenerator.cs new file mode 100644 index 0000000..3884c30 --- /dev/null +++ b/Infrastructure/Snowflake/Contract/IIdGenerator.cs @@ -0,0 +1,29 @@ +/* + * 版权属于:yitter(yitter@126.com) + * 开源地址:https://github.com/yitter/idgenerator + * 版权协议:MIT + * 版权说明:只要保留本版权,你可以免费使用、修改、分发本代码。 + * 免责条款:任何因为本代码产生的系统、法律、政治、宗教问题,均与版权所有者无关。 + * + */ + +using System; + +namespace Yitter.IdGenerator +{ + public interface IIdGenerator + { + /// + /// 生成过程中产生的事件 + /// + //Action GenIdActionAsync { get; set; } + + /// + /// 生成新的long型Id + /// + /// + long NewLong(); + + // Guid NewGuid(); + } +} diff --git a/Infrastructure/Snowflake/Contract/ISnowWorker.cs b/Infrastructure/Snowflake/Contract/ISnowWorker.cs new file mode 100644 index 0000000..52e6183 --- /dev/null +++ b/Infrastructure/Snowflake/Contract/ISnowWorker.cs @@ -0,0 +1,22 @@ +/* + * 版权属于:yitter(yitter@126.com) + * 开源地址:https://github.com/yitter/idgenerator + * 版权协议:MIT + * 版权说明:只要保留本版权,你可以免费使用、修改、分发本代码。 + * 免责条款:任何因为本代码产生的系统、法律、政治、宗教问题,均与版权所有者无关。 + * + */ + +using System; +using System.Collections.Generic; +using System.Text; + +namespace Yitter.IdGenerator +{ + internal interface ISnowWorker + { + //Action GenAction { get; set; } + + long NextId(); + } +} diff --git a/Infrastructure/Snowflake/Contract/IdGeneratorOptions.cs b/Infrastructure/Snowflake/Contract/IdGeneratorOptions.cs new file mode 100644 index 0000000..ce577b7 --- /dev/null +++ b/Infrastructure/Snowflake/Contract/IdGeneratorOptions.cs @@ -0,0 +1,92 @@ +/* + * 版权属于:yitter(yitter@126.com) + * 开源地址:https://github.com/yitter/idgenerator + * 版权协议:MIT + * 版权说明:只要保留本版权,你可以免费使用、修改、分发本代码。 + * 免责条款:任何因为本代码产生的系统、法律、政治、宗教问题,均与版权所有者无关。 + * + */ + +using System; +using System.Collections.Generic; +using System.Text; + +namespace Yitter.IdGenerator +{ + public class IdGeneratorOptions + { + /// + /// 雪花计算方法 + /// (1-漂移算法|2-传统算法),默认1 + /// + public virtual short Method { get; set; } = 1; + + /// + /// 基础时间(UTC格式) + /// 不能超过当前系统时间 + /// + public virtual DateTime BaseTime { get; set; } = new DateTime(2020, 2, 20, 2, 20, 2, 20, DateTimeKind.Utc); + + /// + /// 机器码 + /// 必须由外部设定,最大值 2^WorkerIdBitLength-1 + /// + public virtual ushort WorkerId { get; set; } = 0; + + /// + /// 机器码位长 + /// 默认值6,取值范围 [1, 15](要求:序列数位长+机器码位长不超过22) + /// + public virtual byte WorkerIdBitLength { get; set; } = 6;//10; + + /// + /// 序列数位长 + /// 默认值6,取值范围 [3, 21](要求:序列数位长+机器码位长不超过22) + /// + public virtual byte SeqBitLength { get; set; } = 6;//10; + + /// + /// 最大序列数(含) + /// 设置范围 [MinSeqNumber, 2^SeqBitLength-1],默认值0,表示最大序列数取最大值(2^SeqBitLength-1]) + /// + public virtual int MaxSeqNumber { get; set; } = 0; + + /// + /// 最小序列数(含) + /// 默认值5,取值范围 [5, MaxSeqNumber],每毫秒的前5个序列数对应编号0-4是保留位,其中1-4是时间回拨相应预留位,0是手工新值预留位 + /// + public virtual ushort MinSeqNumber { get; set; } = 5; + + /// + /// 最大漂移次数(含), + /// 默认2000,推荐范围500-10000(与计算能力有关) + /// + public virtual int TopOverCostCount { get; set; } = 2000; + + /// + /// 数据中心ID(默认0) + /// + public virtual uint DataCenterId { get; set; } = 0; + + /// + /// 数据中心ID长度(默认0) + /// + public virtual byte DataCenterIdBitLength { get; set; } = 0; + + /// + /// 时间戳类型(0-毫秒,1-秒),默认0 + /// + public virtual byte TimestampType { get; set; } = 0; + + + public IdGeneratorOptions() + { + + } + + public IdGeneratorOptions(ushort workerId) + { + WorkerId = workerId; + } + } +} diff --git a/Infrastructure/Snowflake/Contract/OverCostActionArg.cs b/Infrastructure/Snowflake/Contract/OverCostActionArg.cs new file mode 100644 index 0000000..bf46ff3 --- /dev/null +++ b/Infrastructure/Snowflake/Contract/OverCostActionArg.cs @@ -0,0 +1,57 @@ +/* + * 版权属于:yitter(yitter@126.com) + * 开源地址:https://github.com/yitter/idgenerator + * 版权协议:MIT + * 版权说明:只要保留本版权,你可以免费使用、修改、分发本代码。 + * 免责条款:任何因为本代码产生的系统、法律、政治、宗教问题,均与版权所有者无关。 + * + */ + +using System; +using System.Collections.Generic; +using System.Text; + +namespace Yitter.IdGenerator +{ + /// + /// Id生成时回调参数 + /// + public class OverCostActionArg + { + /// + /// 事件类型 + /// 1-开始,2-结束,8-漂移 + /// + public virtual int ActionType { get; set; } + /// + /// 时间戳 + /// + public virtual long TimeTick { get; set; } + /// + /// 机器码 + /// + public virtual ushort WorkerId { get; set; } + /// + /// 漂移计算次数 + /// + public virtual int OverCostCountInOneTerm { get; set; } + /// + /// 漂移期间生产ID个数 + /// + public virtual int GenCountInOneTerm { get; set; } + /// + /// 漂移周期 + /// + public virtual int TermIndex { get; set; } + + public OverCostActionArg(ushort workerId, long timeTick, int actionType = 0, int overCostCountInOneTerm = 0, int genCountWhenOverCost = 0, int index = 0) + { + ActionType = actionType; + TimeTick = timeTick; + WorkerId = workerId; + OverCostCountInOneTerm = overCostCountInOneTerm; + GenCountInOneTerm = genCountWhenOverCost; + TermIndex = index; + } + } +} diff --git a/Infrastructure/Snowflake/Core/SnowWorkerM1.cs b/Infrastructure/Snowflake/Core/SnowWorkerM1.cs new file mode 100644 index 0000000..c71f7ee --- /dev/null +++ b/Infrastructure/Snowflake/Core/SnowWorkerM1.cs @@ -0,0 +1,379 @@ +/* + * 版权属于:yitter(yitter@126.com) + * 开源地址:https://github.com/yitter/idgenerator + * 版权协议:MIT + * 版权说明:只要保留本版权,你可以免费使用、修改、分发本代码。 + * 免责条款:任何因为本代码产生的系统、法律、政治、宗教问题,均与版权所有者无关。 + * + */ + +using System; +using System.Threading; +using System.Threading.Tasks; + +namespace Yitter.IdGenerator +{ + /// + /// 雪花漂移算法 + /// + internal class SnowWorkerM1 : ISnowWorker + { + /// + /// 基础时间 + /// + protected readonly DateTime BaseTime; + + /// + /// 机器码 + /// + protected readonly ushort WorkerId = 0; + + /// + /// 机器码位长 + /// + protected readonly byte WorkerIdBitLength = 0; + + /// + /// 自增序列数位长 + /// + protected readonly byte SeqBitLength = 0; + + /// + /// 最大序列数(含) + /// + protected readonly int MaxSeqNumber = 0; + + /// + /// 最小序列数(含) + /// + protected readonly ushort MinSeqNumber = 0; + + /// + /// 最大漂移次数(含) + /// + protected int TopOverCostCount = 0; + + protected byte _TimestampShift = 0; + protected static object _SyncLock = new object(); + + protected ushort _CurrentSeqNumber = 0; + protected long _LastTimeTick = 0; // -1L + protected long _TurnBackTimeTick = 0; // -1L; + protected byte _TurnBackIndex = 0; + protected bool _IsOverCost = false; + protected int _OverCostCountInOneTerm = 0; + +#if DEBUG + protected int _GenCountInOneTerm = 0; + protected int _TermIndex = 0; +#endif + + public Action GenAction { get; set; } + + //private static long _StartTimeTick = 0; + //private static long _BaseTimeTick = 0; + + + public SnowWorkerM1(IdGeneratorOptions options) + { + // 1.BaseTime + if (options.BaseTime != DateTime.MinValue) + { + BaseTime = options.BaseTime; + } + + // 2.WorkerIdBitLength + if (options.WorkerIdBitLength == 0) + { + WorkerIdBitLength = 6; + } + else + { + WorkerIdBitLength = options.WorkerIdBitLength; + } + + // 3.WorkerId + WorkerId = options.WorkerId; + + // 4.SeqBitLength + if (options.SeqBitLength == 0) + { + SeqBitLength = 6; + } + else + { + SeqBitLength = options.SeqBitLength; + } + + // 5.MaxSeqNumber + if (options.MaxSeqNumber <= 0) + { + MaxSeqNumber = (1 << SeqBitLength) - 1; + } + else + { + MaxSeqNumber = options.MaxSeqNumber; + } + + // 6.MinSeqNumber + MinSeqNumber = options.MinSeqNumber; + + // 7.Others + TopOverCostCount = options.TopOverCostCount; + //if (TopOverCostCount == 0) + //{ + // TopOverCostCount = 2000; + //} + + _TimestampShift = (byte)(WorkerIdBitLength + SeqBitLength); + _CurrentSeqNumber = options.MinSeqNumber; + + //_BaseTimeTick = BaseTime.Ticks; + //_StartTimeTick = (long)(DateTime.UtcNow.Subtract(BaseTime).TotalMilliseconds) - Environment.TickCount; + } + +#if DEBUG + private void DoGenIdAction(OverCostActionArg arg) + { + //return; + Task.Run(() => + { + GenAction(arg); + }); + } + + private void BeginOverCostAction(in long useTimeTick) + { + if (GenAction == null) + { + return; + } + + DoGenIdAction(new OverCostActionArg( + WorkerId, + useTimeTick, + 1, + _OverCostCountInOneTerm, + _GenCountInOneTerm, + _TermIndex)); + } + + private void EndOverCostAction(in long useTimeTick) + { + //if (_TermIndex > 10000) + //{ + // _TermIndex = 0; + //} + + if (GenAction == null) + { + return; + } + + DoGenIdAction(new OverCostActionArg( + WorkerId, + useTimeTick, + 2, + _OverCostCountInOneTerm, + _GenCountInOneTerm, + _TermIndex)); + } + + private void BeginTurnBackAction(in long useTimeTick) + { + if (GenAction == null) + { + return; + } + + DoGenIdAction(new OverCostActionArg( + WorkerId, + useTimeTick, + 8, + 0, + 0, + _TurnBackIndex)); + } + + private void EndTurnBackAction(in long useTimeTick) + { + if (GenAction == null) + { + return; + } + + DoGenIdAction(new OverCostActionArg( + WorkerId, + useTimeTick, + 9, + 0, + 0, + _TurnBackIndex)); + } +#endif + + protected virtual long NextOverCostId() + { + long currentTimeTick = GetCurrentTimeTick(); + + if (currentTimeTick > _LastTimeTick) + { +#if DEBUG + EndOverCostAction(currentTimeTick); + _GenCountInOneTerm = 0; +#endif + _LastTimeTick = currentTimeTick; + _CurrentSeqNumber = MinSeqNumber; + _IsOverCost = false; + _OverCostCountInOneTerm = 0; + + return CalcId(_LastTimeTick); + } + + if (_OverCostCountInOneTerm >= TopOverCostCount) + { +#if DEBUG + EndOverCostAction(currentTimeTick); + _GenCountInOneTerm = 0; +#endif + // TODO: 在漂移终止,等待时间对齐时,如果发生时间回拨较长,则此处可能等待较长时间。可优化为:在漂移终止时增加时间回拨应对逻辑。(该情况发生概率低,暂不处理) + + _LastTimeTick = GetNextTimeTick(); + _CurrentSeqNumber = MinSeqNumber; + _IsOverCost = false; + _OverCostCountInOneTerm = 0; + + return CalcId(_LastTimeTick); + } + + if (_CurrentSeqNumber > MaxSeqNumber) + { +#if DEBUG + _GenCountInOneTerm++; +#endif + _LastTimeTick++; + _CurrentSeqNumber = MinSeqNumber; + _IsOverCost = true; + _OverCostCountInOneTerm++; + + return CalcId(_LastTimeTick); + } + +#if DEBUG + _GenCountInOneTerm++; +#endif + return CalcId(_LastTimeTick); + } + + protected virtual long NextNormalId() + { + long currentTimeTick = GetCurrentTimeTick(); + + if (currentTimeTick < _LastTimeTick) + { + if (_TurnBackTimeTick < 1) + { + _TurnBackTimeTick = _LastTimeTick - 1; + + _TurnBackIndex++; + // 每毫秒序列数的前5位是预留位,0用于手工新值,1-4是时间回拨次序 + // 支持4次回拨次序(避免回拨重叠导致ID重复),可无限次回拨(次序循环使用)。 + if (_TurnBackIndex > 4) + { + _TurnBackIndex = 1; + } + +#if DEBUG + BeginTurnBackAction(_TurnBackTimeTick); +#endif + } + + //Thread.Sleep(1); + return CalcTurnBackId(_TurnBackTimeTick); + } + + // 时间追平时,_TurnBackTimeTick清零 + if (_TurnBackTimeTick > 0) + { +#if DEBUG + EndTurnBackAction(_TurnBackTimeTick); +#endif + _TurnBackTimeTick = 0; + } + + if (currentTimeTick > _LastTimeTick) + { + _LastTimeTick = currentTimeTick; + _CurrentSeqNumber = MinSeqNumber; + + return CalcId(_LastTimeTick); + } + + if (_CurrentSeqNumber > MaxSeqNumber) + { +#if DEBUG + BeginOverCostAction(currentTimeTick); + _TermIndex++; + _GenCountInOneTerm = 1; +#endif + _OverCostCountInOneTerm = 1; + _LastTimeTick++; + _CurrentSeqNumber = MinSeqNumber; + _IsOverCost = true; + + return CalcId(_LastTimeTick); + } + + return CalcId(_LastTimeTick); + } + + protected virtual long CalcId(long useTimeTick) + { + var result = ((useTimeTick << _TimestampShift) + + ((long)WorkerId << SeqBitLength) + + (uint)_CurrentSeqNumber); + + _CurrentSeqNumber++; + return result; + } + + protected virtual long CalcTurnBackId(long useTimeTick) + { + var result = ((useTimeTick << _TimestampShift) + + ((long)WorkerId << SeqBitLength) + _TurnBackIndex); + + _TurnBackTimeTick--; + return result; + } + + protected virtual long GetCurrentTimeTick() + { + //return (long)(DateTime.UtcNow - BaseTime).Ticks; + //return (long)(_StartTimeTick + Environment.TickCount); + return (long)(DateTime.UtcNow - BaseTime).TotalMilliseconds; + } + + protected virtual long GetNextTimeTick() + { + long tempTimeTicker = GetCurrentTimeTick(); + + while (tempTimeTicker <= _LastTimeTick) + { + //Thread.Sleep(1); + SpinWait.SpinUntil(() => false, 1); + tempTimeTicker = GetCurrentTimeTick(); + } + + return tempTimeTicker; + } + + + public virtual long NextId() + { + lock (_SyncLock) + { + return _IsOverCost ? NextOverCostId() : NextNormalId(); + } + } + } +} diff --git a/Infrastructure/Snowflake/Core/SnowWorkerM2.cs b/Infrastructure/Snowflake/Core/SnowWorkerM2.cs new file mode 100644 index 0000000..ab2b2db --- /dev/null +++ b/Infrastructure/Snowflake/Core/SnowWorkerM2.cs @@ -0,0 +1,58 @@ +/* + * 版权属于:yitter(yitter@126.com) + * 开源地址:https://github.com/yitter/idgenerator + * 版权协议:MIT + * 版权说明:只要保留本版权,你可以免费使用、修改、分发本代码。 + * 免责条款:任何因为本代码产生的系统、法律、政治、宗教问题,均与版权所有者无关。 + * + */ + +using System; +using System.Collections.Generic; +using System.Text; + +namespace Yitter.IdGenerator +{ + /// + /// 常规雪花算法 + /// + internal class SnowWorkerM2 : SnowWorkerM1 + { + public SnowWorkerM2(IdGeneratorOptions options) : base(options) + { + + } + + public override long NextId() + { + lock (_SyncLock) + { + long currentTimeTick = GetCurrentTimeTick(); + + if (_LastTimeTick == currentTimeTick) + { + if (_CurrentSeqNumber++ > MaxSeqNumber) + { + _CurrentSeqNumber = MinSeqNumber; + currentTimeTick = GetNextTimeTick(); + } + } + else + { + _CurrentSeqNumber = MinSeqNumber; + } + + if (currentTimeTick < _LastTimeTick) + { + throw new Exception(string.Format("Time error for {0} milliseconds", _LastTimeTick - currentTimeTick)); + } + + _LastTimeTick = currentTimeTick; + var result = ((currentTimeTick << _TimestampShift) + ((long)WorkerId << SeqBitLength) + (uint)_CurrentSeqNumber); + + return result; + } + } + + } +} diff --git a/Infrastructure/Snowflake/Core/SnowWorkerM3.cs b/Infrastructure/Snowflake/Core/SnowWorkerM3.cs new file mode 100644 index 0000000..a70e97b --- /dev/null +++ b/Infrastructure/Snowflake/Core/SnowWorkerM3.cs @@ -0,0 +1,83 @@ +/* + * 版权属于:yitter(yitter@126.com) + * 开源地址:https://github.com/yitter/idgenerator + * 版权协议:MIT + * 版权说明:只要保留本版权,你可以免费使用、修改、分发本代码。 + * 免责条款:任何因为本代码产生的系统、法律、政治、宗教问题,均与版权所有者无关。 + * + */ + +using System; +using System.Threading; +using System.Threading.Tasks; + +namespace Yitter.IdGenerator +{ + /// + /// 雪花漂移算法(支持数据中心ID和秒级时间戳) + /// + internal class SnowWorkerM3 : SnowWorkerM1 + { + /// + /// 数据中心ID(默认0) + /// + protected readonly uint DataCenterId = 0; + + /// + /// 数据中心ID长度(默认0) + /// + protected readonly byte DataCenterIdBitLength = 0; + + /// + /// 时间戳类型(0-毫秒,1-秒),默认0 + /// + protected readonly byte TimestampType = 0; + + + public SnowWorkerM3(IdGeneratorOptions options) : base(options) + { + // 秒级时间戳类型 + TimestampType = options.TimestampType; + + // DataCenter相关 + DataCenterId = options.DataCenterId; + DataCenterIdBitLength = options.DataCenterIdBitLength; + + if (TimestampType == 1) + { + TopOverCostCount = 0; + } + _TimestampShift = (byte)(DataCenterIdBitLength + WorkerIdBitLength + SeqBitLength); + } + + protected override long CalcId(long useTimeTick) + { + var result = ((useTimeTick << _TimestampShift) + + ((long)DataCenterId << DataCenterIdBitLength) + + ((long)WorkerId << SeqBitLength) + + (long)_CurrentSeqNumber); + + _CurrentSeqNumber++; + return result; + } + + protected override long CalcTurnBackId(long useTimeTick) + { + var result = ((useTimeTick << _TimestampShift) + + ((long)DataCenterId << DataCenterIdBitLength) + + ((long)WorkerId << SeqBitLength) + + _TurnBackIndex); + + _TurnBackTimeTick--; + return result; + } + + protected override long GetCurrentTimeTick() + { + return TimestampType == 0 ? + (long)(DateTime.UtcNow - BaseTime).TotalMilliseconds : + (long)(DateTime.UtcNow - BaseTime).TotalSeconds; + } + + } +} diff --git a/Infrastructure/Snowflake/DefaultIdGenerator.cs b/Infrastructure/Snowflake/DefaultIdGenerator.cs new file mode 100644 index 0000000..26d3dba --- /dev/null +++ b/Infrastructure/Snowflake/DefaultIdGenerator.cs @@ -0,0 +1,130 @@ +/* + * 版权属于:yitter(yitter@126.com) + * 开源地址:https://github.com/yitter/idgenerator + * 版权协议:MIT + * 版权说明:只要保留本版权,你可以免费使用、修改、分发本代码。 + * 免责条款:任何因为本代码产生的系统、法律、政治、宗教问题,均与版权所有者无关。 + * + */ + +using System; +using System.Collections.Generic; +using System.Text; +using System.Threading; + +namespace Yitter.IdGenerator +{ + /// + /// 默认实现 + /// + public class DefaultIdGenerator : IIdGenerator + { + private ISnowWorker _SnowWorker { get; set; } + + //public Action GenIdActionAsync + //{ + // get => _SnowWorker.GenAction; + // set => _SnowWorker.GenAction = value; + //} + + + public DefaultIdGenerator(IdGeneratorOptions options) + { + if (options == null) + { + throw new ArgumentException("options error."); + } + + // 1.BaseTime + if (options.BaseTime < DateTime.Now.AddYears(-50) || options.BaseTime > DateTime.Now) + { + throw new ArgumentException("BaseTime error."); + } + + // 2.WorkerIdBitLength + int maxLength = options.TimestampType == 0 ? 22 : 31; // (秒级时间戳时放大到31位) + if (options.WorkerIdBitLength <= 0) + { + throw new ArgumentException("WorkerIdBitLength error.(range:[1, 21])"); + } + if (options.DataCenterIdBitLength + options.WorkerIdBitLength + options.SeqBitLength > maxLength) + { + throw new ArgumentException("error:DataCenterIdBitLength + WorkerIdBitLength + SeqBitLength <= " + maxLength); + } + + // 3.WorkerId & DataCenterId + var maxWorkerIdNumber = (1 << options.WorkerIdBitLength) - 1; + if (maxWorkerIdNumber == 0) + { + maxWorkerIdNumber = 63; + } + if (options.WorkerId < 0 || options.WorkerId > maxWorkerIdNumber) + { + throw new ArgumentException("WorkerId error. (range:[0, " + maxWorkerIdNumber + "]"); + } + + var maxDataCenterIdNumber = (1 << options.DataCenterIdBitLength) - 1; + if (options.DataCenterId < 0 || options.DataCenterId > maxDataCenterIdNumber) + { + throw new ArgumentException("DataCenterId error. (range:[0, " + maxDataCenterIdNumber + "]"); + } + + // 4.SeqBitLength + if (options.SeqBitLength < 2 || options.SeqBitLength > 21) + { + throw new ArgumentException("SeqBitLength error. (range:[2, 21])"); + } + + // 5.MaxSeqNumber + var maxSeqNumber = (1 << options.SeqBitLength) - 1; + if (maxSeqNumber == 0) + { + maxSeqNumber = 63; + } + if (options.MaxSeqNumber < 0 || options.MaxSeqNumber > maxSeqNumber) + { + throw new ArgumentException("MaxSeqNumber error. (range:[1, " + maxSeqNumber + "]"); + } + + // 6.MinSeqNumber + if (options.MinSeqNumber < 5 || options.MinSeqNumber > maxSeqNumber) + { + throw new ArgumentException("MinSeqNumber error. (range:[5, " + maxSeqNumber + "]"); + } + + // 7.TopOverCostCount + if (options.TopOverCostCount < 0 || options.TopOverCostCount > 10000) + { + throw new ArgumentException("TopOverCostCount error. (range:[0, 10000]"); + } + + switch (options.Method) + { + case 2: + _SnowWorker = new SnowWorkerM2(options); + break; + default: + if (options.DataCenterIdBitLength == 0 && options.TimestampType == 0) + { + _SnowWorker = new SnowWorkerM1(options); + } + else + { + _SnowWorker = new SnowWorkerM3(options); + } + break; + } + + if (options.Method != 2) + { + Thread.Sleep(500); + } + } + + + public long NewLong() + { + return _SnowWorker.NextId(); + } + } +} diff --git a/Infrastructure/Snowflake/YitIdHelper.cs b/Infrastructure/Snowflake/YitIdHelper.cs new file mode 100644 index 0000000..d5f1baa --- /dev/null +++ b/Infrastructure/Snowflake/YitIdHelper.cs @@ -0,0 +1,53 @@ +/* + * 版权属于:yitter(yitter@126.com) + * 开源地址:https://gitee.com/yitter/idgenerator + * 版权协议:MIT + * 版权说明:只要保留本版权,你可以免费使用、修改、分发本代码。 + * 免责条款:任何因为本代码产生的系统、法律、政治、宗教问题,均与版权所有者无关。 + * + */ + +using System; +using System.Collections.Generic; +using System.Text; + +namespace Yitter.IdGenerator +{ + /// + /// 这是一个调用的例子,默认情况下,单机集成者可以直接使用 NextId()。 + /// + public class YitIdHelper + { + private static IIdGenerator _IdGenInstance = null; + + public static IIdGenerator IdGenInstance => _IdGenInstance; + + /// + /// 设置参数,建议程序初始化时执行一次 + /// + /// + public static void SetIdGenerator(IdGeneratorOptions options) + { + _IdGenInstance = new DefaultIdGenerator(options); + } + + /// + /// 生成新的Id + /// 调用本方法前,请确保调用了 SetIdGenerator 方法做初始化。 + /// 否则将会初始化一个WorkerId为1的对象。 + /// + /// + public static long NextId() + { + if (_IdGenInstance == null) + { + _IdGenInstance = new DefaultIdGenerator( + new IdGeneratorOptions() { WorkerId = 1 } + ); + } + + return _IdGenInstance.NewLong(); + } + + } +} diff --git a/Infrastructure/Test/TestAutoMapper.cs b/Infrastructure/Test/TestAutoMapper.cs new file mode 100644 index 0000000..47824e9 --- /dev/null +++ b/Infrastructure/Test/TestAutoMapper.cs @@ -0,0 +1,50 @@ +using System; +using System.Collections.Generic; +using NUnit.Framework; + +namespace Infrastructure.Test +{ + class TestAutoMapper + { + [Test] + public void TestConvert() + { + var my = new MyClass + { + Name = "yubao" + }; + + var dest = my.MapTo(); + Console.WriteLine(JsonHelper.Instance.Serialize(dest)); + } + [Test] + public void TestConvertList() + { + var users = new List { + new MyClass {Name = "yubaolee1"} + , new MyClass{Name = "yubaolee2"} + + }; + + var dest = users.MapToList(); + Console.WriteLine(JsonHelper.Instance.Serialize(dest)); + + var dest2 = users.MapToList(); + Console.WriteLine(JsonHelper.Instance.Serialize(dest2)); + + } + } + + + class MyClass + { + public string Name { get; set; } + public string NickName { get; set; } + } + + class DestClass + { + public string Name { get; set; } + public int Age { get; set; } + } +} diff --git a/Infrastructure/Test/TestDynamicLinq.cs b/Infrastructure/Test/TestDynamicLinq.cs new file mode 100644 index 0000000..ddaef10 --- /dev/null +++ b/Infrastructure/Test/TestDynamicLinq.cs @@ -0,0 +1,54 @@ +using System; +using System.Linq.Expressions; +using NUnit.Framework; + +namespace Infrastructure.Test +{ + public class TestDynamicLinq + { + [Test] + public void Convert() + { + FilterGroup sub = new FilterGroup + { + Operation = "or" + }; + sub.Filters = new[] + { + new Filter {Key = "name", Value = "name", Contrast = "=="}, + new Filter {Key = "c3", Value = "10,20,30", Contrast = "in"} + }; + + FilterGroup filterGroup = new FilterGroup + { + Operation = "and" + }; + filterGroup.Filters = new[] + { + new Filter {Key = "c1", Value = "name", Contrast = "contains"}, + new Filter {Key = "10,20,30", Value = "40", Contrast = "intersect"} + }; + + filterGroup.Children = new[] + { + sub + }; + + var expression = DynamicLinq.ConvertGroup(filterGroup, + Expression.Parameter(typeof(TestOjb), "c")); + + Console.WriteLine(expression.ToString()); + + } + } + + public class TestOjb{ + public string c1 { get; set; } + public string c2 { get; set; } + public string c3 { get; set; } + public string c4 { get; set; } + public string c5 { get; set; } + public string c6 { get; set; } + public string c7 { get; set; } + } +} \ No newline at end of file diff --git a/Infrastructure/Test/TestSnowflake.cs b/Infrastructure/Test/TestSnowflake.cs new file mode 100644 index 0000000..40aa02c --- /dev/null +++ b/Infrastructure/Test/TestSnowflake.cs @@ -0,0 +1,29 @@ +using System; +using System.Collections.Generic; +using NUnit.Framework; +using Yitter.IdGenerator; + +namespace Infrastructure.Test +{ + class TestSnowflake + { + [Test] + public void Generate() + { + // 全局初始化设置WorkerId,默认最大2^16-1。(初始化过程全局只需一次,且必须最先设置) + var options = new IdGeneratorOptions() + { + Method = 1, + WorkerId = 1 + }; + + YitIdHelper.SetIdGenerator(options); + long newId = YitIdHelper.NextId(); + Console.WriteLine("====================================="); + Console.WriteLine("生成的 Id:" + newId); + } + + } + + +} diff --git a/Infrastructure/TreeItem.cs b/Infrastructure/TreeItem.cs new file mode 100644 index 0000000..c4db87b --- /dev/null +++ b/Infrastructure/TreeItem.cs @@ -0,0 +1,10 @@ +using System.Collections.Generic; + +namespace Infrastructure +{ + public class TreeItem + { + public T Item { get; set; } + public IEnumerable> Children { get; set; } + } +} \ No newline at end of file diff --git a/Infrastructure/Utilities/DESEncrypt.cs b/Infrastructure/Utilities/DESEncrypt.cs new file mode 100644 index 0000000..622db70 --- /dev/null +++ b/Infrastructure/Utilities/DESEncrypt.cs @@ -0,0 +1,104 @@ +using Infrastructure.Helpers; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Security.Cryptography; +using System.Text; +using System.Threading.Tasks; + +namespace Infrastructure.Utilities +{ + public static class DESEncrypt + { + private static readonly string key = "hopetry###***"; + + // + // 摘要: + // 加密 + // + // 参数: + // Text: + // 需要加密的内容 + public static string Encrypt(string Text) + { + return Encrypt(Text, key); + } + + // + // 摘要: + // 加密数据 + // + // 参数: + // Text: + // 需要加密的内容 + // + // sKey: + // 秘钥 + public static string Encrypt(string Text, string sKey) + { + DESCryptoServiceProvider dESCryptoServiceProvider = new DESCryptoServiceProvider(); + byte[] bytes = Encoding.Default.GetBytes(Text); + dESCryptoServiceProvider.Key = Encoding.ASCII.GetBytes(Md5Helper.Hash(sKey).ToUpper().Substring(0, 8)); + dESCryptoServiceProvider.IV = Encoding.ASCII.GetBytes(Md5Helper.Hash(sKey).ToUpper().Substring(0, 8)); + MemoryStream memoryStream = new MemoryStream(); + CryptoStream cryptoStream = new CryptoStream(memoryStream, dESCryptoServiceProvider.CreateEncryptor(), CryptoStreamMode.Write); + cryptoStream.Write(bytes, 0, bytes.Length); + cryptoStream.FlushFinalBlock(); + StringBuilder stringBuilder = new StringBuilder(); + byte[] array = memoryStream.ToArray(); + foreach (byte b in array) + { + stringBuilder.AppendFormat("{0:X2}", b); + } + + return stringBuilder.ToString(); + } + + // + // 摘要: + // 解密 + // + // 参数: + // Text: + // 需要解密的内容 + public static string Decrypt(string Text) + { + if (!string.IsNullOrEmpty(Text)) + { + return Decrypt(Text, key); + } + + return ""; + } + + // + // 摘要: + // 解密数据 + // + // 参数: + // Text: + // 需要解密的内容 + // + // sKey: + // 秘钥 + public static string Decrypt(string Text, string sKey) + { + DESCryptoServiceProvider dESCryptoServiceProvider = new DESCryptoServiceProvider(); + int num = Text.Length / 2; + byte[] array = new byte[num]; + for (int i = 0; i < num; i++) + { + int num2 = Convert.ToInt32(Text.Substring(i * 2, 2), 16); + array[i] = (byte)num2; + } + + dESCryptoServiceProvider.Key = Encoding.ASCII.GetBytes(Md5Helper.Hash(sKey).ToUpper().Substring(0, 8)); + dESCryptoServiceProvider.IV = Encoding.ASCII.GetBytes(Md5Helper.Hash(sKey).ToUpper().Substring(0, 8)); + MemoryStream memoryStream = new MemoryStream(); + CryptoStream cryptoStream = new CryptoStream(memoryStream, dESCryptoServiceProvider.CreateDecryptor(), CryptoStreamMode.Write); + cryptoStream.Write(array, 0, array.Length); + cryptoStream.FlushFinalBlock(); + return Encoding.Default.GetString(memoryStream.ToArray()); + } + } +} diff --git a/Infrastructure/Utilities/DynamicPropertyBag .cs b/Infrastructure/Utilities/DynamicPropertyBag .cs new file mode 100644 index 0000000..2e55f19 --- /dev/null +++ b/Infrastructure/Utilities/DynamicPropertyBag .cs @@ -0,0 +1,43 @@ +using System.Collections.Generic; +using System.Dynamic; +using System.IO; + +namespace Infrastructure.Utilities +{ + /// + /// 动态属性Bag + /// + public class DynamicPropertyBag : DynamicObject + { + private Dictionary storage = new Dictionary(); + + public override bool TryGetMember(GetMemberBinder binder, out object result) + { + if (storage.ContainsKey(binder.Name)) + { + result = storage[binder.Name]; + return true; + } + result = null; + return false; + } + + public override bool TrySetMember(SetMemberBinder binder, object value) + { + string key = binder.Name; + if (storage.ContainsKey(key)) + storage[key] = value; + else + storage.Add(key, value); + return true; + } + + public override string ToString() + { + StringWriter message = new StringWriter(); + foreach (var item in storage) + message.WriteLine("{0}:\t{1}", item.Key, item.Value); + return message.ToString(); + } + } +} diff --git a/Infrastructure/Utilities/HttpContextUtil.cs b/Infrastructure/Utilities/HttpContextUtil.cs new file mode 100644 index 0000000..ef98865 --- /dev/null +++ b/Infrastructure/Utilities/HttpContextUtil.cs @@ -0,0 +1,40 @@ +using Infrastructure.Extensions.AutofacManager; +using Microsoft.AspNetCore.Http; + +namespace Infrastructure.Utilities +{ + public static class HttpContextUtil + { + private static IHttpContextAccessor _accessor=AutofacContainerModule.GetService(); + + public static Microsoft.AspNetCore.Http.HttpContext Current => _accessor.HttpContext; + + /// + /// 获取租户ID + /// + /// + public static string GetTenantId(this IHttpContextAccessor accessor) + { + string tenantId = "OpenAuthDBContext"; + + if (accessor != null && accessor.HttpContext != null) + { + //读取多租户ID + var httpTenantId = accessor.HttpContext.Request.Query[Define.TENANT_ID]; + if (string.IsNullOrEmpty(httpTenantId)) + { + httpTenantId = accessor.HttpContext.Request.Headers[Define.TENANT_ID]; + } + + //如果没有租户id,或租户用的是默认的OpenAuthDBContext,则不做任何调整 + if (!string.IsNullOrEmpty(httpTenantId)) + { + tenantId = httpTenantId; + } + } + + return tenantId; + } + + } +} diff --git a/Infrastructure/Utilities/HttpManager.cs b/Infrastructure/Utilities/HttpManager.cs new file mode 100644 index 0000000..5ed3331 --- /dev/null +++ b/Infrastructure/Utilities/HttpManager.cs @@ -0,0 +1,70 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Net; +using System.Text; +using System.Threading.Tasks; + +namespace Infrastructure.Utilities +{ + public class HttpManager + { + public static Task HttpPostAsync(string url, string postData = null, string contentType = null, int timeOut = 30, Dictionary headers = null) + { + HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url); + request.Method = "POST"; + if (!string.IsNullOrEmpty(contentType)) + { + request.ContentType = contentType; + } + if (headers != null) + { + foreach (var header in headers) + request.Headers[header.Key] = header.Value; + } + + try + { + byte[] bytes = Encoding.UTF8.GetBytes(postData ?? ""); + using (Stream sendStream = request.GetRequestStream()) + { + sendStream.Write(bytes, 0, bytes.Length); + } + + using (HttpWebResponse response = (HttpWebResponse)request.GetResponse()) + { + Stream responseStream = response.GetResponseStream(); + StreamReader streamReader = new StreamReader(responseStream, Encoding.UTF8); + return streamReader.ReadToEndAsync(); + } + } + catch (Exception ex) + { + return Task.FromResult(ex.Message); + } + + } + public static Task HttpGetAsync(string url, Dictionary headers = null) + { + try + { + HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url); + if (headers != null) + { + foreach (var header in headers) + request.Headers[header.Key] = header.Value; + } + using (HttpWebResponse response = (HttpWebResponse)request.GetResponse()) + { + Stream responseStream = response.GetResponseStream(); + StreamReader streamReader = new StreamReader(responseStream, Encoding.UTF8); + return streamReader.ReadToEndAsync(); + } + } + catch (Exception ex) + { + return Task.FromResult(ex.Message); + } + } + } +} diff --git a/Infrastructure/Utilities/ProjectPath.cs b/Infrastructure/Utilities/ProjectPath.cs new file mode 100644 index 0000000..189f726 --- /dev/null +++ b/Infrastructure/Utilities/ProjectPath.cs @@ -0,0 +1,64 @@ +using System.IO; +using System.Linq; +using Infrastructure.Extensions; + +namespace Infrastructure.Utilities +{ + public class ProjectPath + { + /// + /// 获取web父目录所在位置 + /// + /// + public static DirectoryInfo GetProjectDirectoryInfo() + { + return GetProjectDirectoryInfo(new DirectoryInfo("".MapPath()), 1); + } + /// + /// 获取指定结尾的项目名称 + /// + /// + /// + public static string GetLastIndexOfDirectoryName(string lastIndexOfName) + { + string projectName = GetProjectDirectoryInfo()?.GetDirectories() + .Where(c => c.Name.LastIndexOf(lastIndexOfName) != -1).Select(x => x.Name).FirstOrDefault(); + if (string.IsNullOrEmpty(projectName)) + { + projectName = new DirectoryInfo("".MapPath()).GetFiles().Where(x => x.Name.LastIndexOf(lastIndexOfName + ".dll") != -1).FirstOrDefault().Name; + if (!string.IsNullOrEmpty(projectName)) + { + projectName = projectName.Replace(".dll", ""); + } + } + return projectName; + } + /// + /// 获取项目所在路径 + /// + /// + /// + private static DirectoryInfo GetProjectDirectoryInfo(DirectoryInfo directoryInfo, int findCount) + { + if (directoryInfo == null) + { + return null; + } + if (directoryInfo.Exists + && directoryInfo.GetDirectories().Where(x => x.Name.LastIndexOf(".App") != -1).FirstOrDefault() != null) + { + return directoryInfo; + } + if (findCount < 7) + { + findCount++; + DirectoryInfo dir = GetProjectDirectoryInfo(directoryInfo.Parent, findCount); + if (dir != null) + { + return dir; + } + } + return null; + } + } +} diff --git a/Infrastructure/Utilities/ResponseMsg.cs b/Infrastructure/Utilities/ResponseMsg.cs new file mode 100644 index 0000000..4a7ed46 --- /dev/null +++ b/Infrastructure/Utilities/ResponseMsg.cs @@ -0,0 +1,59 @@ +using Infrastructure.Const; + + namespace Infrastructure.Utilities +{ + public static class ResponseMsg + { + public static string GetMsg(this ResponseType responseType) + { + string msg; + switch (responseType) + { + case ResponseType.LoginExpiration: + msg = "登陆已过期,请重新登陆"; break; + case ResponseType.TokenExpiration: + msg = "Token已过期,请重新登陆"; break; + case ResponseType.AccountLocked: + msg = "帐号已被锁定"; break; + case ResponseType.LoginSuccess: + msg = "登陆成功"; break; + case ResponseType.ParametersLack: + msg = "参数不完整"; break; + case ResponseType.NoPermissions: + msg = "没有权限操作"; break; + case ResponseType.NoRolePermissions: + msg = "角色没有权限操作"; break; + case ResponseType.ServerError: + msg = "服务器好像出了点问题....."; break; + case ResponseType.LoginError: + msg = "用户名或密码错误"; break; + case ResponseType.SaveSuccess: + msg = "保存成功"; break; + case ResponseType.NoKey: + msg = "没有主键不能编辑"; break; + case ResponseType.NoKeyDel: + msg = "没有主键不能删除"; break; + case ResponseType.KeyError: + msg = "主键不正确或没有传入主键"; break; + case ResponseType.EidtSuccess: + msg = "编辑成功"; break; + case ResponseType.DelSuccess: + msg = "删除成功"; break; + case ResponseType.RegisterSuccess: + msg = "注册成功"; break; + case ResponseType.AuditSuccess: + msg = "审核成功"; break; + case ResponseType.ModifyPwdSuccess: + msg = "密码修改成功"; break; + case ResponseType.OperSuccess: + msg = "操作成功"; break; + case ResponseType.PINError: + msg = "验证码不正确"; break; + + default: msg = responseType.ToString(); break; + } + return msg; + } + + } +} diff --git a/Infrastructure/Utilities/UriUtil.cs b/Infrastructure/Utilities/UriUtil.cs new file mode 100644 index 0000000..b931c9d --- /dev/null +++ b/Infrastructure/Utilities/UriUtil.cs @@ -0,0 +1,62 @@ +// *********************************************************************** +// Assembly : Infrastructure +// Author : yubaolee +// Created : 06-21-2016 +// +// Last Modified By : yubaolee +// Last Modified On : 06-22-2016 +// Contact : +// File: UriUtil.cs +// *********************************************************************** + +using System; +using System.Collections.Specialized; +using System.Web; + +namespace Infrastructure.Utilities +{ + /// + /// URl帮助类 + /// + public class UriUtil + { + /// + /// 在URL后面追加参数 + /// + /// + /// + /// + /// + public static string GetAppendedQueryString(string url, string key, string value) + { + if (url.Contains("?")) + { + url = string.Format("{0}&{1}={2}", url, key, value); + } + else + { + url = string.Format("{0}?{1}={2}", url, key, value); + } + + return url; + } + + public static string RemoveParameter(string url, string key) + { + + url = url.ToLower(); + key = key.ToLower(); + if (!url.Contains(key + "=")) return url; + + Uri uri = new Uri(url); + NameValueCollection collection = HttpUtility.ParseQueryString(uri.Query); + if (collection.Count == 0) return url; + + var val = collection[key]; + string fragmentToRemove = string.Format("{0}={1}",key , val); + + String result = url.ToLower().Replace("&" + fragmentToRemove, string.Empty).Replace("?" + fragmentToRemove, string.Empty); + return result; + } + } +} \ No newline at end of file diff --git a/Infrastructure/Utilities/WebResponseContent.cs b/Infrastructure/Utilities/WebResponseContent.cs new file mode 100644 index 0000000..644704a --- /dev/null +++ b/Infrastructure/Utilities/WebResponseContent.cs @@ -0,0 +1,81 @@ +using Infrastructure.Const; + +namespace Infrastructure.Utilities +{ + public class WebResponseContent : Response + { + public WebResponseContent() + { + Code = 200; + Message = "操作成功"; + } + public WebResponseContent(bool status) + { + this.Status = status; + } + public bool Status { get; set; } + public object Result { get; set; } + + public WebResponseContent OK() + { + this.Status = true; + return this; + } + + public static WebResponseContent Instance + { + get { return new WebResponseContent(); } + } + public WebResponseContent OK(string message = null,object data=null) + { + this.Status = true; + this.Message = message; + this.Result = data; + return this; + } + public WebResponseContent OK(ResponseType responseType) + { + return Set(responseType, true); + } + public WebResponseContent Error(string message = null) + { + this.Status = false; + this.Message = message; + return this; + } + public WebResponseContent Error(ResponseType responseType) + { + return Set(responseType, false); + } + public WebResponseContent Set(ResponseType responseType) + { + bool? b = null; + return this.Set(responseType, b); + } + public WebResponseContent Set(ResponseType responseType, bool? status) + { + return this.Set(responseType, null, status); + } + public WebResponseContent Set(ResponseType responseType, string msg) + { + bool? b = null; + return this.Set(responseType, msg, b); + } + public WebResponseContent Set(ResponseType responseType, string msg, bool? status) + { + if (status != null) + { + this.Status = (bool)status; + } + this.Code = (int)responseType; + if (!string.IsNullOrEmpty(msg)) + { + Message = msg; + return this; + } + Message = responseType.GetMsg(); + return this; + } + + } +} diff --git a/Infrastructure/log4net.config b/Infrastructure/log4net.config new file mode 100644 index 0000000..dc7a861 --- /dev/null +++ b/Infrastructure/log4net.config @@ -0,0 +1,57 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/OpenAuth.App/AuthContextFactory.cs b/OpenAuth.App/AuthContextFactory.cs new file mode 100644 index 0000000..759b791 --- /dev/null +++ b/OpenAuth.App/AuthContextFactory.cs @@ -0,0 +1,48 @@ +using Infrastructure; +using OpenAuth.Repository; +using SqlSugar; + +namespace OpenAuth.App +{ + /// + /// 加载用户所有可访问的资源/机构/模块 + /// 李玉宝新增于2016-07-19 10:53:30 + /// + public class AuthContextFactory + { + private SystemAuthStrategy _systemAuth; + private NormalAuthStrategy _normalAuthStrategy; + private readonly ISugarUnitOfWork _unitWork; + + public AuthContextFactory(SystemAuthStrategy sysStrategy + , NormalAuthStrategy normalAuthStrategy + , ISugarUnitOfWork unitWork) + { + _systemAuth = sysStrategy; + _normalAuthStrategy = normalAuthStrategy; + _unitWork = unitWork; + } + + public AuthStrategyContext GetAuthStrategyContext(string username) + { + if (string.IsNullOrEmpty(username)) return null; + + IAuthStrategy service = null; + if (username == Define.SYSTEM_USERNAME) + { + service = _systemAuth; + } + else + { + service = _normalAuthStrategy; + //unitWork 是否自己释放 + using (var uow = _unitWork.CreateContext()) + { + service.User = uow.User.GetFirst(u => u.Account == username); + } + } + + return new AuthStrategyContext(service); + } + } +} \ No newline at end of file diff --git a/OpenAuth.App/AutofacExt.cs b/OpenAuth.App/AutofacExt.cs new file mode 100644 index 0000000..838e004 --- /dev/null +++ b/OpenAuth.App/AutofacExt.cs @@ -0,0 +1,104 @@ +using System.Reflection; +using System.Runtime.Loader; +using Autofac; +using Autofac.Extras.Quartz; +using ce.autofac.extension; +using Infrastructure.Cache; +using Infrastructure.Extensions.AutofacManager; +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.DependencyModel; +using OpenAuth.App.Interface; +using OpenAuth.App.SSO; +using OpenAuth.Repository; +using SqlSugar; + +namespace OpenAuth.App +{ + public static class AutofacExt + { + public static void InitAutofac(ContainerBuilder builder) + { + //注册数据库基础操作和工作单元 + builder.RegisterGeneric(typeof(SugarRepositiry<>)).As(typeof(ISimpleClient<>)).InstancePerLifetimeScope(); + //builder.RegisterGeneric(typeof(SimpleClient<>)).As(typeof(ISimpleClient<>)).InstancePerLifetimeScope(); + //builder.RegisterGeneric(typeof(SugarRepositiry<>)).As(typeof(ISugarRepositiry<>)).InstancePerLifetimeScope(); + //builder.RegisterGeneric(typeof(SugarUnitOfWork<>)).As(typeof(ISugarUnitOfWork<>)).InstancePerLifetimeScope(); + + //注入授权 + builder.RegisterType(typeof(LocalAuth)).As(typeof(IAuth)).InstancePerLifetimeScope(); + + //注册app层 + builder.RegisterAssemblyTypes(Assembly.GetExecutingAssembly()).InstancePerLifetimeScope(); + + builder.RegisterType(typeof(RedisCacheContext)).As(typeof(ICacheContext)).InstancePerLifetimeScope(); + //builder.RegisterType(typeof(CacheContext)).As(typeof(ICacheContext)); + builder.RegisterType(typeof(HttpContextAccessor)).As(typeof(IHttpContextAccessor)) + .InstancePerLifetimeScope(); + try + { + foreach (Assembly assembly in ce.autofac.extension.Extensions.GetAssemblies()) + { + foreach (Type type in assembly.GetTypes().Where((Func)(t => + typeof(IBLL).IsAssignableFrom(t) && + t.GetCustomAttribute() != null))) + { + BLLNameAttribute customAttribute = type.GetCustomAttribute(); + Type serviceType = + type.GetInterfaces().FirstOrDefault( + (Func)(t => typeof(IBLL).IsAssignableFrom(t))); + if (serviceType != null) + { + builder.RegisterType(type).AsImplementedInterfaces() + .Named(customAttribute.BLLName, serviceType); + builder.RegisterType(type).Named(customAttribute.BLLName, type); + } + } + } + } + catch (Exception e) + { + Console.WriteLine(e); + throw; + } + + InitDependency(builder); + + builder.RegisterModule(new QuartzAutofacFactoryModule()); + } + + + /// + /// 注入所有继承了IDependency接口 + /// + /// + private static void InitDependency(ContainerBuilder builder) + { + Type baseType = typeof(IDependency); + var compilationLibrary = DependencyContext.Default + .CompileLibraries + .Where(x => !x.Serviceable + && x.Type == "project") + .ToList(); + var count1 = compilationLibrary.Count; + List assemblyList = new List(); + + foreach (var _compilation in compilationLibrary) + { + try + { + assemblyList.Add( + AssemblyLoadContext.Default.LoadFromAssemblyName(new AssemblyName(_compilation.Name))); + } + catch (Exception ex) + { + Console.WriteLine(_compilation.Name + ex.Message); + } + } + + builder.RegisterAssemblyTypes(assemblyList.ToArray()) + .Where(type => baseType.IsAssignableFrom(type) && !type.IsAbstract) + .AsSelf().AsImplementedInterfaces() + .InstancePerLifetimeScope(); + } + } +} \ No newline at end of file diff --git a/OpenAuth.App/BaseApp/AppManager/AppManager.cs b/OpenAuth.App/BaseApp/AppManager/AppManager.cs new file mode 100644 index 0000000..ff95b0f --- /dev/null +++ b/OpenAuth.App/BaseApp/AppManager/AppManager.cs @@ -0,0 +1,57 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using OpenAuth.App.Base; +using OpenAuth.App.BaseApp.Base; +using OpenAuth.App.Interface; +using OpenAuth.App.Request; +using OpenAuth.Repository; +using OpenAuth.Repository.Domain; +using SqlSugar; + +namespace OpenAuth.App +{ + /// + /// 分类管理 + /// + public class AppManager : SqlSugarBaseApp + { + + public AppManager(ISugarUnitOfWork unitWork, ISimpleClient repository, IAuth auth) : base(unitWork, repository, auth) + { + + } + + public void Add(SysApplication Application) + { + if (Application.Id==0) + { + Application.Id = Yitter.IdGenerator.YitIdHelper.NextId(); + } + + Repository.Insert(Application); + } + + public void Update(SysApplication Application) + { + Repository.Update(Application); + } + + + public async Task> GetList(QueryAppListReq request) + { + return await Repository.GetListAsync(); + } + + + public SysApplication GetByAppKey(string modelAppKey) + { + return Repository.GetFirst(u => u.AppSecret == modelAppKey); + } + + public void Delete(string[] ids) + { + Repository.DeleteByIds(ids); + } + } +} \ No newline at end of file diff --git a/OpenAuth.App/BaseApp/AppManager/Request/QueryAppListReq.cs b/OpenAuth.App/BaseApp/AppManager/Request/QueryAppListReq.cs new file mode 100644 index 0000000..20081a0 --- /dev/null +++ b/OpenAuth.App/BaseApp/AppManager/Request/QueryAppListReq.cs @@ -0,0 +1,7 @@ +namespace OpenAuth.App.Request +{ + public class QueryAppListReq : PageReq + { + + } +} diff --git a/OpenAuth.App/BaseApp/AuthStrategies/AuthStrategyContext.cs b/OpenAuth.App/BaseApp/AuthStrategies/AuthStrategyContext.cs new file mode 100644 index 0000000..e92db63 --- /dev/null +++ b/OpenAuth.App/BaseApp/AuthStrategies/AuthStrategyContext.cs @@ -0,0 +1,56 @@ +using System; +using System.Collections.Generic; +using Infrastructure; +using OpenAuth.App.Response; +using OpenAuth.Repository.Domain; + +namespace OpenAuth.App +{ + /// + /// 授权策略上下文,一个典型的策略模式 + /// + public class AuthStrategyContext + { + private readonly IAuthStrategy _strategy; + public AuthStrategyContext(IAuthStrategy strategy) + { + this._strategy = strategy; + } + + public SysUser User + { + get { return _strategy.User; } + } + + public List Modules + { + get { return _strategy.Modules; } + } + + public List ModuleElements + { + get { return _strategy.ModuleElements; } + } + + public List Roles + { + get { return _strategy.Roles; } + } + + public List Resources + { + get { return _strategy.Resources; } + } + + public List Orgs + { + get { return _strategy.Orgs; } + } + + public List Positions + { + get { return _strategy.Positions; } + } + } + +} diff --git a/OpenAuth.App/BaseApp/AuthStrategies/NormalAuthStrategy.cs b/OpenAuth.App/BaseApp/AuthStrategies/NormalAuthStrategy.cs new file mode 100644 index 0000000..9ddec30 --- /dev/null +++ b/OpenAuth.App/BaseApp/AuthStrategies/NormalAuthStrategy.cs @@ -0,0 +1,123 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using Infrastructure; +using Infrastructure.Extensions; +using OpenAuth.App.Base; +using OpenAuth.App.BaseApp.Base; +using OpenAuth.App.BasicQueryService; +using OpenAuth.App.Response; +using OpenAuth.Repository; +using OpenAuth.Repository.Domain; +using SqlSugar; +using StackExchange.Redis; + +namespace OpenAuth.App +{ + /// + /// 普通用户授权策略 + /// + public class NormalAuthStrategy : SqlSugarBaseApp, IAuthStrategy + { + ISqlSugarClient client; + UserManager manager; + protected SysUser _user; + private List _userRoleIds; //用户角色ID + + public NormalAuthStrategy( + ISugarUnitOfWork unitWork, + ISimpleClient repository, + UserManager userManager + ) : base(unitWork, repository, null) + { + client = base.Repository.AsSugarClient(); + manager = userManager; + } + + + + public List Modules + { + get + { + //var moduleIds = Repository.ChangeRepository>().GetList( + // u => + // (_userRoleIds.Contains(u.RoleId))).Select(u => u.ModuleId); + //var modules = Repository.ChangeRepository>().AsQueryable().Where(u => moduleIds.Contains(u.Id)) + // .Select(module => new ModuleView + // { + // SortNo = module.SortNo, + // Name = module.Name, + // Code = module.Code, + // CascadeId = module.CascadeId, + // Id = module.Id, + // IconName = module.IconName, + // Url = module.Url, + // ParentId = module.ParentId, + // ParentName = module.ParentName, + // IsSys = module.IsSys, + // Status = module.Status + // }).ToList(); + + var modules = manager.RoleModules(_userRoleIds).Distinct().OrderBy((r, m) => m.SortNo).Select().ToList(); + + var usermoduleelements = ModuleElements; + + foreach (var module in modules) + { + module.Elements = usermoduleelements.Where(u => u.ModuleId == module.Id).DistinctBy(u=>u.Id).ToList(); + } + + return modules; + } + } + + public List ModuleElements + { + get + { + return manager.RoleModuleElements(_userRoleIds).OrderBy((r, m) => m.Sort).Select((r, m) => m).ToList(); + } + } + + public List Roles + { + get { return manager.UserRoles(_user.Id).Select((u, r) => r).ToList(); } + } + + public List Resources + { + get + { + var resourceIds = base.Repository.ChangeRepository>() + .GetList(u => _userRoleIds.Contains(u.RoleId)).Select(u => u.ResourceId); + return base.Repository.ChangeRepository>().GetList(u => resourceIds.Contains(u.Id)).ToList(); + } + } + + public List Orgs + { + get + { + return manager.UserOrgs(_user.Id).Select((u, o) => o).ToList(); + } + } + + public List Positions + { + get { return manager.UserPositons(_user.Id).Select((u, p) => p).ToList(); } + } + + public SysUser User + { + get { return _user; } + set + { + _user = value; + _userRoleIds = manager.UserRoles(_user.Id).Select((u, r) => r.Id).ToList(); + } + } + + } +} \ No newline at end of file diff --git a/OpenAuth.App/BaseApp/AuthStrategies/SystemAuthStrategy.cs b/OpenAuth.App/BaseApp/AuthStrategies/SystemAuthStrategy.cs new file mode 100644 index 0000000..648275e --- /dev/null +++ b/OpenAuth.App/BaseApp/AuthStrategies/SystemAuthStrategy.cs @@ -0,0 +1,83 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Infrastructure; +using OpenAuth.App.Base; +using OpenAuth.App.BaseApp.Base; +using OpenAuth.App.Response; +using OpenAuth.Repository; +using OpenAuth.Repository.Domain; +using SqlSugar; + +namespace OpenAuth.App +{ + /// + /// 领域服务 + /// 超级管理员权限 + /// 超级管理员使用-1,可以根据需要修改 + /// + public class SystemAuthStrategy : SqlSugarBaseApp, IAuthStrategy + { + protected SysUser _user; + + public List Modules + { + get + { + return Repository.ChangeRepository>().AsQueryable() + .Includes(a => a.Elements) + .Select(a => new ModuleView + { + Elements = a.Elements + }, true) + .ToList(); + } + } + + public List Roles + { + get { return Repository.ChangeRepository>().GetList(); } + } + + public List ModuleElements + { + get { return Repository.ChangeRepository>().GetList(); } + } + + public List Resources + { + get { return Repository.ChangeRepository>().GetList(); } + } + + public List Orgs + { + get { return Repository.ChangeRepository>().GetList(); } + } + + public List Positions + { + get { return Repository.ChangeRepository>().GetList(); } + } + + public SysUser User + { + get { return _user; } + set //禁止外部设置 + { + throw new Exception("超级管理员,禁止设置用户"); + } + } + + + + public SystemAuthStrategy(ISugarUnitOfWork unitWork, ISimpleClient repository) : base(unitWork, repository, null) + { + _user = new SysUser + { + Account = Define.SYSTEM_USERNAME, + Name = "超级管理员", + Id = -1 + }; + } + } +} \ No newline at end of file diff --git a/OpenAuth.App/BaseApp/Base/IdRequest.cs b/OpenAuth.App/BaseApp/Base/IdRequest.cs new file mode 100644 index 0000000..6f08a2e --- /dev/null +++ b/OpenAuth.App/BaseApp/Base/IdRequest.cs @@ -0,0 +1,14 @@ +namespace OpenAuth.App.Request +{ + /// + /// 请求参数中只有Id + /// + /// + public class IdRequest + { + /// + /// 操作Id + /// + public T Id { get; set; } + } +} diff --git a/OpenAuth.App/BaseApp/Base/PageReq.cs b/OpenAuth.App/BaseApp/Base/PageReq.cs new file mode 100644 index 0000000..8fcc1c7 --- /dev/null +++ b/OpenAuth.App/BaseApp/Base/PageReq.cs @@ -0,0 +1,24 @@ +namespace OpenAuth.App.Request +{ + public class PageReq + { + /// + /// 页码 + /// + /// 1 + public int page { get; set; } + /// + /// 每页条数 + /// + /// 10 + public int limit { get; set; } + + public string key { get; set; } + + public PageReq() + { + page = 1; + limit = 10; + } + } +} diff --git a/OpenAuth.App/BaseApp/Base/SqlSugarBaseApp.cs b/OpenAuth.App/BaseApp/Base/SqlSugarBaseApp.cs new file mode 100644 index 0000000..7817199 --- /dev/null +++ b/OpenAuth.App/BaseApp/Base/SqlSugarBaseApp.cs @@ -0,0 +1,122 @@ +using Microsoft.Extensions.Configuration; +using OpenAuth.App.FormScheme.FormHelpers; +using OpenAuth.App.Interface; +using SqlSugar; + +namespace OpenAuth.App.BaseApp.Base +{ + public abstract class SqlSugarBaseApp + where T : class, new() + where TDbContext : SugarUnitOfWork, new() + { + + /// + /// 用于普通的数据库操作 + /// + protected ISimpleClient Repository; + /// + /// 用于事务操作 + /// + protected ISugarUnitOfWork UnitWork; + + protected IAuth _auth; + + + public SqlSugarBaseApp(ISugarUnitOfWork unitWork, ISimpleClient repository, IAuth auth) + { + UnitWork = unitWork; + Repository = repository; + _auth = auth; + } + + public SqlSugarClient CodeClient(string code, IConfiguration config) + { + if (string.IsNullOrEmpty(code) || code == "hcsystemdb") + { + return new SqlSugarClient(new ConnectionConfig() + { + DbType = SqlSugar.DbType.PostgreSQL, + ConnectionString = config.GetConnectionString("OpenAuthDBContext"), + IsAutoCloseConnection = true, + MoreSettings = new SqlSugar.ConnMoreSettings() + { + //PgSqlIsAutoToLower = false, + //PgSqlIsAutoToLowerCodeFirst = false, + IsAutoToUpper = false, + + DatabaseModel = DbType.PostgreSQL + } + }, + db => + { + //单例参数配置,所有上下文生效 + db.Aop.OnLogExecuting = (sql, pars) => + { + //Console.WriteLine(sql + "\r\n" + + //db.Utilities.SerializeObject(pars.ToDictionary(it => it.ParameterName, it => it.Value))); + //Console.WriteLine(); + }; + }); + } + else + { + var link = UnitWork.Db.Queryable().Where(r => r.DBName == code).First(); + if (link != null) + { + return new SqlSugarClient(new ConnectionConfig() + { + DbType = DBCommonHelper.GetDbType(link.DBType), + ConnectionString = link.DBConnection, + IsAutoCloseConnection = true, + MoreSettings = new SqlSugar.ConnMoreSettings() + { + //PgSqlIsAutoToLower = false, + //PgSqlIsAutoToLowerCodeFirst = false, + IsAutoToUpper = false, + + DatabaseModel = DbType.PostgreSQL + } + }, + db => + { + //单例参数配置,所有上下文生效 + db.Aop.OnLogExecuting = (sql, pars) => + { + //Console.WriteLine(sql + "\r\n" + + //db.Utilities.SerializeObject(pars.ToDictionary(it => it.ParameterName, it => it.Value))); + //Console.WriteLine(); + }; + }); + } + else + { + throw new Exception("此编码找不到对应数据库:" + code); + } + } + } + + public SqlSugarClient TestLinkClient(string conn, string dbtype) + { + var client = new SqlSugarClient(new ConnectionConfig() + { + DbType = DBCommonHelper.GetDbType(dbtype), + ConnectionString = conn, + IsAutoCloseConnection = true, + MoreSettings = new SqlSugar.ConnMoreSettings() + { + IsAutoToUpper = false, + DatabaseModel = DbType.PostgreSQL + } + }, + db => + { + //单例参数配置,所有上下文生效 + db.Aop.OnLogExecuting = (sql, pars) => + { + }; + }); + return client; + } + + } +} diff --git a/OpenAuth.App/BaseApp/Base/TableData.cs b/OpenAuth.App/BaseApp/Base/TableData.cs new file mode 100644 index 0000000..7318adf --- /dev/null +++ b/OpenAuth.App/BaseApp/Base/TableData.cs @@ -0,0 +1,33 @@ + +namespace OpenAuth.App.Response +{ + /// + /// table的返回数据 + /// + public class TableData + { + /// + /// 状态码 + /// + public int code { get; set; } + /// + /// 操作消息 + /// + public string msg { get; set; } + + /// + /// 总记录条数 + /// + public int count { get; set; } + /// + /// 数据内容 + /// + public dynamic data { get; set; } + + public TableData() + { + code = 200; + msg = "加载成功"; + } + } +} \ No newline at end of file diff --git a/OpenAuth.App/BaseApp/Base/TableResp.cs b/OpenAuth.App/BaseApp/Base/TableResp.cs new file mode 100644 index 0000000..08cae99 --- /dev/null +++ b/OpenAuth.App/BaseApp/Base/TableResp.cs @@ -0,0 +1,42 @@ +using System.Collections.Generic; +using Infrastructure; + +namespace OpenAuth.App.Response +{ + /// + /// 返回确定类型的表格数据,可以为swagger提供精准的注释 + /// + public class TableResp + { + /// + /// 状态码 + /// + public int code { get; set; } + /// + /// 操作消息 + /// + public string msg { get; set; } + + /// + /// 总记录条数 + /// + public int count { get; set; } + + /// + /// 数据内容 + /// + public List data { get; set; } + + /// + /// 返回的列表头信息 + /// + public List columnHeaders { get; set; } + + public TableResp() + { + code = 200; + msg = "加载成功"; + columnHeaders = new List(); + } + } +} \ No newline at end of file diff --git a/OpenAuth.App/BaseApp/Base/Tree/LongTreeApp.cs b/OpenAuth.App/BaseApp/Base/Tree/LongTreeApp.cs new file mode 100644 index 0000000..23c88f9 --- /dev/null +++ b/OpenAuth.App/BaseApp/Base/Tree/LongTreeApp.cs @@ -0,0 +1,31 @@ +using System.Linq; +using Infrastructure; +using OpenAuth.App.Interface; +using OpenAuth.Repository; +using OpenAuth.Repository.Core; +using SqlSugar; + +namespace OpenAuth.App.Base +{ + public class LongTreeApp : TreeApp + where T : TreeEntity, new() + where TDbContext : SugarUnitOfWork, new() + { + public LongTreeApp(ISugarUnitOfWork unitWork, ISimpleClient repository, IAuth auth) + : base(unitWork, repository, auth) + { + } + + protected override long GetDefaultId() + { + + return 0; // Define your default Id value for long type + } + + protected override string GetDefaultCascadeId() + { + return ""; + //return "0."; // Define your default CascadeId value for long type + } + } +} \ No newline at end of file diff --git a/OpenAuth.App/BaseApp/Base/Tree/StringTreeApp.cs b/OpenAuth.App/BaseApp/Base/Tree/StringTreeApp.cs new file mode 100644 index 0000000..787b2cf --- /dev/null +++ b/OpenAuth.App/BaseApp/Base/Tree/StringTreeApp.cs @@ -0,0 +1,35 @@ +using Infrastructure.Extensions; +using OpenAuth.App.Interface; +using OpenAuth.Repository; +using OpenAuth.Repository.Core; +using SqlSugar; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace OpenAuth.App.Base +{ + public class StringTreeApp : TreeApp + where T : TreeEntity, new() + where TDbContext : SugarUnitOfWork, new() + { + public StringTreeApp(ISugarUnitOfWork unitWork, ISimpleClient repository, IAuth auth) + : base(unitWork, repository, auth) + { + } + + protected override string GetDefaultId() + { + return "0"; + /* return ""; */// Define your default Id value for string type + } + + protected override string GetDefaultCascadeId() + { + var max = base.Repository.AsSugarClient().Queryable().Where(a => a.ParentId == "0").Max(a => SqlFunc.ToInt32(a.CascadeId)); + return (max+1).ToString(); // Define your default CascadeId value for string type + } + } +} diff --git a/OpenAuth.App/BaseApp/Base/Tree/TreeApp.cs b/OpenAuth.App/BaseApp/Base/Tree/TreeApp.cs new file mode 100644 index 0000000..2f50aed --- /dev/null +++ b/OpenAuth.App/BaseApp/Base/Tree/TreeApp.cs @@ -0,0 +1,146 @@ +using DocumentFormat.OpenXml.EMMA; +using Infrastructure.Extensions; +using OpenAuth.App.Interface; +using OpenAuth.Repository; +using OpenAuth.Repository.Core; +using OpenAuth.Repository.Domain; +using SqlSugar; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Text; +using System.Threading.Tasks; +using OpenAuth.App.BaseApp.Base; + +namespace OpenAuth.App.Base +{ + public abstract class TreeApp : SqlSugarBaseApp + where T : TreeEntity, new() + where TDbContext : SugarUnitOfWork, new() + { + protected TreeApp(ISugarUnitOfWork unitWork, ISimpleClient repository, IAuth auth) : base(unitWork, repository, auth) + { + } + + protected abstract TIdType GetDefaultId(); + protected abstract string GetDefaultCascadeId(); + + protected virtual void CalculateCascade(T entity) + { + if (EqualityComparer.Default.Equals(entity.ParentId, GetDefaultId())) + { + var entityType = entity.GetType(); + var baseType = entityType.BaseType; + + if (baseType == typeof(TreeEntity)) + { + var parentIdProperty = entityType.GetProperty("ParentId"); + parentIdProperty.SetValue(entity, "0", null); + } + else + { + entity.ParentId = default; + } + + //entity.ParentId = default; + + } + + string cascadeId; + int currentCascadeId = 1; + var sameLevels = GetSameLevelEntities(entity); + + foreach (var obj in sameLevels) + { + int objCascadeId = int.Parse(obj.CascadeId.TrimEnd('.').Split('.').Last()); + currentCascadeId = Math.Max(currentCascadeId, objCascadeId + 1); + } + + + + + + //if (!EqualityComparer.Default.Equals(entity.ParentId, GetDefaultId())) + //{ + // var parentOrg = GetParentOrganization(entity.ParentId); + // cascadeId = parentOrg.CascadeId + currentCascadeId; + // entity.ParentName = parentOrg.Name; + //} + if (!Object.Equals(entity.ParentId, GetDefaultId())) + { + var parentOrg = GetParentOrganization(entity.ParentId); + cascadeId = parentOrg.CascadeId + "." + currentCascadeId; + entity.ParentName = parentOrg.Name; + } + else + { + //cascadeId = GetDefaultCascadeId() + currentCascadeId + "."; + //cascadeId = GetDefaultCascadeId() + currentCascadeId; + cascadeId = GetDefaultCascadeId(); + entity.ParentName = "根节点"; + } + + entity.CascadeId = cascadeId; + } + + //protected virtual IEnumerable GetSameLevelEntities(T entity) + //{ + // return Repository + // .ChangeRepository>() + // .GetList(o => EqualityComparer.Default.Equals(o.ParentId, entity.ParentId) && !o.Id.Equals(entity.Id)); + //} + + protected virtual IEnumerable GetSameLevelEntities(T entity) + { + var parentId = entity.ParentId; + return Repository + .ChangeRepository>() + .GetList(o => Object.Equals(o.ParentId, parentId) && !o.Id.Equals(entity.Id)); + } + + + protected virtual T GetParentOrganization(TIdType parentId) + { + //var parentOrg = Repository + // .ChangeRepository>() + // .GetFirst(o => EqualityComparer.Default.Equals(o.Id, parentId)); + + var parentOrg = Repository + .ChangeRepository>() + .GetFirst(o => Object.Equals(o.Id, parentId)); + + + if (parentOrg == null) + { + throw new Exception("未能找到该组织的父节点信息"); + } + + return parentOrg; + } + + public void UpdateTreeObj(T obj) + { + //CalculateCascade(obj); + + //var cascadeId = Repository.GetFirst(o => object.Equals(o.Id, obj.Id)).CascadeId; + //var objs = Repository.GetList(u => u.CascadeId.Contains(cascadeId) && !object.Equals(u.Id, obj.Id)) + // .OrderBy(u => u.CascadeId).ToList(); + + Repository.Update(obj); + + //foreach (var a in objs) + //{ + // a.CascadeId = a.CascadeId.Replace(cascadeId, obj.CascadeId); + // if (EqualityComparer.Default.Equals(a.ParentId, obj.Id)) + // { + // a.ParentName = obj.Name; + // } + + // Repository.Update(a); + //} + } + + + } +} diff --git a/OpenAuth.App/BaseApp/Base/Tree/TreeExtensions.cs b/OpenAuth.App/BaseApp/Base/Tree/TreeExtensions.cs new file mode 100644 index 0000000..445a016 --- /dev/null +++ b/OpenAuth.App/BaseApp/Base/Tree/TreeExtensions.cs @@ -0,0 +1,53 @@ +using OpenAuth.Repository.Core; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace OpenAuth.App.Base.Tree +{ + public static class TreeExtensions + { + public static IEnumerable BuildTree(this List nodes, long? parentId = null) + { + var rootNodes = nodes + .Where(n => n.ParentId == parentId || (parentId == null && (n.ParentId == null || !nodes.Any(child => child.Id == n.ParentId)))); + + foreach (var rootNode in rootNodes) + { + yield return new TreeItemLong + { + Id = rootNode.Id, + Name = rootNode.Name, + ParentId = rootNode.ParentId, + Level = rootNode.Level, + Children = nodes.BuildTree(rootNode.Id).ToList() + }; + } + } + + + + + public static List BuildTree1(this List nodes, long? parentId = null) + { + var result = new List(); + + foreach (var node in nodes.Where(n => n.ParentId == parentId)) + { + var newNode = new TreeItemLong + { + Id = node.Id, + Name = node.Name, + ParentId = node.ParentId, + Children = nodes.BuildTree1(node.Id) + }; + + result.Add(newNode); + } + + return result; + } + } +} diff --git a/OpenAuth.App/BaseApp/BasicQueryService/CommonDataManager.cs b/OpenAuth.App/BaseApp/BasicQueryService/CommonDataManager.cs new file mode 100644 index 0000000..9e4240f --- /dev/null +++ b/OpenAuth.App/BaseApp/BasicQueryService/CommonDataManager.cs @@ -0,0 +1,365 @@ +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using OpenAuth.App.Request; +using OpenAuth.App.Response; +using SqlSugar; +using System; +using System.Collections.Generic; +using System.Data; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace OpenAuth.App.BasicQueryService +{ + public class CommonDataManager + { + ISqlSugarClient client; + + public CommonDataManager(ISqlSugarClient client) + { + this.client = client; + } + + /// + /// wkt转geom数据 + /// + /// + /// + /// + public string WktToGeometry(string wkt, int wkid) + { + StringBuilder geomsql = new StringBuilder(); + geomsql.AppendFormat(" SELECT CAST(st_geomfromtext('{0}',{1}) AS text) geom", wkt, wkid); + var model_geom = client.SqlQueryable(geomsql.ToString()).First(); + return model_geom.geom; + } + + /// + /// Pgsql获取GeoJson数据 + /// + /// + /// + /// + public JToken PgsqlGeoJsonCommon(QueryGeoJsonCommonReq req) + { + //如果有的参数为空,设置参数的默认值 + req.init(); + + //查询列的数据 + StringBuilder sqlColumn = new StringBuilder(); + sqlColumn.AppendFormat(" select {1} from {0} {3} order by \"{2}\"", req.tablename, req.column, req.order, + req.where); + + StringBuilder sqlGeo = new StringBuilder(); + sqlGeo.AppendFormat(" select st_asgeojson(\"{0}\") geom from \"{1}\" {3} order by \"{2}\"", req.geom, + req.tablename, req.order, req.where); + + return PgsqlGeoJsonCommon(sqlColumn, sqlGeo, req.pageIndex, req.limit); + } + + /// + /// 查询GeoJson公共方法 + /// + /// + /// + /// + /// + /// + /// + public JToken PgsqlGeoJsonCommon(StringBuilder sqlColumn, StringBuilder sqlGeom, int pageIndex, int pageSize) + { + //获取到数据库操作实例 + int total = 0; + int totalPage = 0; + //查询列 + var dt_properties = client.SqlQueryable(sqlColumn.ToString()) + .ToPageList(pageIndex, pageSize, ref total, ref totalPage); + //列数据转换为字符串 + string str_properties = JsonConvert.SerializeObject(dt_properties); + //列数据转换为json对象 + JToken jtoken_properties = (JToken)JsonConvert.DeserializeObject(str_properties); + + //查询geojson数据 + var dt_geom = client.SqlQueryable(sqlGeom.ToString()).ToDataTablePage(pageIndex, pageSize); + + //组装最终数据 + JObject obj = new JObject(); + JArray array = new JArray(); + obj.Add("type", "FeatureCollection"); + obj.Add("totalNumber", total); + obj.Add("totalPage", totalPage); + //遍历geojson数据 + for (var i = 0; i < dt_geom.Rows.Count; i++) + { + //行数据 + DataRow item = dt_geom.Rows[i]; + //单条geojson + string _geom = item["geom"] == DBNull.Value ? "" : item["geom"].ToString(); + //给数据赋值 + JObject featureObj = new JObject(); + featureObj.Add("type", "Feature"); + featureObj.Add("geometry", (JToken)JsonConvert.DeserializeObject(_geom)); + featureObj.Add("properties", jtoken_properties[i]); + //添加到数组 + array.Add(featureObj); + } + + obj.Add("features", array); + return obj; + } + + /// + /// 获取列名 + /// + /// + /// + public List SearchColumnsList(string tablename) + { + //查询列名和注释描述 + StringBuilder columnSql = new StringBuilder(); + columnSql.AppendFormat( + @$" SELECT a.attname column_name,col_description(a.attrelid,a.attnum) as description,format_type(a.atttypid,a.atttypmod) as column_type + FROM pg_class as c,pg_attribute as a + where c.relname = '{tablename}' + and a.attrelid = c.oid + and a.attnum>0 + and attstattarget = -1"); + //查询结果 + + var column_list = client.SqlQueryable(columnSql.ToString()).ToList(); + return column_list; + } + + /// + /// 矢量切片公共方法 + /// + /// + /// + /// + public byte[] VectorTile(VectorTileSearchModel req) + { + req.Init(); + + List lons = getLon(req.x, req.z); + List lats = getLat(req.y, req.z); + double lonmin = lons[0]; + double lonmax = lons[1]; + double latmin = lats[1]; + double latmax = lats[0]; + + StringBuilder sql = new StringBuilder(); + sql.AppendFormat( + $" SELECT ST_AsMVT(tile, '{req.source_layer}', 4096, 'geom') tile FROM (SELECT {req.field}ST_AsMVTGeom(geom, ST_Transform(ST_MakeEnvelope({lonmin},{latmin},{lonmax},{latmax}, 4326),4326),4096, 256, true) AS geom FROM public.{req.table} where {req.filter} ) AS tile"); + + + var dataTable = client.Ado.GetDataTable(sql.ToString()); + byte[] result = (Byte[])dataTable.Rows[0]["tile"]; + + return result; + } + + /// + /// 查询最新的主键 + /// + /// + /// + /// + /// + public string GetMaxKeyVal(string _column, string tablename, int _gid_const) + { + string _val = ""; + //查询最大的gid + var column_list = SearchColumnsList(tablename); + string _type = column_list[0].column_type; + if (_type != "integer") + { + _val = Guid.NewGuid().ToString(); + return _val; + } + + var _maxgid = client + .SqlQueryable( + $"SELECT max(\"{_column}\") \"maxGid\" FROM \"{tablename}\" where \"{_column}\" < 6000000").First() + .maxGid; + //gid为空时,说明一条数据都没有 + if (_maxgid == null) + _val = _gid_const.ToString(); + else + { + //如果大于设定值,在此基础上加一,否则设置为默认值 + if (_maxgid >= _gid_const) + _val = (_maxgid + 1).ToString(); + else + _val = _gid_const.ToString(); + } + + return _val; + } + + /// + /// 格式化wkt数据,兼容多个地块 + /// + /// + /// + /// + /// + public string WktDataConvert(string _wkt, string _newType, int _len) + { + //使用逗号分割 + var list = _wkt.Split(","); + + //返回值 + StringBuilder res = new StringBuilder(); + + //扩展的维度,用 0补齐 + string extStr = ""; + for (int i = 0; i < list.Length; i++) + { + //当前项 + var item = list[i]; + extStr = GetWktDemension(item, _len); + if (i == 0) + { + //第一项数据先去除前缀 + item = item.Substring(item.IndexOf("(")).TrimStart('('); + if (_newType == "MULTIPOLYGON ZM") + item = "(((" + item; + else if (_newType == "MULTILINESTRING") + item = "((" + item; + + //扩展后的值 + item = _newType + item + extStr; + } + else if (i == list.Length - 1) + { + //最后一项 + item = item.TrimEnd(')'); + if (_newType == "MULTIPOLYGON ZM") + item = item + extStr + ")))"; + else if (_newType == "MULTILINESTRING") + item = item + extStr + "))"; + } + else + { + //判断是否存在右括号 + if (item.IndexOf(")") > -1) + { + //在第一个右括号前插入维度数据 + item = item.Insert(item.IndexOf(")"), extStr); + } + else + { + //在后面追加维度数据 + item = item + extStr; + } + } + + //追加数据 + res.Append($"{item},"); + } + + string strRes = res.ToString(); + + //把最后一个逗号截取掉 + return strRes.Substring(0, strRes.Length - 1); + } + + private string GetWktDemension(string item, int _len) + { + if (item.IndexOf("(") > -1) + { + item = item.Substring(item.IndexOf("(")); + item = item.TrimStart('('); + } + + string extStr = ""; + //数据的维度 + var _count = _len - item.Split(" ", StringSplitOptions.RemoveEmptyEntries).Length; + + //扩展维度 + if (_count == 1) + extStr = " 0"; + else if (_count == 2) + extStr = " 0 0"; + return extStr; + } + + #region Util + + /// + /// 瓦片转经度 + /// + /// 列号 + /// 缩放级别 + /// + public static double tile2lon(int x, int z) + { + return x / Math.Pow(2.0, z) * 360.0 - 180; + } + + /// + /// 瓦片转纬度 + /// + /// 行号 + /// 缩放级别 + /// + public static double tile2lat(int y, int z) + { + double n = Math.PI - (2.0 * Math.PI * y) / Math.Pow(2.0, z); + return Math.Atan(Math.Sinh(n)) * 180 / Math.PI; + } + + /// + /// 获取经度最大值和经度最小值 + /// + /// 列号 + /// 缩放级别 + /// + public static List getLon(int x, int z) + { + List lonExtent = new List(); + lonExtent.Add(tile2lon(x, z)); + lonExtent.Add(tile2lon(x + 1, z)); + return lonExtent; + } + + /// + /// 获取纬度最大值和经度最小值 + /// + /// 列号 + /// 缩放级别 + /// + public static List getLat(int y, int z) + { + List latExtent = new List(); + latExtent.Add(tile2lat(y, z)); + latExtent.Add(tile2lat(y + 1, z)); + return latExtent; + } + + #endregion + + public byte[] VectorTile1(VectorTileSearchModel req) + { + req.Init(); + + List lons = getLon(req.x, req.z); + List lats = getLat(req.y, req.z); + double lonmin = lons[0]; + double lonmax = lons[1]; + double latmin = lats[1]; + double latmax = lats[0]; + + StringBuilder sql = new StringBuilder(); + sql.AppendFormat( + $" SELECT ST_AsMVT(tile, '{req.source_layer}', 4096, 'geom') tile FROM (SELECT {req.field}ST_AsMVTGeom(geometry, ST_Transform(ST_MakeEnvelope({lonmin},{latmin},{lonmax},{latmax}, 4326),4326),4096, 256, true) AS geom FROM public.{req.table} where {req.filter} ) AS tile"); + + + var dataTable = client.Ado.GetDataTable(sql.ToString()); + byte[] result = (Byte[])dataTable.Rows[0]["tile"]; + + return result; + } + } +} \ No newline at end of file diff --git a/OpenAuth.App/BaseApp/BasicQueryService/PositionManager.cs b/OpenAuth.App/BaseApp/BasicQueryService/PositionManager.cs new file mode 100644 index 0000000..85111ad --- /dev/null +++ b/OpenAuth.App/BaseApp/BasicQueryService/PositionManager.cs @@ -0,0 +1,187 @@ +using OpenAuth.Repository.Domain; +using SqlSugar; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace OpenAuth.App.BasicQueryService +{ + public class PositionManager + { + ISqlSugarClient client; + public PositionManager(ISqlSugarClient client) + { + this.client = client; + } + + private async Task HasRelation(string beginId, Dictionary map) + { + bool res = false; + var entity = await client.Queryable().Where(a => a.Id.ToString() == beginId).FirstAsync(); + if (entity == null || entity.ParentId.ToString() == "0") + { + res = false; + } + else if (map.ContainsKey(entity.ParentId.ToString())) + { + res = true; + } + else + { + res = await HasRelation(entity.ParentId.ToString(), map); + } + return res; + } + + /// + /// 判断是否是上级 + /// + /// 自己的岗位 + /// 对方的岗位 + /// + public async Task IsUp(List myIds, List otherIds) + { + bool res = false; + if (myIds.Count > 0 && otherIds.Count > 0) + { + Dictionary map = new Dictionary(); + foreach (var otherItem in myIds) + { + if (!map.ContainsKey(otherItem)) + { + map.Add(otherItem, 1); + } + } + foreach (var myItem in otherIds) + { + if (await HasRelation(myItem, map)) + { + res = true; + break; + } + } + } + return res; + } + /// + /// 判断是否是下级 + /// + /// 自己的岗位 + /// 对方的岗位 + /// + public async Task IsDown(List myIds, List otherIds) + { + bool res = false; + if (myIds.Count > 0 && otherIds.Count > 0) + { + Dictionary map = new Dictionary(); + foreach (var myItem in myIds) + { + if (!map.ContainsKey(myItem)) + { + map.Add(myItem, 1); + } + } + foreach (var otherItem in otherIds) + { + if (await HasRelation(otherItem, map)) + { + res = true; + break; + } + } + } + return res; + } + + /// + /// 获取上级岗位人员ID + /// + /// 岗位 + /// 级数 + /// + public async Task> GetUpIdList(List postIds, int level) + { + List res = new List(); + if (postIds.Count > 0 && level > 0 && level < 6) + {// 现在支持1-5级查找 + bool isHave = false; // 判断是否指定级数的职位 + foreach (var postId in postIds) + { + isHave = false; + var entity = await client.Queryable().Where(a => a.Id.ToString() == postId).FirstAsync(); + if (entity != null) + { + string parentId = entity.ParentId.ToString(); + SysPosition parentEntity = null; + for (int i = 0; i < level; i++) + { + parentEntity = await client.Queryable().Where(a => a.Id.ToString() == parentId).FirstAsync(); + if (parentEntity != null) + { + parentId = parentEntity.ParentId.ToString(); + if (i == (level - 1)) + { + isHave = true; + } + } + else + { + break; + } + } + if (isHave) + { + if (parentEntity != null) + { + res.Add(parentEntity.Id.ToString()); + } + } + } + } + } + return res; + } + /// + /// 获取下级岗位人员ID + /// + /// 岗位 + /// 级数 + /// + public async Task> GetDownIdList(List postIds, int level) + { + List res = new List(); + if (postIds.Count > 0 && level > 0 && level < 6) + {// 现在支持1-5级查找 + bool isHave = false; // 判断是否指定级数的职位 + List parentList = new List(); + parentList.AddRange(postIds); + for (int i = 0; i < level; i++) + { + parentList = (List)await client.Queryable().Where(a => parentList.Contains(a.ParentId.ToString())).Select(a => a.Id.ToString()).ToListAsync(); + if (parentList.Count > 0) + { + if (i == (level - 1)) + { + isHave = true; + } + } + else + { + break; + } + } + if (isHave) + { + res.AddRange(parentList); + } + } + return res; + } + + + + } +} diff --git a/OpenAuth.App/BaseApp/BasicQueryService/Request/QueryGeoJsonCommonReq.cs b/OpenAuth.App/BaseApp/BasicQueryService/Request/QueryGeoJsonCommonReq.cs new file mode 100644 index 0000000..512c839 --- /dev/null +++ b/OpenAuth.App/BaseApp/BasicQueryService/Request/QueryGeoJsonCommonReq.cs @@ -0,0 +1,28 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace OpenAuth.App.Request +{ + public class QueryGeoJsonCommonReq + { + public string tablename { get; set; } + public string geom { get; set; } + public string column { get; set; } + public string where { get; set; } + public string order { get; set; } + public int pageIndex { get; set; } + public int limit { get; set; } + + public void init() + { + //如果有的参数为空,设置参数的默认值 + if (string.IsNullOrEmpty(geom)) geom = "geom"; + if (string.IsNullOrEmpty(column)) column = "*"; + if (limit == 0 || limit == null) limit = 2000; + if (string.IsNullOrEmpty(order)) order = "gid"; + } + } +} diff --git a/OpenAuth.App/BaseApp/BasicQueryService/Request/QueryVectorTileByTableReq.cs b/OpenAuth.App/BaseApp/BasicQueryService/Request/QueryVectorTileByTableReq.cs new file mode 100644 index 0000000..f82596b --- /dev/null +++ b/OpenAuth.App/BaseApp/BasicQueryService/Request/QueryVectorTileByTableReq.cs @@ -0,0 +1,21 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace OpenAuth.App.Request +{ + public class QueryVectorTileByTableReq + { + public int x { get; set; } + public int y { get; set; } + public int z { get; set; } + + public string table { get; set; } + + public string field { get; set; } + + public string filter { get; set; } + } +} diff --git a/OpenAuth.App/BaseApp/BasicQueryService/Request/VectorTileSearchModel.cs b/OpenAuth.App/BaseApp/BasicQueryService/Request/VectorTileSearchModel.cs new file mode 100644 index 0000000..f43312c --- /dev/null +++ b/OpenAuth.App/BaseApp/BasicQueryService/Request/VectorTileSearchModel.cs @@ -0,0 +1,29 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace OpenAuth.App.Request +{ + public class VectorTileSearchModel + { + public void Init() + { + if (string.IsNullOrEmpty(filter)) filter = " 1=1"; + if (string.IsNullOrEmpty(source_layer)) source_layer = table; + } + + public int x { get; set; } + public int y { get; set; } + public int z { get; set; } + + public string table { get; set; } + + public string source_layer { get; set; } + + public string field { get; set; } + + public string filter { get; set; } + } +} diff --git a/OpenAuth.App/BaseApp/BasicQueryService/UserManager.cs b/OpenAuth.App/BaseApp/BasicQueryService/UserManager.cs new file mode 100644 index 0000000..4e7ac07 --- /dev/null +++ b/OpenAuth.App/BaseApp/BasicQueryService/UserManager.cs @@ -0,0 +1,162 @@ +using Infrastructure.Extensions; +using OpenAuth.App.Interface; +using OpenAuth.Repository.Domain; +using SqlSugar; +using StackExchange.Redis; +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; +using System.Runtime.CompilerServices; +using System.Security.Cryptography; +using System.Text; +using System.Threading.Tasks; + +namespace OpenAuth.App.BasicQueryService +{ + public class UserManager + { + ISqlSugarClient client; + + public UserManager(ISqlSugarClient sqlSugarClient) + { + client = sqlSugarClient; + } + + /// + /// x用户的部门集合 + /// + /// + /// + public ISugarQueryable UserOrgs(long userId) + { + var query = client.Queryable() + .LeftJoin((u, o) => u.OrgId == o.Id) + .Where((u, o) => u.UserId == userId && o.Id != null); + + return query; + } + + /// + /// x用户的角色集合 + /// + /// + /// + public ISugarQueryable UserRoles(long userId) + { + var query = client.Queryable() + .LeftJoin((u, r) => u.RoleId == r.Id) + .Where((u, r) => u.UserId == userId && r.Id != null); + + return query; + } + + + /// + /// x角色集合的菜单集合 + /// + /// + /// + /// + /// + /// + public ISugarQueryable RoleModules(List roleIds) + { + var query = client.Queryable() + .LeftJoin((r, m) => r.ModuleId == m.Id) + .Where((r, m) => roleIds.Contains(r.RoleId) && m.Id != null); + + return query; + } + + /// + /// x角色集合的按钮集合 + /// + /// + /// + /// + /// + /// + public ISugarQueryable RoleModuleElements(List roleIds) + { + var query = client.Queryable() + .LeftJoin((r, m) => r.ElementId == m.Id) + .Where((r, m) => roleIds.Contains(r.RoleId) && m.Id != null); + + return query; + } + + public ISugarQueryable UserPositons(long userId) + { + var query = client.Queryable() + .LeftJoin((u, p) => u.PositionId == p.Id) + .Where((u, p) => u.PositionId != null && u.UserId == userId && p.Id != null); + + return query; + } + + public async Task> UserIdsByPosition(string orgId, string positionId) + { + var list = await client.Queryable() + .Where(a => a.PositionId.ToString() == positionId) + .WhereIF(!string.IsNullOrEmpty(orgId), a => a.OrgId.ToString() == orgId) + .Select(a => a.UserId.ToString()).ToListAsync(); + return list; + } + + public async Task> UserIdsByPositions(List positionIds) + { + var list = await client.Queryable() + .Where(a => positionIds.Contains(a.PositionId.ToString())) + .Select(a => a.UserId.ToString()).ToListAsync(); + return list; + } + + public async Task> UserIdsByRole(string roleId) + { + var list = await client.Queryable() + .Where(a => a.RoleId.ToString() == roleId) + .Select(a => a.UserId.ToString()) + .ToListAsync(); + return list; + } + + public async Task> UserOrgsByUserIds(List userIds) + { + var list1 = await client.Queryable() + .Where(a => userIds.Contains(a.UserId.ToString())) + .GroupBy(a => a.UserId) + .Select(it => new + { + UserId = it.UserId.ToString(), + OrgIds = SqlFunc.Subqueryable().Where(s => s.UserId == it.UserId).ToList(s => s.OrgId.ToString()) + }).ToListAsync(); + //.ToDictionaryAsync(x => x.UserId, x => x.OrgIds); + + //var result = list.GroupBy(a => a.UserId.ToString()) + // .Select(g => new + // { + // UserId = g.Key, + // OrgIds = g.Select(a => a.OrgId.ToString()).ToList() + // }).ToDictionary(x => x.UserId, x => x.OrgIds); + Dictionary list = new Dictionary(); + foreach(var item in list1) + { + list.Add(item.UserId, item.OrgIds); + } + return list; + } + + public async Task> PositonsByUser(string userId) + { + var list = await client.Queryable() + .Where(a => a.UserId.ToString() == userId) + .Select(a => a.PositionId.ToString()) + .ToListAsync(); + return list; + } + + + } +} diff --git a/OpenAuth.App/BaseApp/BasicQueryService/WorkFlowMangaer.cs b/OpenAuth.App/BaseApp/BasicQueryService/WorkFlowMangaer.cs new file mode 100644 index 0000000..0c204c1 --- /dev/null +++ b/OpenAuth.App/BaseApp/BasicQueryService/WorkFlowMangaer.cs @@ -0,0 +1,83 @@ +using Infrastructure.Helpers; +using OpenAuth.Repository; +using OpenAuth.Repository.Domain; +using SqlSugar; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace OpenAuth.App.BasicQueryService +{ + public class WorkFlowMangaer + { + ISqlSugarClient client; + ISugarUnitOfWork unitWork; + public WorkFlowMangaer( + ISqlSugarClient sqlSugarClient, + ISugarUnitOfWork unitWork) + { + client = sqlSugarClient; + this.unitWork = unitWork; + } + + #region 模版 + + #endregion + + #region 流程 + + #endregion + + #region 任务 + + #endregion + + #region 签名 + + + public string ToWfImg(string keyValue, string password) + { + return ""; + + //if (string.IsNullOrEmpty(keyValue)) + //{ + // return ""; + //} + + //WFStamp entity = await GetEntity(keyValue); + //if (entity != null && (entity.F_IsNotPassword == 0 || entity.F_Password.Equals(password))) + //{ + // var imgId = entity.F_ImgFile; + // var data = await _annexesFileIBLL.GetEntity(imgId); + // if (data == null) + // { + // // 说明上传的是附件夹主键 + // var list = (List)await _annexesFileIBLL.GetList(imgId); + // if (list.Count == 0) + // { + // return ""; + // } + // data = list[0]; + // } + // var basePath = ConfigHelper.GetConfig().FilePath; + // string path = $"{basePath}/Learun_WF_IMG/{imgId}{data.F_FileExtensions}"; + // if (!File.Exists(path)) + // { + // byte[] arr = FileHelper.Read($"{basePath}/Learun_ANNEXESFILE/{data.F_FilePath}"); + // FileHelper.Write(path, arr); + // } + + // return $"{imgId}{data.F_FileExtensions}"; + //} + //else + //{ + // return ""; + //} + } + + + #endregion + } +} diff --git a/OpenAuth.App/BaseApp/Category/CategoryApp.cs b/OpenAuth.App/BaseApp/Category/CategoryApp.cs new file mode 100644 index 0000000..9c607cd --- /dev/null +++ b/OpenAuth.App/BaseApp/Category/CategoryApp.cs @@ -0,0 +1,117 @@ +using Infrastructure; +using OpenAuth.App.Base; +using OpenAuth.App.BaseApp.Base; +using OpenAuth.App.Interface; +using OpenAuth.App.Request; +using OpenAuth.App.Response; +using OpenAuth.Repository; +using OpenAuth.Repository.Domain; +using SqlSugar; + +namespace OpenAuth.App +{ + public class CategoryApp : SqlSugarBaseApp + { + private DbExtension extension; + public CategoryApp(ISugarUnitOfWork unitWork, ISimpleClient repository, IAuth auth, DbExtension dbExtension) : base(unitWork, repository, auth) + { + extension = dbExtension; + } + /// + /// 加载列表 + /// + + public void Add(AddOrUpdateCategoryReq req) + { + var obj = req.MapTo(); + obj.CreateTime = DateTime.Now; + var user = _auth.GetCurrentUser().User; + obj.CreateUserId = user.Id; + obj.CreateUserName = user.Name; + Repository.Insert(obj); + } + + public void Update(AddOrUpdateCategoryReq obj) + { + var user = _auth.GetCurrentUser().User; + Repository.Update(u => new SysCategory + { + Name = obj.Name, + Enable = obj.Enable, + DtValue = obj.DtValue, + DtCode = obj.DtCode, + TypeId = obj.TypeId, + UpdateTime = DateTime.Now, + UpdateUserId = user.Id, + UpdateUserName = user.Name + //todo:要修改的字段赋值 + }, u => u.Id == obj.Id); + + } + + /// + /// 加载一个分类类型里面的所有值,即字典的所有值 + /// + /// + /// + public dynamic LoadByTypeId(string typeId, int page, int limit) + { + var loginContext = _auth.GetCurrentUser(); + + var properties = extension.GetProperties(typeof(SysCategory)); + + var p = new PageModel() { PageIndex = page, PageSize = limit }; + + var exp = Expressionable.Create() + .AndIF(!string.IsNullOrEmpty(typeId), u => u.TypeId == typeId) + .ToExpression(); + + var list = Repository.GetPageList(exp, p); + + //return new TableData + //{ + // code = 200, + // count = p.TotalCount, + // data = list, + // msg = "success" + //}; + return new + { + code = 200, + columnHeaders = properties, + count = p.TotalCount, + data = list, + msg = "success" + }; + } + + public TableData QueryCategory(string type) + { + var table = base.Repository.AsQueryable() + .Where(a => a.TypeId == type) + .OrderBy(a => a.SortNo) + .Select(a => new + { + a.Name, + a.DtCode, + a.DtValue + }).ToList(); + + return new TableData() + { + count = table.Count, + data = table + }; + } + + public void Delete(string[] ids) + { + Repository.DeleteByIds(ids); + } + + public SysCategory Get(string id) + { + return Repository.GetById(id); + } + } +} \ No newline at end of file diff --git a/OpenAuth.App/BaseApp/Category/CategoryTypeApp.cs b/OpenAuth.App/BaseApp/Category/CategoryTypeApp.cs new file mode 100644 index 0000000..55b810a --- /dev/null +++ b/OpenAuth.App/BaseApp/Category/CategoryTypeApp.cs @@ -0,0 +1,73 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Infrastructure; +using OpenAuth.App.Base; +using OpenAuth.App.BaseApp.Base; +using OpenAuth.App.Interface; +using OpenAuth.App.Request; +using OpenAuth.App.Response; +using OpenAuth.Repository; +using OpenAuth.Repository.Domain; +using SqlSugar; + +namespace OpenAuth.App +{ + public class CategoryTypeApp : SqlSugarBaseApp + { + public CategoryTypeApp(ISugarUnitOfWork unitWork, ISimpleClient repository, IAuth auth) : base(unitWork, repository, auth) + { + + } + /// + /// 加载列表 + /// + public async Task Load(QueryCategoryTypeListReq request) + { + int totalCount = 0; + var result = new TableData(); + var objs = base.Repository.AsQueryable() + .WhereIF(!string.IsNullOrEmpty(request.key), u => u.Id.Contains(request.key) || u.Name.Contains(request.key)); + + result.data = await objs.OrderBy(u => u.Name).ToPageListAsync(request.page, request.limit, totalCount); + result.count = totalCount; + + return result; + } + + public void Add(AddOrUpdateCategoryTypeReq req) + { + var obj = req.MapTo(); + //todo:补充或调整自己需要的字段 + obj.CreateTime = DateTime.Now; + Repository.Insert(obj); + } + + public void Update(AddOrUpdateCategoryTypeReq obj) + { + var user = _auth.GetCurrentUser().User; + Repository.Update(u => new SysCategoryType + { + Name = obj.Name, + CreateTime = DateTime.Now + //todo:补充或调整自己需要的字段 + }, u => u.Id == obj.Id); + } + + public void Delete(string[] ids) + { + using (var uwo = UnitWork.CreateContext()) + { + uwo.CategoryType.Delete(u => ids.Contains(u.Id)); + uwo.Category.Delete(u => ids.Contains(u.TypeId)); + uwo.Commit(); + } + } + + public List AllTypes() + { + return Repository.GetList(); + } + } +} \ No newline at end of file diff --git a/OpenAuth.App/BaseApp/Category/Request/AddOrUpdateCategoryReq.cs b/OpenAuth.App/BaseApp/Category/Request/AddOrUpdateCategoryReq.cs new file mode 100644 index 0000000..b13186a --- /dev/null +++ b/OpenAuth.App/BaseApp/Category/Request/AddOrUpdateCategoryReq.cs @@ -0,0 +1,51 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a CodeSmith Template. +// +// DO NOT MODIFY contents of this file. Changes to this +// file will be lost if the code is regenerated. +// Author:Yubao Li +// +//------------------------------------------------------------------------------ + +namespace OpenAuth.App.Request +{ + /// + /// 分类表,也可用作数据字典。表示一个全集,比如:男、女、未知。关联的分类类型表示按什么进行的分类,如:按照性别对人类对象集 + /// + public class AddOrUpdateCategoryReq + { + + public long Id { get; set; } + + /// + /// 分类名称或描述 + /// + public string Name { get; set; } + /// + /// 分类标识 + /// + public string DtCode { get; set; } + /// + /// 通常与分类标识一致,但万一有不一样的情况呢? + /// + public string DtValue { get; set; } + /// + /// + /// + public bool Enable { get; set; } + /// + /// 排序号 + /// + public int SortNo { get; set; } + /// + /// 详细说明,基本没啥用 + /// + public string Description { get; set; } + /// + /// 分类的ID + /// + public string TypeId { get; set; } + + } +} \ No newline at end of file diff --git a/OpenAuth.App/BaseApp/Category/Request/AddOrUpdateCategoryTypeReq.cs b/OpenAuth.App/BaseApp/Category/Request/AddOrUpdateCategoryTypeReq.cs new file mode 100644 index 0000000..a2748dd --- /dev/null +++ b/OpenAuth.App/BaseApp/Category/Request/AddOrUpdateCategoryTypeReq.cs @@ -0,0 +1,31 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a CodeSmith Template. +// +// DO NOT MODIFY contents of this file. Changes to this +// file will be lost if the code is regenerated. +// Author:Yubao Li +// +//------------------------------------------------------------------------------ + +using System.ComponentModel.DataAnnotations.Schema; + +namespace OpenAuth.App.Request +{ + /// + /// 分类类型 + /// + [Table("CategoryType")] + public partial class AddOrUpdateCategoryTypeReq + { + + /// + /// 分类表ID + /// + public string Id { get; set; } + /// + /// 名称 + /// + public string Name { get; set; } + } +} \ No newline at end of file diff --git a/OpenAuth.App/BaseApp/Category/Request/QueryCategoryListReq.cs b/OpenAuth.App/BaseApp/Category/Request/QueryCategoryListReq.cs new file mode 100644 index 0000000..538e63e --- /dev/null +++ b/OpenAuth.App/BaseApp/Category/Request/QueryCategoryListReq.cs @@ -0,0 +1,11 @@ +namespace OpenAuth.App.Request +{ + public class QueryCategoryListReq : PageReq + { + + /// + /// TypeID + /// + public string TypeId { get; set; } + } +} \ No newline at end of file diff --git a/OpenAuth.App/BaseApp/Category/Request/QueryCategoryTypeListReq.cs b/OpenAuth.App/BaseApp/Category/Request/QueryCategoryTypeListReq.cs new file mode 100644 index 0000000..1f27d06 --- /dev/null +++ b/OpenAuth.App/BaseApp/Category/Request/QueryCategoryTypeListReq.cs @@ -0,0 +1,7 @@ +namespace OpenAuth.App.Request +{ + public class QueryCategoryTypeListReq : PageReq + { + //todo:添加自己的请求字段 + } +} \ No newline at end of file diff --git a/OpenAuth.App/BaseApp/Category/Response/CategoryTypeResp.cs b/OpenAuth.App/BaseApp/Category/Response/CategoryTypeResp.cs new file mode 100644 index 0000000..c49734f --- /dev/null +++ b/OpenAuth.App/BaseApp/Category/Response/CategoryTypeResp.cs @@ -0,0 +1,10 @@ +using OpenAuth.Repository.Domain; + +namespace OpenAuth.App.Response +{ + public class CategoryTypeResp : SysCategoryType + { + public string ParentId { get; set; } + + } +} \ No newline at end of file diff --git a/OpenAuth.App/BaseApp/CodeTable/CodeColumnsApp.cs b/OpenAuth.App/BaseApp/CodeTable/CodeColumnsApp.cs new file mode 100644 index 0000000..99758d4 --- /dev/null +++ b/OpenAuth.App/BaseApp/CodeTable/CodeColumnsApp.cs @@ -0,0 +1,74 @@ +using OpenAuth.App.Base; +using OpenAuth.Repository; +using OpenAuth.Repository.Domain; +using SqlSugar; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using OpenAuth.App.BaseApp.Base; + +namespace OpenAuth.App.CodeTable +{ + /// + /// 日 期: 2024-03-02 + /// 描 述: 数据库表字段信息 + /// + public class CodeColumnsApp : SqlSugarBaseApp + { + public CodeColumnsApp(ISugarUnitOfWork unitWork, ISimpleClient repository) : base(unitWork, repository, null) + { + + } + public Task> GetList(DbCodeColumns queryParams) + { + return base.Repository.AsQueryable() + .WhereIF(!string.IsNullOrEmpty(queryParams.CodeTableId), t => t.CodeTableId == queryParams.CodeTableId) + .WhereIF(!string.IsNullOrEmpty(queryParams.DbType), t => t.DbType == queryParams.DbType) + .WhereIF(!string.IsNullOrEmpty(queryParams.DbColumnName), t => t.DbColumnName == queryParams.DbColumnName) + .WhereIF(queryParams.IsIdentity != null, t => t.IsIdentity == queryParams.IsIdentity) + .WhereIF(queryParams.IsPrimaryKey != null, t => t.IsPrimaryKey == queryParams.IsPrimaryKey) + .OrderBy(t => t.Sort) + .ToListAsync(); + } + #region 提交数据 + /// + /// 删除 + /// + /// 表格主键 + public async Task Delete(string tableId) + { + await base.Repository.DeleteAsync(t => t.CodeTableId == tableId); + } + /// + /// 新增数据 + /// + /// 字段数据集 + /// + public async Task Add(List list) + { + await base.Repository.InsertRangeAsync(list); + } + /// + /// 更新数据 + /// + /// 字段数据集 + /// + public async Task Update(List list) + { + await base.Repository.UpdateRangeAsync(list); + } + + /// + /// 更新数据 + /// + /// 字段数据集 + /// + public async Task Deletes(List list) + { + await base.Repository.DeleteAsync(list); + } + #endregion + } +} diff --git a/OpenAuth.App/BaseApp/CodeTable/CodeTableApp.cs b/OpenAuth.App/BaseApp/CodeTable/CodeTableApp.cs new file mode 100644 index 0000000..3874698 --- /dev/null +++ b/OpenAuth.App/BaseApp/CodeTable/CodeTableApp.cs @@ -0,0 +1,277 @@ +using Infrastructure; +using Infrastructure.Extensions; +using Infrastructure.Utilities; +using Microsoft.Extensions.Configuration; +using NUnit.Framework; +using OpenAuth.App.Base; +using OpenAuth.App.FormScheme.FormHelpers; +using OpenAuth.Repository; +using OpenAuth.Repository.Domain; +using SqlSugar; +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using OpenAuth.App.BaseApp.Base; +using static Microsoft.Extensions.Logging.EventSource.LoggingEventSource; + +namespace OpenAuth.App.CodeTable +{ + public class CodeTableApp : SqlSugarBaseApp + { + CodeColumnsApp _codeColumnsApp; + private IConfiguration _configuration; + public CodeTableApp(ISugarUnitOfWork unitWork, ISimpleClient repository, CodeColumnsApp codeColumnsApp, IConfiguration configuration) : base(unitWork, repository, null) + { + _codeColumnsApp = codeColumnsApp; + _configuration = configuration; + } + + /// + /// 获取CodeTable的分页数据 + /// + /// 查询关键字 + /// 数据库编码 + /// + /// + /// + public async Task>>> LoadCodeTablePage(string keyWord, string dbCode, int page, int limit) + { + RefAsync totalCount = 0; + var info = await base.Repository.AsQueryable() + .WhereIF(!string.IsNullOrEmpty(keyWord), t => t.ClassName.Contains(keyWord) || t.TableName.Contains(keyWord) || t.Description.Contains(keyWord)) + .WhereIF(!string.IsNullOrEmpty(dbCode), t => t.DbId == dbCode) + .ToPageListAsync(page, limit, totalCount); + return new Response>> + { + Result = new PageInfo> + { + Items = info, + Total = totalCount + } + }; + } + /// + /// 获取实体集合 + /// + /// 数据库编码 + /// 数据库表名 + /// + public async Task> GetEntitys(string dbCode, List tableNames) + { + var list = await base.Repository.AsQueryable() + .Where(t => t.DbId == dbCode && tableNames.Contains(t.TableName)) + .ToListAsync(); + return list; + } + /// + /// 获取列表数据 + /// + /// 数据库编码 + /// + public Task> GetList(string dbCode) + { + + return base.Repository.AsQueryable().Where(t => t.DbId == dbCode).ToListAsync(); + } + /// + /// 跨服务器获取表单字段 + /// + /// + /// + /// + public List GetTableColumnList(string code, string tableName) + { + using (var db = this.CodeClient(code, _configuration)) + { + //var list= this.Repository.AsSugarClient().DbMaintenance.GetTableInfoList(isCache); + var list = db.DbMaintenance.GetColumnInfosByTableName(tableName,false); + return list; + } + + } + /// + /// 导入表信息 + /// + /// 数据库编码 + /// 数据表信息 + /// + public async Task ImportTable(string dbcode, List tableList) + { + using (SugarDbContext db = base.UnitWork.CreateContext()) + { + var oldTables = await GetList(dbcode); + List updateList = new List(); + List addList = new List(); + List deleteList = new List(); + foreach (var tableItem in tableList) + { + //排除中间表 + if (tableItem.Name.ToLower() == "db_codecolumns" || tableItem.Name.ToLower() == "db_codetable" || tableItem.Name.ToLower() == "db_history") + { + continue; + } + DbCodeTable entity = oldTables.FirstOrDefault(t => t.TableName == tableItem.Name); + //var dbColumns = db.Db.DbMaintenance.GetColumnInfosByTableName(tableItem.Name, false); + var dbColumns = GetTableColumnList(dbcode, tableItem.Name); + foreach (var item in dbColumns) + { + item.PropertyName = FormHelper.GetCsType(SqlSugar.DbType.Kdbndp, item.DataType); + } + int columnSort = 1; + if (entity == null) + { + // 新增 + entity = new DbCodeTable(); + entity.DbId = dbcode; + entity.ClassName = StringExtension.CsharpName(tableItem.Name); + entity.TableName = tableItem.Name; + entity.Description = tableItem.Description; + entity.IsLock = 0; + entity.DeleteMark = 0; + entity.State = 1; + await SaveEntity(null, entity); + + // 新增列 + foreach (var dbColumnItem in dbColumns) + { + var codeColumnsEntity = CreateColumn(dbColumnItem); + codeColumnsEntity.CodeTableId = entity.Id; + codeColumnsEntity.Id = Guid.NewGuid().ToString(); + codeColumnsEntity.Sort = columnSort; + columnSort++; + addList.Add(codeColumnsEntity); + } + } + else + { + // 编辑 + if (entity.Description != tableItem.Description + || entity.State != 1 + || entity.IsLock != 0 + || entity.DeleteMark != 0 + ) + { + entity.State = 1; + entity.Description = tableItem.Description; + entity.IsLock = 0; + entity.DeleteMark = 0; + await SaveEntity(entity.Id, entity); + } + var oldColumns = await _codeColumnsApp.GetList(new DbCodeColumns { CodeTableId = entity.Id }); + foreach (var dbColumnItem in dbColumns) + { + var codeColumnsEntity = CreateColumn(dbColumnItem); + codeColumnsEntity.CodeTableId = entity.Id; + codeColumnsEntity.Sort = columnSort; + columnSort++; + + var oldColumn = oldColumns.Find(t => t.DbColumnName == dbColumnItem.DbColumnName); + if (oldColumn == null) + { + codeColumnsEntity.Id = Guid.NewGuid().ToString(); + addList.Add(codeColumnsEntity); + } + else + { + codeColumnsEntity.Id = oldColumn.Id; + updateList.Add(codeColumnsEntity); + } + } + // 获取需要删除的列 + var deleteColumns = oldColumns.FindAll(t => updateList.FindIndex(t2 => t2.Id == t.Id) == -1); + deleteList.AddRange(deleteColumns); + } + } + //await _codeColumnsApp.Add(addList); + //await _codeColumnsApp.Update(updateList); + //await _codeColumnsApp.Deletes(deleteList); + await db.DbCodeColumns.InsertRangeAsync(addList); + await db.DbCodeColumns.UpdateRangeAsync(updateList); + await db.DbCodeColumns.DeleteAsync(deleteList); + db.Commit(); + } + } + #region 操作方法 + /// + /// 保存(新增,更新) + /// + /// 唯一标识码(GUID) + /// 数据库表信息db_codetable实体数据 + public async Task SaveEntity(string Id, DbCodeTable dbCodeTable) + { + if (!string.IsNullOrEmpty(Id)) // 更新 + { + dbCodeTable.Id = Id; + await base.Repository.UpdateAsync(dbCodeTable); + } + else // 新增 + { + if (string.IsNullOrEmpty(dbCodeTable.Id)) + { + dbCodeTable.Id = Guid.NewGuid().ToString(); + } + await base.Repository.InsertAsync(dbCodeTable); + } + } + /// + /// 获取类字段信息 + /// + /// 数据库字段信息 + /// + private DbCodeColumns CreateColumn(DbColumnInfo dbColumnInfo) + { + var codeColumns = new DbCodeColumns(); + codeColumns.DbColumnName = dbColumnInfo.DbColumnName; + codeColumns.IsNullable = dbColumnInfo.IsNullable == true ? 1 : 0; + codeColumns.IsIdentity = dbColumnInfo.IsIdentity == true ? 1 : 0; + codeColumns.IsPrimaryKey = dbColumnInfo.IsPrimarykey == true ? 1 : 0; + codeColumns.Description = dbColumnInfo.ColumnDescription; + codeColumns.CsType = dbColumnInfo.PropertyName; + codeColumns.DbType = dbColumnInfo.DataType; + codeColumns.Length = dbColumnInfo.Length; + codeColumns.DecimalDigits = dbColumnInfo.DecimalDigits; + return codeColumns; + } + #endregion + #region 获取表及视图字段 + /// + /// 获取表及视图字段 + /// + /// + /// + public List GetTableAndViewColumnList(string dbCode, string tableName) + { + using (var db = this.CodeClient(dbCode, _configuration)) + { + string query = $@" + SELECT + c.column_name, + d.description + FROM + information_schema.columns c + LEFT JOIN + pg_catalog.pg_class t ON t.relname = c.table_name + LEFT JOIN + pg_catalog.pg_attribute a ON a.attnum > 0 + AND a.attrelid = t.oid + AND a.attname = c.column_name + LEFT JOIN + pg_catalog.pg_namespace n ON n.oid = t.relnamespace + LEFT JOIN + pg_catalog.pg_description d ON d.objoid = a.attrelid + AND d.objsubid = a.attnum + WHERE + c.table_name = @tableName + AND c.table_schema = 'public';"; + // 执行查询并获取结果 + var viewColumns = db.Ado.SqlQuery(query, new { tableName = tableName }); + return viewColumns; + } + + } + #endregion + } +} diff --git a/OpenAuth.App/BaseApp/CodeTable/Request/CodeTableReq.cs b/OpenAuth.App/BaseApp/CodeTable/Request/CodeTableReq.cs new file mode 100644 index 0000000..ccab511 --- /dev/null +++ b/OpenAuth.App/BaseApp/CodeTable/Request/CodeTableReq.cs @@ -0,0 +1,27 @@ +using SqlSugar; +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace OpenAuth.App.CodeTable.Request +{ + /// + /// 数据库操作中间类 + /// + public class CodeTableReq + { + /// + /// 导入的数据表字符串 + /// + public string CodeTableStr { get; set; } + + /// + /// 需要导入表数据 + /// + [Required(ErrorMessage = "请选择导入的数据表")] + public List TableList { get; set; } + } +} diff --git a/OpenAuth.App/BaseApp/CodeTable/Response/CodeTableColumnsResp.cs b/OpenAuth.App/BaseApp/CodeTable/Response/CodeTableColumnsResp.cs new file mode 100644 index 0000000..120a5e5 --- /dev/null +++ b/OpenAuth.App/BaseApp/CodeTable/Response/CodeTableColumnsResp.cs @@ -0,0 +1,19 @@ +using OpenAuth.Repository.Domain; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace OpenAuth.App.CodeTable.Response +{ + public class CodeTableColumnsResp + { + /// db_codetable(数据库表信息)表的实体 + /// + public DbCodeTable db_codetable { get; set; } + /// db_codecolumns(数据库表字段信息)表的实体 + /// + public List db_codecolumnsList { get; set; } + } +} diff --git a/OpenAuth.App/BaseApp/CreateTable/BasicTableService.cs b/OpenAuth.App/BaseApp/CreateTable/BasicTableService.cs new file mode 100644 index 0000000..77a77fd --- /dev/null +++ b/OpenAuth.App/BaseApp/CreateTable/BasicTableService.cs @@ -0,0 +1,211 @@ +using Castle.Core.Internal; +using ClosedXML.Excel; +using DocumentFormat.OpenXml.Drawing; +using DocumentFormat.OpenXml.Spreadsheet; +using DocumentFormat.OpenXml.Wordprocessing; +using Infrastructure; +using Infrastructure.Extensions; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; +using NUnit.Framework.Internal.Execution; +using OpenAuth.App.Interface; +using OpenAuth.Repository; +using OpenAuth.Repository.Domain; +using SqlSugar; +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Text.Json.Nodes; +using System.Threading.Tasks; + +namespace OpenAuth.App +{ + public class BasicTableService + { + + ISqlSugarClient client; + + IAuth auth; + + public BasicTableService(ISqlSugarClient client,IAuth auth) + { + this.client = client; + this.auth = auth; + } + + /// + /// 创建表 + /// + /// + /// + [HttpPost] + public string CreateTable([FromBody] CreateTableReq req) + { + string tableName = req.TableName; + List tableInfo = req.TableInfos; + if(tableName == null||tableInfo.Count==0) { + return "数据不能为空"; + } + string sql = @"CREATE TABLE ""public""." + tableName+"("+ "id varchar(255) COLLATE \"pg_catalog\".\"default\" NOT NULL,"; + string explanation = ""; + int type = 0; + for (int i = 0; i < tableInfo.Count; i++) { + TableInfo table = tableInfo[i]; + if ("id".Equals(table.Name.ToLower())){ + continue; + } + sql = sql +table.Name+" "+ table.Type; + if (!table.Name.IsNullOrEmpty()) { + explanation = explanation + "COMMENT ON COLUMN \"public\"." + tableName + "." + table.Name + " Is "+"'"+ table.Explanation+"'"+";" ; + } + if ("varchar".Equals(table.Name.ToLower())) { + sql = sql +"("+255+")"; + } + if (table.Type.Contains("geom")) { + type = 1; + } + /*if (table.IsNull) { + sql = sql+" "+"NOT NUll"; + }*/ + sql = sql + ","; + if (tableInfo.Count-1==i) { + sql = sql + "CONSTRAINT "+ tableName +"_pkey PRIMARY KEY (\"id\"));"; + } + } + Console.WriteLine(sql); + int count = client.Ado.ExecuteCommand(sql); + Console.WriteLine(count); + string sql1 = "ALTER TABLE \"public\"." + tableName+ " OWNER TO \"postgres\""; + client.Ado.ExecuteCommand(sql1); + addExplain(explanation); + SysUser user = auth.GetCurrentUser().User; + string sql22 = "insert into table_record (\"TableName\",\"Type\",\"ColumnJson\",\"CreateUser\") values('" + tableName+"'," + type+",'"+ String.Join(":",tableInfo.Select( + p=> $"{p.Name},{p.Type},{p.Explanation}"))+ "','"+user.Account+"')"; + client.Ado.ExecuteCommand(sql22); + return "创建成功"; + } + + [HttpPost] + public string CreateView([FromBody] CreateViewReq req) { + int count = client.Ado.ExecuteCommand(req.sql); + return "创建成功"; + } + + [HttpPost] + public List UploadExcel(UploadExcelReq req) + { + using (var workbook = new XLWorkbook(req.file.OpenReadStream())){ + string tableName = req.tableName; + var wooksheet = workbook.Worksheet(1); + IXLRows row = wooksheet.RowsUsed(); + List rowCells = row.ToList(); + + IXLRow row1 = rowCells[0]; + IXLCells cells = row1.CellsUsed(); + + string sqltable = @"CREATE TABLE if not exists ""public""." + "table_relation_record " + "(" + "id varchar(255) COLLATE \"pg_catalog\".\"default\" NOT NULL," + + "name varchar(32) COLLATE \"pg_catalog\".\"default\"," + + "relation text COLLATE \"pg_catalog\".\"default\","+ + " CONSTRAINT \"table_relation_record_pkey\" PRIMARY KEY (\"id\"))\r\n;" + + "ALTER TABLE \"public\".\"table_relation_record\" OWNER TO \"postgres\";"; + + + addExplain(sqltable); + string sql = @"CREATE TABLE ""public""." + tableName + "(" + "id varchar(255) COLLATE \"pg_catalog\".\"default\" NOT NULL,"; + string explanation = ""; + List listTableInfo = new List(); + + int k = 0; + JsonObject json = new JsonObject(); + foreach (IXLCell cell in row1.CellsUsed()) + { + if (cell.GetString().IsNullOrEmpty()) { + break;} + json.Add(cell.GetString(), "cloum" + k); + TableInfo tableInfo = new TableInfo(); + tableInfo.Name = "cloum" + k; + tableInfo.Type = "varchar"; + // tableInfo.Length = 255; + tableInfo.Explanation = cell.GetString(); + listTableInfo.Add(tableInfo); + sql = sql + "cloum" + k + " " + "varchar(255),"; + explanation = explanation + "COMMENT ON COLUMN \"public\"." + tableName + "." + "cloum" + k + " Is " + "'" + cell.GetString() + "'" + ";"; + k++; + } + Dictionary dict = new Dictionary(); + dict.Add("id", Guid.NewGuid()); + dict.Add("name", tableName); + dict.Add("relation", json.ToJsonString()); + client.Insertable(dict).AS("table_relation_record").ExecuteCommand(); + sql = sql + "CONSTRAINT " + tableName + "_pkey PRIMARY KEY (\"id\"));"; + + string sql1 = "ALTER TABLE \"public\"." + tableName + " OWNER TO \"postgres\""; + addExplain(sql); + addExplain(sql1); + addExplain(explanation); + var count = wooksheet.RowCount(); + + List> listmap = new List>(); + + foreach (IXLRow rowValue in wooksheet.RowsUsed()) { + if (rowValue.RowNumber() == 1) { + + continue; + } + + Dictionary map = new Dictionary(); + map.Add("id", Guid.NewGuid()); + int g = 0; + foreach (IXLCell cell in rowValue.CellsUsed()) { + map.Add("cloum" + g, cell.GetString()); + g++; + } + listmap.Add(map); + + } + + client.Insertable(listmap).AS(tableName).ExecuteCommand(); + return listTableInfo; + } + + + } + [HttpPost] + public string UpdateTable([FromBody] CreateTableReq req) + { + List infos = req.TableInfos; + string sql = ""; + foreach (TableInfo info in infos) { + if (!info.YuanName.IsNullOrEmpty()) + { + sql = sql + @"ALTER TABLE ""public""."+req.TableName+"RENAME COLUMN"+info.YuanName + " TO " + info.Name+ ";"; + } + } + if (!sql.IsNullOrEmpty()) { addExplain(sql); } + + return "创建成功"; + } + public void addExplain(String sql) { + client.Ado.ExecuteCommand(sql); + } + + + public bool CheckTableExist(string tableName) + { + + string sql = "SELECT COUNT(*) FROM information_schema.tables WHERE table_name = '" + tableName + "';"; + + int count = client.Ado.GetInt(sql); + if (count > 0) + { + return true; + } + else { return false; } + + + } + } +} \ No newline at end of file diff --git a/OpenAuth.App/BaseApp/CreateTable/CreateTableReq.cs b/OpenAuth.App/BaseApp/CreateTable/CreateTableReq.cs new file mode 100644 index 0000000..ff2ff41 --- /dev/null +++ b/OpenAuth.App/BaseApp/CreateTable/CreateTableReq.cs @@ -0,0 +1,20 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace OpenAuth.App +{ + public class CreateTableReq + { + /// + /// 表名称 + /// + public string TableName { get; set;} + + public List TableInfos { get; set; } + } + + +} diff --git a/OpenAuth.App/BaseApp/CreateTable/CreateViewReq.cs b/OpenAuth.App/BaseApp/CreateTable/CreateViewReq.cs new file mode 100644 index 0000000..3f7fba6 --- /dev/null +++ b/OpenAuth.App/BaseApp/CreateTable/CreateViewReq.cs @@ -0,0 +1,19 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace OpenAuth.App +{ + public class CreateViewReq + { + /// + /// 表名称 + /// + public string sql { get; set;} + + } + + +} diff --git a/OpenAuth.App/BaseApp/CreateTable/TableInfo.cs b/OpenAuth.App/BaseApp/CreateTable/TableInfo.cs new file mode 100644 index 0000000..5908ff3 --- /dev/null +++ b/OpenAuth.App/BaseApp/CreateTable/TableInfo.cs @@ -0,0 +1,44 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace OpenAuth.App +{ + public class TableInfo + { + + public TableInfo() { } + /// + /// 字段名称 + /// + public string Name { get; set; } + /// + /// 字段名称 + /// + + public string YuanName { get; set; } + /// + /// 字段类型 + /// + public string Type { get; set; } + /// + /// 字段长度 + /// + /* public int Length { get; set; } + /// + /// 是否为空 + /// + public bool IsNull { get; set; }*/ + /// + /// 注释 + /// + public string Explanation { get; set; } + + public override string ToString() + { + return $"{Name},{Type},{Explanation}"; + } + } +} diff --git a/OpenAuth.App/BaseApp/CreateTable/TableRecordApp.cs b/OpenAuth.App/BaseApp/CreateTable/TableRecordApp.cs new file mode 100644 index 0000000..960fade --- /dev/null +++ b/OpenAuth.App/BaseApp/CreateTable/TableRecordApp.cs @@ -0,0 +1,41 @@ +using Infrastructure; +using OpenAuth.App.Base; +using OpenAuth.Repository; +using SqlSugar; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using OpenAuth.App.BaseApp.Base; + +namespace OpenAuth.App +{ + public class TableRecordApp : SqlSugarBaseApp + { + public TableRecordApp(ISugarUnitOfWork unitWork, + ISimpleClient repository) + : base(unitWork, repository, null) + { + + } + public Response>> LoadTableRecordList(string keyword, int pageindex, int pagesize) + { + //定义且实例化分页数据 + int totalCount = 0; + //数据查询并返回 + var info = this.Repository.AsQueryable() + .WhereIF(!string.IsNullOrEmpty(keyword), t => t.TableName.Contains(keyword)) + .ToPageList(pageindex, pagesize, ref totalCount); + return new Response>> + { + Result = new PageInfo> + { + Items = info, + Total = totalCount + } + }; + } + + } +} diff --git a/OpenAuth.App/BaseApp/CreateTable/UploadExcelReq.cs b/OpenAuth.App/BaseApp/CreateTable/UploadExcelReq.cs new file mode 100644 index 0000000..5ff845d --- /dev/null +++ b/OpenAuth.App/BaseApp/CreateTable/UploadExcelReq.cs @@ -0,0 +1,17 @@ +using Microsoft.AspNetCore.Http; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace OpenAuth.App +{ + public class UploadExcelReq + { + + public string tableName { get; set; } + + public IFormFile file { get; set; } + } +} diff --git a/OpenAuth.App/BaseApp/DataCodeRule/CodeRuleApp.cs b/OpenAuth.App/BaseApp/DataCodeRule/CodeRuleApp.cs new file mode 100644 index 0000000..0191a68 --- /dev/null +++ b/OpenAuth.App/BaseApp/DataCodeRule/CodeRuleApp.cs @@ -0,0 +1,245 @@ +using OpenAuth.App.Base; +using OpenAuth.App.DataCodeRule.Response; +using OpenAuth.Repository; +using OpenAuth.Repository.Domain; +using SqlSugar; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Infrastructure.Extensions; +using Infrastructure.Cache; +using OpenAuth.App.BaseApp.Base; + +namespace OpenAuth.App.DataCodeRule +{ + /// + /// 日期:2024-03-06 + /// 描述:编号规则 + /// + public class CodeRuleApp : SqlSugarBaseApp + { + private ICacheContext _iCache; + public CodeRuleApp(ISugarUnitOfWork unitWork, ICacheContext cacheContext, + ISimpleClient repository) + : base(unitWork, repository, null) + { + _iCache = cacheContext; + } + #region 单据编码处理 + /// + /// 获得指定模块或者编号的单据号 + /// + /// 编码 + /// 单据号 + public async Task GetBillCode(string enCode) + { + string billCode = ""; //单据号 + string nextBillCode = "";//单据号 + + SysCodeRule coderuleentity = await GetEntityByCode(enCode); + if (coderuleentity != null) + { + List codeRuleFormatList = coderuleentity.RuleFormatJson.ToList(); + string dateFormatStr = ""; + foreach (CodeRuleFormatModel codeRuleFormatEntity in codeRuleFormatList) + { + switch (codeRuleFormatEntity.itemType.ToString()) + { + //自定义项 + case "0": + billCode += codeRuleFormatEntity.formatStr; + nextBillCode += codeRuleFormatEntity.formatStr; + break; + //日期 + case "1": + //日期字符串类型 + dateFormatStr += codeRuleFormatEntity.formatStr.Replace("m", "M"); + billCode += DateTime.Now.ToString(dateFormatStr); + nextBillCode += DateTime.Now.ToString(dateFormatStr); + break; + //流水号 + case "2": + int seedStep = codeRuleFormatEntity.stepValue == null ? 1 : int.Parse(codeRuleFormatEntity.stepValue.ToString());//如果步长为空默认1 + int initValue = codeRuleFormatEntity.initValue == null ? 1 : int.Parse(codeRuleFormatEntity.initValue.ToString());//如果初始值为空默认1 + + int nowSerious = await _iCache.Lock(coderuleentity.RuleId, id => { return GetSeedValue(coderuleentity.RuleId, initValue, seedStep, dateFormatStr); }); + //int nowSerious =await codeRuleService.GetSeedValue(coderuleentity.F_RuleId, initValue, seedStep, dateFormatStr); + + + int nextSerious = nowSerious + seedStep; + // 最大种子已经过期 + string seriousStr = new string('0', (int)(codeRuleFormatEntity.formatStr.Length - nowSerious.ToString().Length)) + nowSerious.ToString(); + string NextSeriousStr = new string('0', (int)(codeRuleFormatEntity.formatStr.Length - nextSerious.ToString().Length)) + nextSerious.ToString(); + billCode += seriousStr; + nextBillCode += NextSeriousStr; + break; + //部门 + case "3": + //var userInfo = await CurrentUser(); + //DepartmentEntity departmentEntity = await _departmentIBLL.GetEntity(userInfo.F_DepartmentId); + //if (departmentEntity != null) + //{ + // if (codeRuleFormatEntity.formatStr == "code") + // { + // billCode += departmentEntity.F_EnCode; + // nextBillCode = nextBillCode + departmentEntity.F_EnCode; + // } + // else + // { + // billCode = billCode + departmentEntity.F_FullName; + // nextBillCode = nextBillCode + departmentEntity.F_FullName; + + // } + //} + break; + //公司 + case "4": + //var userInfo2 = await CurrentUser(); + //CompanyEntity companyEntity = await _companyIBLL.GetEntity(userInfo2.F_CompanyId); + //if (companyEntity != null) + //{ + // if (codeRuleFormatEntity.formatStr == "code") + // { + // billCode += companyEntity.F_EnCode; + // nextBillCode += companyEntity.F_EnCode; + // } + // else + // { + // billCode += companyEntity.F_FullName; + // nextBillCode += companyEntity.F_FullName; + // } + //} + break; + //用户 + case "5": + //var userInfo3 = await CurrentUser(); + //if (codeRuleFormatEntity.formatStr == "code") + //{ + // billCode += userInfo3.F_Account; + // nextBillCode += userInfo3.F_Account; + //} + //else + //{ + // billCode += userInfo3.F_RealName; + // nextBillCode += userInfo3.F_RealName; + //} + break; + default: + break; + } + } + coderuleentity.CurrentNumber = nextBillCode; + await SaveEntity(coderuleentity.RuleId, coderuleentity); + } + return billCode; + + } + + /// + /// 保存规则表单(新增、修改) + /// + /// 主键值 + /// 规则实体 + /// + public async Task SaveEntity(string keyValue, SysCodeRule codeRuleEntity) + { + if (!string.IsNullOrEmpty(keyValue)) + { + codeRuleEntity.RuleId = keyValue; + codeRuleEntity.ModifyDate = DateTime.Now; + codeRuleEntity.ModifyUserId = _auth.GetUserId(); + codeRuleEntity.ModifyUserName = _auth.GetUserName(); + await base.Repository.UpdateAsync(codeRuleEntity); + } + else + { + codeRuleEntity.RuleId = Guid.NewGuid().ToString(); + codeRuleEntity.CreateDate = DateTime.Now; + codeRuleEntity.DeleteMark = 0; + codeRuleEntity.EnabledMark = 1; + codeRuleEntity.CreateUserId = _auth.GetUserId(); + codeRuleEntity.CreateUserName = _auth.GetUserName(); + await base.Repository.InsertAsync(codeRuleEntity); + } + } + + /// + /// 规则实体 + /// + /// 规则编码 + /// + public Task GetEntityByCode(string enCode) + { + return base.Repository.GetFirstAsync(t => t.EnabledMark == 1 && t.DeleteMark == 0 && t.EnCode == enCode); + } + #endregion + #region 单据编码处理 + /// + /// 获取当前编码规则流程号值 + /// + /// + /// + /// + /// + /// + public async Task GetSeedValue(string ruleId, int initValue, int stepNum, string dateFormatStr) + { + int value = 0; + using (SugarDbContext db = base.UnitWork.CreateContext()) + { + var codeRuleSeed = await base.Repository.ChangeRepository>().GetFirstAsync(t => t.RuleId == ruleId); + if (codeRuleSeed == null) + { + //说明没有种子,插入一条种子 + codeRuleSeed = new SysCodeRuleSeed(); + codeRuleSeed.RuleSeedId = Guid.NewGuid().ToString(); + codeRuleSeed.CreateDate = DateTime.Now; + codeRuleSeed.ModifyDate = DateTime.Now; + codeRuleSeed.SeedValue = initValue; + codeRuleSeed.RuleId = ruleId; + + value = (int)codeRuleSeed.SeedValue; + codeRuleSeed.SeedValue += stepNum; + + await db.SysCodeRuleSeed.InsertAsync(codeRuleSeed); + } + else + { + if (dateFormatStr.Contains("dd")) + { + if ((codeRuleSeed.ModifyDate).ToDateString() != DateTime.Now.ToString("yyyy-MM-dd")) + { + codeRuleSeed.SeedValue = initValue; + } + } + else if (dateFormatStr.Contains("mm")) + { + if (((DateTime)codeRuleSeed.ModifyDate).ToString("yyyy-MM") != DateTime.Now.ToString("yyyy-MM")) + { + codeRuleSeed.SeedValue = initValue; + } + } + else if (dateFormatStr.Contains("yy")) + { + if (((DateTime)codeRuleSeed.ModifyDate).ToString("yyyy") != DateTime.Now.ToString("yyyy")) + { + codeRuleSeed.SeedValue = initValue; + } + } + codeRuleSeed.ModifyDate = DateTime.Now; + value = (int)codeRuleSeed.SeedValue; + + codeRuleSeed.SeedValue += stepNum; + + await db.SysCodeRuleSeed.UpdateAsync(codeRuleSeed); + } + db.Commit(); + return value; + } + + } + #endregion + } +} diff --git a/OpenAuth.App/BaseApp/DataCodeRule/Response/CodeRuleFormatModel.cs b/OpenAuth.App/BaseApp/DataCodeRule/Response/CodeRuleFormatModel.cs new file mode 100644 index 0000000..d7ef74c --- /dev/null +++ b/OpenAuth.App/BaseApp/DataCodeRule/Response/CodeRuleFormatModel.cs @@ -0,0 +1,42 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace OpenAuth.App.DataCodeRule.Response +{ + /// + /// 日 期:2023.03.06 + /// 描 述:编号规则 + /// + public class CodeRuleFormatModel + { + #region 实体成员 + /// + /// 项目类型 + /// + public int? itemType { get; set; } + /// + /// 项目类型名称 + /// + public string itemTypeName { get; set; } + /// + /// 格式化字符串 + /// + public string formatStr { get; set; } + /// + /// 步长 + /// + public int? stepValue { get; set; } + /// + /// 初始值 + /// + public int? initValue { get; set; } + /// + /// 备注 + /// + public string description { get; set; } + #endregion + } +} diff --git a/OpenAuth.App/BaseApp/DataSource/DataSourceApp.cs b/OpenAuth.App/BaseApp/DataSource/DataSourceApp.cs new file mode 100644 index 0000000..6b709d9 --- /dev/null +++ b/OpenAuth.App/BaseApp/DataSource/DataSourceApp.cs @@ -0,0 +1,173 @@ +using Infrastructure; +using Infrastructure.Extensions; +using OpenAuth.App.Base; +using OpenAuth.App.FormScheme.FormHelpers; +using OpenAuth.Repository; +using OpenAuth.Repository.Domain; +using SqlSugar; +using System; +using System.Collections.Generic; +using System.Data; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using OpenAuth.App.BaseApp.Base; + +namespace OpenAuth.App.DataSource +{ + public class DataSourceApp : SqlSugarBaseApp + { + public DataSourceApp(ISugarUnitOfWork unitWork, + ISimpleClient repository) + : base(unitWork, repository, null) + { + + } + /// + /// 分页获取列表数据 + /// + /// + /// + /// + /// + public async Task>>> LoadDataSourceList(string keyword, int pageindex, int pagesize) + { + //定义且实例化分页数据 + RefAsync totalCount = 0; + //数据查询并返回 + var info = await this.Repository.AsQueryable() + .WhereIF(!string.IsNullOrEmpty(keyword), t => t.Name.Contains(keyword)) + .OrderBy(t => t.Name) + .ToPageListAsync(pageindex, pagesize, totalCount); + return new Response>> + { + Result = new PageInfo> + { + Items = info, + Total = totalCount + } + }; + } + /// + /// 获取实体 + /// + /// 编码 + /// + public Task GetEntityByCode(string code) + { + return this.Repository.AsQueryable().FirstAsync(t => t.Code == code); + } + /// + /// 获取sql的列 + /// + /// 编码 + /// + public async Task>> GetDataColName(string code) + { + Repository.Domain.DataSource entity = await GetEntityByCode(code); + if (entity == null || string.IsNullOrEmpty(entity.Sql) || !entity.Sql.Contains("select")) + { + return new Response>(); + } + else + { + string sql = entity.Sql; + sql = sql.Replace("={userId}", " is not null "); + sql = sql.Replace("={userAccount}", " is not null"); + sql = sql.Replace("={companyId}", " is not null"); + sql = sql.Replace("={departmentId}", " is not null"); + + sql = sql.Replace("= {userId}", " is not null "); + sql = sql.Replace("= {userAccount}", " is not null"); + sql = sql.Replace("= {companyId}", " is not null"); + sql = sql.Replace("= {departmentId}", " is not null"); + + sql = sql.Replace("=@param", " is not null"); + sql = sql.Replace("= @param", " is not null"); + using (SugarDbContext db = base.UnitWork.CreateContext()) + { + DataTable dt = await db.Db.SqlQueryable(sql).ToDataTablePageAsync(1, 1); + List res = new List(); + foreach (DataColumn item in dt.Columns) + { + res.Add(item.ColumnName.ToLower()); + } + return new Response> + { + Result = res + }; + } + + } + + } + /// + /// 保存(新增、修改) + /// + /// 主键值 + /// 数据源实体 + /// + public async Task> SaveEntity(string keyValue, Repository.Domain.DataSource dataSourceEntity) + { + var flag = false; + dataSourceEntity.Sql = FormHelper.SqlFilters(dataSourceEntity.Sql); + if (!string.IsNullOrEmpty(keyValue)) + { + Repository.Domain.DataSource entity = await this.Repository.GetFirstAsync(t => t.Code == dataSourceEntity.Code && t.Id != keyValue); + if (entity != null) + { + return new Response + { + Result = flag, + Message = flag == true ? "success" : "error" + }; + } + dataSourceEntity.Id = keyValue; + dataSourceEntity.ModifyDate = DateTime.Now; + dataSourceEntity.ModifyUserId = dataSourceEntity.ModifyUserId; + dataSourceEntity.ModifyUserName = dataSourceEntity.ModifyUserName; + flag = await this.Repository.UpdateAsync(dataSourceEntity); + } + else + { + Repository.Domain.DataSource entity = await GetEntityByCode(dataSourceEntity.Code); + if (entity != null) + { + return new Response + { + Result = flag, + Message = flag == true ? "success" : "error" + }; + } + dataSourceEntity.Id = Guid.NewGuid().ToString(); + dataSourceEntity.CreateDate = DateTime.Now; + dataSourceEntity.CreateUserId = dataSourceEntity.CreateUserId; + dataSourceEntity.CreateUserName = dataSourceEntity.CreateUserName; + flag = await this.Repository.InsertAsync(dataSourceEntity); + } + return new Response + { + Result = flag, + Message = flag == true ? "success" : "error" + }; + } + + /// + /// 删除数据源 + /// + /// 主键 + public async Task> DeleteEntity(string keyValue) + { + Repository.Domain.DataSource entity = new Repository.Domain.DataSource() + { + Id = keyValue, + }; + var flag = await this.Repository.DeleteAsync(entity); + return new Response + { + Result = flag, + Message = flag == true ? "success" : "error" + }; + } + } +} diff --git a/OpenAuth.App/BaseApp/DbTable/DbTableApp.cs b/OpenAuth.App/BaseApp/DbTable/DbTableApp.cs new file mode 100644 index 0000000..79f7b49 --- /dev/null +++ b/OpenAuth.App/BaseApp/DbTable/DbTableApp.cs @@ -0,0 +1,99 @@ +using Infrastructure; +using Infrastructure.Extensions; +using Microsoft.Extensions.Configuration; +using OpenAuth.App.Base; +using OpenAuth.App.CodeTable; +using OpenAuth.App.Request; +using OpenAuth.App.FormScheme.FormHelpers; +using OpenAuth.Repository; +using OpenAuth.Repository.Domain; +using SqlSugar; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using OpenAuth.App.BaseApp.Base; +using static Dm.net.buffer.ByteArrayBuffer; + +namespace OpenAuth.App.DbTable +{ + public class DbTableApp : SqlSugarBaseApp + { + private IConfiguration _configuration; + public DbTableApp(ISugarUnitOfWork unitWork, ISimpleClient repository, IConfiguration configuration) : base(unitWork, repository, null) + { + _configuration = configuration; + } + + /// + /// 创建表信息 + /// + /// 数据库编码 + /// 数据表信息 + /// + public Response AddTable(string dbcode, DbTableReq dbTableReq) + { + if (dbTableReq.DbColumnInfoList == null || !dbTableReq.DbColumnInfoList.Any()) + throw new Exception("请添加数据列"); + using (var db = this.CodeClient(dbcode, _configuration)) + { + var typeBuilder = db.DynamicBuilder().CreateClass(dbTableReq.TableName, new SugarTable() { TableName = dbTableReq.TableName, TableDescription = dbTableReq.Description }); + dbTableReq.DbColumnInfoList.ForEach(u => + { + typeBuilder.CreateProperty(u.DbColumnName, typeof(string), new SugarColumn() + { + IsPrimaryKey = u.IsPrimarykey == 1, + IsIdentity = u.IsIdentity == 1, + ColumnDataType = u.DataType, + Length = u.Length, + IsNullable = u.IsNullable == 1, + DecimalDigits = u.DecimalDigits, + ColumnDescription = u.ColumnDescription, + }); + }); + db.CodeFirst.InitTables(typeBuilder.BuilderType()); + return new Response() + { + Code = 200, + Result = true, + Message = "创建成功" + }; + } + } + /// + /// 创建字段 + /// + /// 数据库编码 + /// 表字段 + /// + public Response AddColumn(string dbcode, DbColumnInput dbColumnInput) + { + using (var db = this.CodeClient(dbcode, _configuration)) + { + var column = new DbColumnInfo + { + ColumnDescription = dbColumnInput.ColumnDescription, + DbColumnName = dbColumnInput.DbColumnName, + IsIdentity = dbColumnInput.IsIdentity == 1, + IsNullable = dbColumnInput.IsNullable == 1, + IsPrimarykey = dbColumnInput.IsPrimarykey == 1, + Length = dbColumnInput.Length, + DecimalDigits = dbColumnInput.DecimalDigits, + DataType = dbColumnInput.DataType + }; + + db.DbMaintenance.AddColumn(dbColumnInput.TableName, column); + db.DbMaintenance.AddColumnRemark(dbColumnInput.DbColumnName, dbColumnInput.TableName, dbColumnInput.ColumnDescription); + if (column.IsPrimarykey) + db.DbMaintenance.AddPrimaryKey(dbColumnInput.TableName, dbColumnInput.DbColumnName); + return new Response() + { + Code = 200, + Result = true, + Message = "创建成功" + }; + } + } + } +} diff --git a/OpenAuth.App/BaseApp/DbTable/Request/DbColumnInput.cs b/OpenAuth.App/BaseApp/DbTable/Request/DbColumnInput.cs new file mode 100644 index 0000000..5e3be9d --- /dev/null +++ b/OpenAuth.App/BaseApp/DbTable/Request/DbColumnInput.cs @@ -0,0 +1,29 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace OpenAuth.App.Request +{ + public class DbColumnInput + { + public string TableName { get; set; } + + public string DbColumnName { get; set; } + + public string DataType { get; set; } + + public int Length { get; set; } + + public string ColumnDescription { get; set; } + + public int IsNullable { get; set; } + + public int IsIdentity { get; set; } + + public int IsPrimarykey { get; set; } + + public int DecimalDigits { get; set; } + } +} diff --git a/OpenAuth.App/BaseApp/DbTable/Request/DbTableReq.cs b/OpenAuth.App/BaseApp/DbTable/Request/DbTableReq.cs new file mode 100644 index 0000000..c542042 --- /dev/null +++ b/OpenAuth.App/BaseApp/DbTable/Request/DbTableReq.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace OpenAuth.App.Request +{ + public class DbTableReq + { + public string TableName { get; set; } + + public string Description { get; set; } + + public List DbColumnInfoList { get; set; } + } +} diff --git a/OpenAuth.App/BaseApp/Extensions/OpenJobExt.cs b/OpenAuth.App/BaseApp/Extensions/OpenJobExt.cs new file mode 100644 index 0000000..03b509f --- /dev/null +++ b/OpenAuth.App/BaseApp/Extensions/OpenJobExt.cs @@ -0,0 +1,55 @@ +using System; +using System.Linq; +using Infrastructure; +using OpenAuth.Repository.Domain; +using Quartz; + +namespace OpenAuth.App.Extensions +{ + /// + /// 定时任务扩展 + /// + public static class OpenJobExt + { + /// + /// 启动定时任务 + /// + /// + /// 一个Quartz Scheduler + public static void Start(this SysOpenJob job, IScheduler scheduler) + { + var jobBuilderType = typeof(JobBuilder); + var method = jobBuilderType.GetMethods().FirstOrDefault( + x => x.Name.Equals("Create", StringComparison.OrdinalIgnoreCase) && + x.IsGenericMethod && x.GetParameters().Length == 0) + ?.MakeGenericMethod(Type.GetType(job.JobCall)); + + var jobBuilder = (JobBuilder) method.Invoke(null, null); + + IJobDetail jobDetail = jobBuilder.WithIdentity(job.Id.ToString()).Build(); + jobDetail.JobDataMap[Define.JOBMAPKEY] = job.Id; //传递job信息 + ITrigger trigger = TriggerBuilder.Create() + .WithCronSchedule(job.Cron) + .WithIdentity(job.Id.ToString()) + .StartNow() + .Build(); + scheduler.ScheduleJob(jobDetail, trigger); + } + + /// + /// 停止一个定时任务 + /// + /// + /// + public static void Stop(this SysOpenJob job, IScheduler scheduler) + { + TriggerKey triggerKey = new TriggerKey(job.Id.ToString()); + // 停止触发器 + scheduler.PauseTrigger(triggerKey); + // 移除触发器 + scheduler.UnscheduleJob(triggerKey); + // 删除任务 + scheduler.DeleteJob(new JobKey(job.Id.ToString())); + } + } +} \ No newline at end of file diff --git a/OpenAuth.App/BaseApp/Files/FileApp.cs b/OpenAuth.App/BaseApp/Files/FileApp.cs new file mode 100644 index 0000000..93db12b --- /dev/null +++ b/OpenAuth.App/BaseApp/Files/FileApp.cs @@ -0,0 +1,215 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Threading.Tasks; +using Infrastructure; +using Infrastructure.Extensions; +using Infrastructure.Helpers; +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; +using OpenAuth.App.Base; +using OpenAuth.App.BaseApp.Base; +using OpenAuth.App.Interface; +using OpenAuth.App.Request; +using OpenAuth.App.Response; +using OpenAuth.Repository; +using OpenAuth.Repository.Domain; +using SqlSugar; + +namespace OpenAuth.App +{ + /// + /// 文件管理 + /// + public class FileApp : SqlSugarBaseApp + { + private ILogger _logger; + private string _filePath; + private string _dbFilePath; //数据库中的文件路径 + private string _dbThumbnail; //数据库中的缩略图路径 + + public FileApp( + IOptions setOptions, + ISugarUnitOfWork unitWork, + ISimpleClient repository, + ILogger logger, + IAuth auth) : base(unitWork, repository, auth) + { + _logger = logger; + _filePath = setOptions.Value.UploadPath; + if (string.IsNullOrEmpty(_filePath)) + { + _filePath = AppContext.BaseDirectory; + } + } + + /// + /// 加载附件列表 + /// + public async Task LoadOld(QueryFileListReq request) + { + int totalCount = 0; + var result = new TableData(); + var objs = Repository.AsQueryable(); + objs.WhereIF(!string.IsNullOrEmpty(request.key), u => u.FileName.Contains(request.key) || u.FilePath.Contains(request.key)); + + result.data = await objs.OrderByDescending(u => u.CreateTime).ToPageListAsync(request.page, request.limit, totalCount); + result.count = totalCount; + + return result; + } + /// + /// 加载附件列表 + /// + public async Task>>> Load(QueryFileListReq request) + { + int totalCount = 0; + var objs = Repository.AsQueryable(); + objs.WhereIF(!string.IsNullOrEmpty(request.key), u => u.FileName.Contains(request.key) || u.FilePath.Contains(request.key)); + var data = await objs.OrderByDescending(u => u.CreateTime).ToPageListAsync(request.page, request.limit, totalCount); + return new Response>> + { + Result = new PageInfo> + { + Items = data, + Total = totalCount + } + }; + } + + /// + /// 批量添加附件 + /// + /// + /// + public List Add(IFormFileCollection files) + { + if (!_auth.CheckLogin()) + { + throw new Exception("必需登录才能上传附件"); + } + + var result = new List(); + foreach (var file in files) + { + result.Add(Add(file)); + } + + return result; + } + + public SysUploadFile Add(IFormFile file) + { + if (file != null) + { + _logger.LogInformation("收到新文件: " + file.FileName); + _logger.LogInformation("收到新文件: " + file.Length); + } + else + { + _logger.LogWarning("收到新文件为空"); + } + + if (file != null && file.Length > 0 ) + { + using (var binaryReader = new BinaryReader(file.OpenReadStream())) + { + var fileName = Path.GetFileName(file.FileName); + var data = binaryReader.ReadBytes((int)file.Length); + SaveFile(fileName, data); + + var filedb = new SysUploadFile + { + Id = SqlSugar.SnowFlakeSingle.instance.getID(), + FilePath = _dbFilePath, + Thumbnail = _dbThumbnail, + FileName = fileName, + FileSize = file.Length.ToInt(), + CreateUserName = _auth.GetUserName(), + FileType = Path.GetExtension(fileName), + Extension = Path.GetExtension(fileName), + CreateTime = DateTime.Now, + }; + Repository.Insert(filedb); + return filedb; + } + } + else + { + throw new Exception("文件过大"); + } + } + + /// + /// 删除附件 + /// + /// + public void Delete(long[] ids) + { + var files = base.Repository.GetList(u => ids.Contains(u.Id)).ToList(); + for (int i = 0; i < files.Count(); i++) + { + var uploadPath = Path.Combine(_filePath, files[i].FilePath); + FileHelper.FileDel(uploadPath); + if (!string.IsNullOrEmpty(files[i].Thumbnail)) + { + FileHelper.FileDel(Path.Combine(_filePath, files[i].Thumbnail)); + } + Repository.Delete(u => u.Id == files[i].Id); + } + } + + /// + /// 存储文件,如果是图片文件则生成缩略图 + /// + /// + /// + /// + private void SaveFile(string fileName, byte[] fileBuffers) + { + string folder = DateTime.Now.ToString("yyyyMMdd"); + + //判断文件是否为空 + if (string.IsNullOrEmpty(fileName)) + { + throw new Exception("文件名不能为空"); + } + + //判断文件是否为空 + if (fileBuffers.Length < 1) + { + throw new Exception("文件不能为空"); + } + + var uploadPath = Path.Combine(_filePath, folder); + _logger.LogInformation("文件写入:" + uploadPath); + if (!Directory.Exists(uploadPath)) + { + Directory.CreateDirectory(uploadPath); + } + + var ext = Path.GetExtension(fileName).ToLower(); + string newName = GenerateId.GenerateOrderNumber() + ext; + + using (var fs = new FileStream(Path.Combine(uploadPath, newName), FileMode.Create)) + { + fs.Write(fileBuffers, 0, fileBuffers.Length); + fs.Close(); + + //生成缩略图 + if (ext.Contains(".jpg") || ext.Contains(".jpeg") || ext.Contains(".png") || ext.Contains(".bmp") || + ext.Contains(".gif")) + { + string thumbnailName = GenerateId.GenerateOrderNumber() + ext; + ImgHelper.MakeThumbnail(Path.Combine(uploadPath, newName), Path.Combine(uploadPath, thumbnailName)); + _dbThumbnail = Path.Combine(folder, thumbnailName); + } + + + _dbFilePath = Path.Combine(folder, newName); + } + } + } +} \ No newline at end of file diff --git a/OpenAuth.App/BaseApp/Files/Request/QueryFileListReq.cs b/OpenAuth.App/BaseApp/Files/Request/QueryFileListReq.cs new file mode 100644 index 0000000..13d2a2b --- /dev/null +++ b/OpenAuth.App/BaseApp/Files/Request/QueryFileListReq.cs @@ -0,0 +1,7 @@ +namespace OpenAuth.App.Request +{ + public class QueryFileListReq : PageReq + { + //todo:添加自己的请求字段 + } +} \ No newline at end of file diff --git a/OpenAuth.App/BaseApp/FormModule/FormModuleApp.cs b/OpenAuth.App/BaseApp/FormModule/FormModuleApp.cs new file mode 100644 index 0000000..cc15f16 --- /dev/null +++ b/OpenAuth.App/BaseApp/FormModule/FormModuleApp.cs @@ -0,0 +1,703 @@ +using DocumentFormat.OpenXml.Spreadsheet; +using Infrastructure; +using Microsoft.Extensions.Configuration; +using NPOI.HSSF.UserModel; +using NPOI.SS.UserModel; +using OpenAuth.App.Base; +using OpenAuth.App.FormModule.Request; +using OpenAuth.App.Interface; +using OpenAuth.Repository; +using OpenAuth.Repository.Domain; +using SqlSugar; +using System.Linq.Expressions; +using System.Text; +using Castle.Core.Internal; +using DocumentFormat.OpenXml.Drawing.Charts; +using OpenAuth.App.BaseApp.Base; + +namespace OpenAuth.App.FormModule +{ + public class FormModuleApp : SqlSugarBaseApp + { + private IConfiguration _configuration; + + public FormModuleApp(ISugarUnitOfWork unitWork, + ISimpleClient repository, IAuth auth, + IConfiguration configuration) : base(unitWork, repository, auth) + { + _configuration = configuration; + } + + #region 查询数据 + + /// + /// 模块获取分页列表 + /// + /// 筛选关键词 + /// 父级菜单 + /// 当前页 + /// 每页条数 + /// + public async Task>>> GetModulePageList(string keyword, + string pmoduleid, int pageindex, int pagesize) + { + //定义且实例化分页数据 + RefAsync totalCount = 0; + //PageModel page = new PageModel { PageIndex = pageindex, PageSize = pagesize, TotalCount = totalCount }; + + ////筛选条件处理 + //Expression> expression = PredicateBuilder.True(); ; + //if (!string.IsNullOrEmpty(keyword)) + //{ + // expression = expression.And(t => t.Code.Contains(keyword) || t.Name.Contains(keyword)); + //} + //if (!string.IsNullOrEmpty(pmoduleid)) + //{ + // expression = expression.And(t => t.PmoduleId == pmoduleid); + //} + + //多租户 + //if (ConfigHelper.GetConfig().IsSass) + //{ + // expression = expression.And(t => t.IsMain != 1); + //} + + //数据查询并返回 + var info = await this.Repository.AsQueryable() + .WhereIF(!string.IsNullOrEmpty(keyword), t => t.Code.Contains(keyword) || t.Name.Contains(keyword)) + .WhereIF(!string.IsNullOrEmpty(pmoduleid), t => t.PmoduleId == pmoduleid) + .OrderByDescending(t => t.CreateDate) + .ToPageListAsync(pageindex, pagesize, totalCount); + + return new Response>> + { + Result = new PageInfo> + { + Items = info, + Total = totalCount + } + }; + } + + /// + /// 获取列表 + /// + /// + /// + public async Task>> GetList() + { + var info = await this.Repository.GetListAsync(r => r.EnabledMark == 1); + return new Response> + { + Result = info + }; + } + + /// + /// 获取实体数据 + /// + /// 发布表单模板的id(formmodule的id) + /// + public async Task> GetForm(string id) + { + var info = await this.Repository.GetByIdAsync(id); + return new Response + { + Result = info + }; + } + + /// + /// 根据菜单主键id获取实体数据 + /// + /// 菜单id(SYSModule的id) + /// + public async Task> GetFormByModuleId(string id) + { + var info = await this.Repository.GetFirstAsync(r => r.ModuleId == id); + return new Response + { + Result = info + }; + } + + /// + /// 根据编号获取实体数据 + /// + /// 编号 + /// + public async Task> GetFormByCode(string code) + { + var info = await this.Repository.GetFirstAsync(r => r.Code == code); + return new Response + { + Result = info + }; + } + + /// + /// 获取设计数据 + /// + /// 编码 + /// + public async Task> GetEntityByCode(string code) + { + var info = new FormModuleSchemeReq(); + info.Entity = await this.Repository.GetFirstAsync(r => r.Code == code); + if (info.Entity != null) + { + info.FormScheme = await base.Repository + .ChangeRepository>() + .GetFirstAsync(r => r.Id == info.Entity.FormVerison); + } + + return new Response + { + Result = info + }; + } + + #endregion + + #region 增删改 + + #region 添加 + + /// + /// 保存模块功能实体(新增、修改)(系统功能菜单,按钮,视图,表单四个表) + /// + /// 主键值 + /// 实体 + /// 按钮列表 + /// 视图列集合 + /// 表单字段 + public async Task> SaveEntity(string keyValue, FormModuleEntity entity, SysModule module, + List moduleElement, List moduleColumn, List moduleForm) + { + var mid = entity.ModuleId; + var oldModuleEntity = await this.Repository.AsSugarClient().Queryable().Where(r => r.Id == mid) + .FirstAsync(); + if (oldModuleEntity == null) + { + module.Id = mid; + mid = string.Empty; + } + + using (var uwo = base.UnitWork.CreateContext()) + { + #region 添加系统菜单及下属菜单信息 + + #region 添加 + + if (string.IsNullOrEmpty(module.ParentId) || module.ParentId == "-1") + { + module.ParentId = "0"; + } + + if (string.IsNullOrEmpty(mid)) + { + // 新增 + if (string.IsNullOrEmpty(module.Id)) + { + module.Id = Guid.NewGuid().ToString(); + } + + //module.F_CreateDate = DateTime.Now; + //module.F_CreateUserId = this.GetUserId(); + //module.F_CreateUserName = this.GetUserName(); + //module.F_DeleteMark = 0; + await uwo.SysModule.InsertAsync(module); + } + else + { + // 编辑 + await uwo.SysModule.UpdateAsync(u => new SysModule + { + ParentId = module.ParentId, + IconName = module.IconName, + ParentName = module.ParentName, + SortNo = module.SortNo, + Encode = module.Encode, + IsSys = module.IsSys, + Code = module.Code, + Status = module.Status, + CascadeId = module.CascadeId, + Url = module.Url + }, u => u.Id == mid); + await uwo.SysModuleElement.DeleteAsync(t => t.ModuleId == mid); + await uwo.SysModuleColumn.DeleteAsync(t => t.ModuleId == mid); + await uwo.SysModuleForm.DeleteAsync(t => t.ModuleId == mid); + } + + int num = 0; + if (moduleElement != null) + { + foreach (var item in moduleElement) + { + item.Sort = num; + item.ModuleId = module.Id; + //item.F_ModuleButtonId = Guid.NewGuid().ToString(); + if (string.IsNullOrEmpty(item.Id)) + { + item.Id = Guid.NewGuid().ToString(); + } + + await uwo.SysModuleElement.InsertAsync(item); + num++; + } + } + + if (moduleColumn != null) + { + num = 0; + foreach (var item in moduleColumn) + { + item.SortCode = num; + item.ModuleId = module.Id; + //item.F_ModuleColumnId = Guid.NewGuid().ToString(); + if (string.IsNullOrEmpty(item.ModuleColumnId)) + { + item.ModuleColumnId = Guid.NewGuid().ToString(); + } + + await uwo.SysModuleColumn.InsertAsync(item); + num++; + } + } + + if (moduleForm != null) + { + num = 0; + foreach (var item in moduleForm) + { + item.SortCode = num; + item.ModuleId = module.Id; + //item.F_ModuleFormId = Guid.NewGuid().ToString(); + if (string.IsNullOrEmpty(item.ModuleFormId)) + { + item.ModuleFormId = Guid.NewGuid().ToString(); + } + + await uwo.SysModuleForm.InsertAsync(item); + num++; + } + } + + #endregion + + #region 权限 + + if (!string.IsNullOrEmpty(entity.ModuleId)) + { + var objectList = await this.Repository.AsSugarClient().Queryable() + .Where(r => r.ModuleId == entity.ModuleId).ToListAsync(); + + List buttonList = new List(); + List columnList = new List(); + List formList = new List(); + + if (moduleElement != null) + { + foreach (var item in moduleElement) + { + buttonList.Add(item.Id); + } + } + + if (moduleColumn != null) + { + foreach (var item in moduleColumn) + { + columnList.Add(item.ModuleColumnId); + } + } + + if (moduleForm != null) + { + foreach (var item in moduleForm) + { + formList.Add(item.ModuleFormId); + } + } + + + if (buttonList.Count == 0) + { + await uwo.SysRoleElement.DeleteAsync(r => objectList.Select(r => r.Id).Contains(r.ElementId)); + } + else + { + await uwo.SysRoleElement.DeleteAsync(r => !buttonList.Contains(r.ElementId)); + } + + if (columnList.Count == 0) + { + //await moduleService.BaseRepository().Delete(t => t.F_ItemType == 3 && t.F_ParentId == moduleEntity.F_ModuleId); + } + else + { + await uwo.SysRoleElement.DeleteAsync(r => !buttonList.Contains(r.ElementId)); + } + + if (formList.Count == 0) + { + //await moduleService.BaseRepository().Delete(t => t.F_ItemType == 4 && t.F_ParentId == moduleEntity.F_ModuleId); + } + else + { + await uwo.SysRoleElement.DeleteAsync(r => !buttonList.Contains(r.ElementId)); + } + } + + #endregion + + #endregion + + #region 表单发布模板信息添加 + + entity.ModuleId = module.Id; + if (string.IsNullOrEmpty(keyValue)) + { + var user = _auth.GetCurrentUser().User; + entity.Id = Guid.NewGuid().ToString(); + entity.CreateDate = DateTime.Now; + entity.CreateUserId = user.Id.ToString(); + entity.CreateUserName = user.Name; + + await uwo.FormModuleEntity.InsertAsync(entity); + } + else + { + //编辑 + var user = _auth.GetCurrentUser().User; + entity.Id = keyValue; + entity.ModifyDate = DateTime.Now; + entity.ModifyUserId = user.Id.ToString(); + entity.ModifyUserName = user.Name; + await uwo.FormModuleEntity.UpdateAsync(entity); + } + + #endregion + + var flag = uwo.Commit(); + return new Response + { + Result = flag, + Message = flag == true ? "操作成功" : "操作失败" + }; + } + } + + + /// + /// 更新状态 + /// + /// 主键 + /// 状态 + /// + public async Task> UpdateState(string keyValue, int state) + { + var entity = await this.Repository.GetByIdAsync(keyValue); + if (entity != null) + { + using (var uwo = UnitWork.CreateContext()) + { + await uwo.FormModuleEntity.UpdateAsync(r => new FormModuleEntity + { + EnabledMark = state + }, r => r.Id == keyValue); + + await uwo.SysModule.UpdateAsync(r => new SysModule + { + Status = state + }, r => r.Id == keyValue); + + var flag = uwo.Commit(); + return new Response + { + Result = flag, + Message = flag == true ? "操作成功" : "操作失败" + }; + } + } + else + { + return new Response + { + Result = false, + Message = "数据丢失" + }; + } + } + + public async Task> DeleteForm(string keyValue) + { + var entity = await this.Repository.GetByIdAsync(keyValue); + if (entity != null) + { + using (var uwo = UnitWork.CreateContext()) + { + await uwo.FormModuleEntity.DeleteByIdAsync(keyValue); + await uwo.SysModule.DeleteByIdAsync(entity.ModuleId); + await uwo.SysModuleColumn.DeleteAsync(r => r.ModuleId == entity.ModuleId); + await uwo.SysModuleElement.DeleteAsync(r => r.ModuleId == entity.ModuleId); + await uwo.SysModuleForm.DeleteAsync(r => r.ModuleId == entity.ModuleId); + var flag = uwo.Commit(); + return new Response + { + Result = flag, + Message = flag == true ? "操作成功" : "操作失败" + }; + } + } + else + { + return new Response + { + Result = false, + Message = "数据不存在" + }; + } + } + + /// + /// 根据菜单主键id删除表单发布模板信息 + /// + /// 菜单id(Sysmodule的主键) + public async Task> DeleteEntityByModuleId(string keyValue) + { + using (var uwo = UnitWork.CreateContext()) + { + await uwo.FormModuleEntity.DeleteAsync(r => r.ModuleId == keyValue); + await uwo.SysModule.DeleteByIdAsync(keyValue); + await uwo.SysModuleColumn.DeleteAsync(r => r.ModuleId == keyValue); + await uwo.SysModuleElement.DeleteAsync(r => r.ModuleId == keyValue); + await uwo.SysModuleForm.DeleteAsync(r => r.ModuleId == keyValue); + var flag = uwo.Commit(); + return new Response + { + Result = flag, + Message = flag == true ? "操作成功" : "操作失败" + }; + } + } + + #endregion + + #endregion + + + #region 扩展方法 + + /// + /// 判断数据表字段重复 + /// + /// 主键值 + /// 表名 + /// 主键名 + /// 数据字段 + /// + public async Task> ExistFiled(string keyValue, string tableName, string keyName, + string filedsJson) + { + StringBuilder strSql = new StringBuilder(); + var tableNameList = tableName.Split("."); + var dbCode = string.Empty; + if (tableNameList.Length == 2) + { + dbCode = tableNameList[0]; + tableName = tableNameList[1]; + } + + strSql.Append("select * from " + tableName + " t where 1=1 "); + if (!string.IsNullOrEmpty(keyValue)) + { + strSql.Append(" AND \"" + keyName + "\" !=\'" + keyValue + "\'"); + } + + var args = filedsJson.ToJObject(); + foreach (var item in args.Properties()) + { + if (!string.IsNullOrEmpty(item.Value.ToString())) + { + strSql.Append(" AND \"" + item.Name + "\" = '" + item.Value + "'"); + } + } + + var dt = await this.Repository.AsSugarClient().SqlQueryable(strSql.ToString()).ToListAsync(); + + if (dt.Count == 0) + { + return new Response + { + Result = true + }; + } + else + { + return new Response + { + Result = false, + Message = "字段重复" + }; + } + } + + #endregion + + #region 获取数据库数据列表 + + /// + /// 数据表列表 + /// + /// 数据库编码 + /// 是否读取存储区 + /// + public Response> GetTableList(string code, string key, bool isCache = false) + { + using (var db = this.CodeClient(code, _configuration)) + { + var list = db.DbMaintenance.GetTableInfoList(isCache); + //var list = db.DbMaintenance.GetTableInfoList((dbtype, sql) => + //{ + // if (dbtype == SqlSugar.DbType.Kdbndp) + // { + // return sql.Replace(@"where relkind = 'r' and c.oid > 16384 and c.relnamespace != 99 and c.relname not like '%pl_profiler_saved%' order by relname", @" INNER JOIN PG_NAMESPACE AS n ON c.relnamespace = n.oid where relkind = 'r' and c.oid > 16384 and c.relnamespace != 99 and c.relname not like '%pl_profiler_saved%' and n.nspname = 'public' order by relname"); + // } + // return sql; + //}); + var info = list.FindAll(t => + t.DbObjectType == DbObjectType.Table && t.Name.ToLower() != "lr_db_codecolumns" && + t.Name.ToLower() != "lr_db_codetable" && t.Name.ToLower() != "lr_db_history") + .WhereIF(!key.IsNullOrEmpty(), t => t.Name.ToLower().Contains(key.ToLower())).ToList(); + return new Response> + { + Result = info + }; + } + } + + #endregion + + #region 导出 + public Response ListToExcel(System.Data.DataTable list, List headers) + { + Response response = new Response(); + try + { + HSSFWorkbook workbook = new HSSFWorkbook(); + #region 内容样式 + + IFont font1 = workbook.CreateFont(); //创建一个字体样式对象 + font1.FontName = "Microsoft YaHei"; //和excel里面的字体对应 + //font1.Boldweight = short.MaxValue;//字体加粗 + font1.FontHeightInPoints = 12; //字体大小 + ICellStyle style = workbook.CreateCellStyle(); //创建样式对象 + style.BorderBottom = BorderStyle.Thin; + style.BorderLeft = BorderStyle.Thin; + style.BorderRight = BorderStyle.Thin; + style.BorderTop = BorderStyle.Thin; + style.Alignment = NPOI.SS.UserModel.HorizontalAlignment.Center; + style.VerticalAlignment = VerticalAlignment.Center; + style.SetFont(font1); //将字体样式赋给样式对象 + style.WrapText = true; + + #endregion + + #region 标题样式 + + IFont font = workbook.CreateFont(); //创建一个字体样式对象 + font.FontName = "Microsoft YaHei"; //和excel里面的字体对应 + font.Boldweight = (short)FontBoldWeight.Bold; //字体加粗 + font.FontHeightInPoints = 12; //字体大小 + ICellStyle style1 = workbook.CreateCellStyle(); //创建样式对象 + style1.BorderBottom = BorderStyle.Thin; + style1.BorderLeft = BorderStyle.Thin; + style1.BorderRight = BorderStyle.Thin; + style1.BorderTop = BorderStyle.Thin; + style1.Alignment = NPOI.SS.UserModel.HorizontalAlignment.Center; + style1.VerticalAlignment = VerticalAlignment.Center; + style1.SetFont(font); //将字体样式赋给样式对象 + + #endregion + + #region 创建表头 + int m = list.Rows.Count / 60000+1; + for (int k = 0; k < m; k++) + { + ISheet sheet = workbook.CreateSheet("Sheet"+k.ToString()); + IRow rowHeader = sheet.CreateRow(0); + rowHeader.Height = 20 * 30; + for (int i = 0; i < headers.Count; i++) + { + ModuleColumn header = headers[i]; + rowHeader.CreateCell(i); + rowHeader.Cells[i].CellStyle = style1; + rowHeader.Cells[i].SetCellValue(header.value); + sheet.SetColumnWidth(i, 20 * 350); + } + #endregion + + #region 填充数据 + var val = (k + 1) * 60000; + var num = 60000; + if (val > list.Rows.Count) + { + num=list.Rows.Count-k*60000; + } + for (int i = 0; i < num; i++) //循环数据 + { + var item = list.Rows[k*60000+i]; //获取数据 + IRow dataRow = sheet.CreateRow(i + 1); //创建行 + for (int j = 0; j < headers.Count; j++) //循环表头 + { + //数据处理 + var objValue = ""; + if (list.Columns.Contains(headers[j].key)) + { + objValue = item[headers[j].key].ToString(); + } + else + { + objValue = ""; + } + //创建单元格 + dataRow.CreateCell(j); + dataRow.Cells[j].CellStyle = style; //添加单元格样式 + if (objValue != null && !string.IsNullOrEmpty(objValue.ToString())) + { + dataRow.Cells[j].SetCellValue(objValue.ToString()); //填充Excel单元格 + } + else + { + dataRow.Cells[j].SetCellValue(""); //填充Excel单元格 + } + } + } + } + + #endregion + + response.Result = new MemoryStream(); + workbook.Write(response.Result); + workbook = null; + response.Result.Close(); + response.Result.Dispose(); + response.Code = 200; + response.Message = "获取成功"; + } + catch (Exception ex) + { + response.Code = 500; + response.Message = ex.Message; + } + return response; + } + #endregion + } + public class ModuleColumn + { + public string key { get; set; } + public string value { get; set; } + } +} \ No newline at end of file diff --git a/OpenAuth.App/BaseApp/FormModule/Request/FormModuleReq.cs b/OpenAuth.App/BaseApp/FormModule/Request/FormModuleReq.cs new file mode 100644 index 0000000..e3dd48f --- /dev/null +++ b/OpenAuth.App/BaseApp/FormModule/Request/FormModuleReq.cs @@ -0,0 +1,38 @@ +using OpenAuth.Repository.Domain; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace OpenAuth.App.Request +{ + /// + /// 提交参数 + /// 2024.03.04 + /// xiaojiejie + /// + public class FormModuleReq + { + /// + /// 设计数据 + /// + public FormModuleEntity FormModuleEntity { get; set; } + /// + /// 功能模块实体 + /// + public SysModule SysModule { get; set; } + /// + /// 按钮列表 + /// + public List SysModuleElement { get; set; } + /// + /// 视图列集合 + /// + public List SysModuleColumn { get; set; } + /// + /// 表单字段集合 + /// + public List SysModuleForm { get; set; } + } +} diff --git a/OpenAuth.App/BaseApp/FormModule/Request/FormModuleSchemeReq.cs b/OpenAuth.App/BaseApp/FormModule/Request/FormModuleSchemeReq.cs new file mode 100644 index 0000000..cd8a40e --- /dev/null +++ b/OpenAuth.App/BaseApp/FormModule/Request/FormModuleSchemeReq.cs @@ -0,0 +1,21 @@ +using OpenAuth.Repository.Domain; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace OpenAuth.App.FormModule.Request +{ + public class FormModuleSchemeReq + { + /// + /// 设计数据 + /// + public FormModuleEntity Entity { get; set; } + /// + /// 表单模板 + /// + public Repository.Domain.FormScheme FormScheme { get; set; } + } +} diff --git a/OpenAuth.App/BaseApp/FormScheme/FormHelpers/DBCommonHelper.cs b/OpenAuth.App/BaseApp/FormScheme/FormHelpers/DBCommonHelper.cs new file mode 100644 index 0000000..87e1214 --- /dev/null +++ b/OpenAuth.App/BaseApp/FormScheme/FormHelpers/DBCommonHelper.cs @@ -0,0 +1,312 @@ +using Newtonsoft.Json.Converters; +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.Data; +using System.Linq; +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Text; +using System.Threading.Tasks; +using DbType = SqlSugar.DbType; +namespace OpenAuth.App.FormScheme.FormHelpers +{ + public class DBCommonHelper + { + /// + /// 获取fsql的数据库类型 + /// + /// SqlServer,Oracle,MySql,PostgreSQL,Dameng,Kdbndp + /// + public static DbType GetDbType(string dbtype) + { + switch (dbtype) + { + case "SqlServer": + return DbType.SqlServer; + case "Oracle": + return DbType.Oracle; + case "MySql": + return DbType.MySql; + case "PostgreSQL": + return DbType.PostgreSQL; + case "Dameng": + return DbType.Dm;//达梦 + case "Kdbndp": + return DbType.Kdbndp;//人大金仓 + case "Sqlite": + return DbType.Sqlite;//Sqlite + default: + return DbType.MySql; + } + } + + /// + /// 将IDataReader转换为DataTable + /// + /// datatable + /// IDataReader + public static DataTable IDataReaderToDataTable(IDataReader reader) + { + using (reader) + { + DataTable objDataTable = new DataTable("Table"); + int intFieldCount = reader.FieldCount; + for (int intCounter = 0; intCounter < intFieldCount; ++intCounter) + { + objDataTable.Columns.Add(reader.GetName(intCounter).ToLower(), reader.GetFieldType(intCounter)); + } + objDataTable.BeginLoadData(); + object[] objValues = new object[intFieldCount]; + while (reader.Read()) + { + reader.GetValues(objValues); + objDataTable.LoadDataRow(objValues, true); + } + reader.Close(); + reader.Dispose(); + objDataTable.EndLoadData(); + return objDataTable; + } + } + + /// + /// 将datatable的字段格式化为小写 + /// + /// + /// + public static DataTable DtColToLow(DataTable dt) + { + foreach (DataColumn item in dt.Columns) + { + //item.ColumnName = item.ColumnName.ToLower(); + item.ColumnName = item.ColumnName;//不转小写 + } + return dt; + } + + /// + /// 判断是否为匿名对象 + /// + /// + /// + public static bool CheckIfAnonymousType(Type type) + { + if (type == null) + throw new ArgumentNullException("type"); + + return Attribute.IsDefined(type, typeof(CompilerGeneratedAttribute), false) + && type.IsGenericType && type.Name.Contains("AnonymousType") + && (type.Name.StartsWith("<>")) + && (type.Attributes & TypeAttributes.NotPublic) == TypeAttributes.NotPublic; + } + + ///// + ///// 将不是匿名对象转化为字典数据 + ///// + ///// + ///// + //public static object ConvertToDic(object param) + //{ + // //判断是否匿名,不是匿名就转一下,是就直接加进去 + // if (param != null && !CheckIfAnonymousType(param.GetType())) + // { + // return param.ToObject>(); + // } + // return param; + //} + /// + /// 获取c#字段类型 + /// + /// 数据库类型 + /// 数据库字段类型 + /// + public static string GetCsType(DbType dbtype, string dbType) + { + string csType = "string"; + switch (dbType.ToLower()) + { + case "varchar": + csType = "string"; + break; + case "varchar2": + csType = "string"; + break; + case "char": + case "nchar": + case "nvarchar": + case "nvarchar2": + csType = "string"; + break; + case "text": + csType = "string"; + break; + case "ntext": + case "longtext": + case "clob": + case "nclog": + csType = "text"; + break; + case "int2": + csType = "int"; + break; + case "integer": + csType = "int"; + break; + case "int4": + csType = "int"; + break; + case "mediumint":// mysql + case "year": + csType = "int"; + break; + case "bigint": + csType = "long"; + break; + case "smallint": + csType = "short"; + break; + case "bit": + csType = "bool"; + break; + case "tinyint": + csType = "byte"; + break; + case "decimal": + csType = "decimal"; + break; + case "numeric": + csType = "decimal"; + break; + case "number": + csType = "int"; + break; + case "number(8,2)": + csType = "double"; + break; + case "money": + case "bool": + csType = "bool"; + break; + case "smallmoney": + csType = "decimal"; + break; + case "real": + case "double": + if (dbtype == DbType.MySql) + { + csType = "double"; + } + else + { + csType = "float"; + } + break; + case "float": + if (dbtype == DbType.MySql) + { + csType = "float"; + } + else + { + csType = "double"; + } + break; + case "float4": + csType = "double"; + break; + case "float8": + csType = "double"; + break; + case "date": + csType = "DateTime"; + break; + case "datetime": + csType = "DateTime"; + break; + case "datetime2": + csType = "DateTime"; + break; + case "smalldatetime": + csType = "DateTime"; + break; + case "datetimeoffset": + csType = "DateTimeOffset"; + break; + case "time": + csType = "DateTime"; + break; + case "timestamp": + if (dbtype == DbType.MySql) + { + csType = "DateTime"; + + } + else + { + csType = "DateTime"; + } + break; + case "binary": + case "varbinary": + case "image": + + case "tinyblob": + case "blob": + case "mediumblob": + case "longblob": + csType = "byte[]"; + break; + case "uniqueidentifier": + csType = "Guid"; + break; + case "variant": + csType = "Object"; + break; + case "timestamp without time zone": + csType = "TimeSpan"; + break; + // mysql + case "set": + case "enum": + csType = "Enum"; + break; + case "point": + case "linestring": + case "polygon": + case "geometry": + case "multipoint": + case "multilinestring": + case "multipolygon": + //case "geometrycollection": + // csType = "MygisGeometry"; + case "geometrycollection": + csType = "string"; + break; + } + + return csType; + } + + public static string GetDbType(DbType dbtype, string dbType, string csType) + { + if (!string.IsNullOrEmpty(dbType)) + { + return dbType; + } + if (csType == "text") + { + if (DbType.Oracle == dbtype) + { + return "clob"; + } + else + { + return "text"; + } + } + + return string.Empty; + } + } +} diff --git a/OpenAuth.App/BaseApp/FormScheme/FormHelpers/FormHelper.cs b/OpenAuth.App/BaseApp/FormScheme/FormHelpers/FormHelper.cs new file mode 100644 index 0000000..475564f --- /dev/null +++ b/OpenAuth.App/BaseApp/FormScheme/FormHelpers/FormHelper.cs @@ -0,0 +1,1444 @@ +using Infrastructure; +using Infrastructure.Extensions; +using Microsoft.AspNetCore.Components; +using Newtonsoft.Json.Linq; +using OpenAuth.App.FormScheme.Response; +using SqlSugar; +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Linq; +using System.Text; +using System.Text.RegularExpressions; +using System.Threading.Tasks; + +namespace OpenAuth.App.FormScheme.FormHelpers +{ + public static class FormHelper + { + /// + /// 获取保存sql语句 + /// + /// + /// + /// + /// + /// + /// + public static List GetSaveSql(FormSchemeModel formSchemeModel, JObject dataJson, string pkey, string pkeyValue, bool isUpdate) + { + List list = new List(); + + // 获取主子表 + FormDbTableInfo mainTable = formSchemeModel.Db.Find(t => t.Type == "main"); + List childrenTables = formSchemeModel.Db.FindAll(t => t.Type == "chlid"); + + // 对表组件按表进行分类 + Dictionary> tableMap = new Dictionary>(); + Dictionary girdTableMap = new Dictionary(); + + foreach (var tab in formSchemeModel.FormInfo.TabList) + { + foreach (var component in tab.Components) + { + if (string.IsNullOrEmpty(component.Table)) + { + continue; + } + if (!tableMap.ContainsKey(component.Table)) + { + tableMap[component.Table] = new List(); + } + + if (component.Type == "gridtable") + { + tableMap[component.Table].AddRange(component.children); + girdTableMap.Add(component.Table, component); + } + else + { + if (component.Prop == pkey) + { + pkey = component.Field; + } + tableMap[component.Table].Add(component); + } + } + } + + if (isUpdate) + { + list.Add(GetUpDateSql(mainTable, tableMap, dataJson, pkey, pkeyValue)); + foreach (var childTable in childrenTables) + { + if (tableMap.ContainsKey(childTable.Name)) + { + var rComponent = tableMap[childTable.RelationName].Find(t => t.Field == childTable.RelationField); + if (girdTableMap.ContainsKey(childTable.Name)) + { + list.Add(GetDeleteSql(childTable, dataJson, rComponent)); + // 新增 + FormComponentModel newComponent = new FormComponentModel(); + newComponent.Prop = rComponent.Prop; + newComponent.Field = childTable.Field; + tableMap[childTable.Name].Add(newComponent); + List girdDataJson = dataJson[girdTableMap[childTable.Name].Prop].ToString().ToObject>(); + foreach (var girdData in girdDataJson) + { + girdData.Add(newComponent.Prop, dataJson[newComponent.Prop]); + list.Add(GetInsertSql(childTable, tableMap, girdData)); + } + } + else + { + list.Add(GetUpDateSql(childTable, tableMap, dataJson, childTable.Field, dataJson[rComponent.Prop].ToString())); + } + } + + } + } + else + { + list.Add(GetInsertSql(mainTable, tableMap, dataJson)); + foreach (var childTable in childrenTables) + { + var rComponent = tableMap[childTable.RelationName].Find(t => t.Field.ToLower() == childTable.RelationField.ToLower()); + FormComponentModel newComponent = new FormComponentModel(); + newComponent.Prop = rComponent.Prop; + newComponent.Field = childTable.Field; + tableMap[childTable.Name].Add(newComponent); + + if (girdTableMap.ContainsKey(childTable.Name)) + { + // 编辑表格 + List girdDataJson = dataJson[girdTableMap[childTable.Name].Prop].ToString().ToObject>(); + foreach (var girdData in girdDataJson) + { + girdData.Add(newComponent.Prop, dataJson[newComponent.Prop]); + list.Add(GetInsertSql(childTable, tableMap, girdData)); + } + } + else + { + list.Add(GetInsertSql(childTable, tableMap, dataJson)); + } + + } + } + + return list; + } + /// + /// 获取保存sql语句 + /// + /// + /// + /// + /// + /// + /// + public static List GetSaveSqlNew(FormSchemeNewModel formSchemeModel, JObject dataJson, string pkey, string pkeyValue, bool isUpdate) + { + List list = new List(); + + // 获取主子表 + FormDbTableInfo mainTable = formSchemeModel.Db.Find(t => t.Type == "main"); + List childrenTables = formSchemeModel.Db.FindAll(t => t.Type == "chlid"); + + // 对表组件按表进行分类 + Dictionary> tableMap = new Dictionary>(); + //Dictionary girdTableMap = new Dictionary(); + Dictionary girdTableMap = new Dictionary(); + + foreach (var tab in formSchemeModel.FormInfo.TabList) + { + foreach (var component in tab.Schemas) + { + if (component == null || component.ComponentProps == null || string.IsNullOrEmpty(component.ComponentProps.DataTable)) + { + //if(component.Component == "Grid1") + //{ + // foreach (var column in component.Columns) + // { + // tableMap[component.ComponentProps.DataTable].AddRange(column.children); + // } + //} + //else + //{ + // continue; + //} + continue; + } + if (!tableMap.ContainsKey(component.ComponentProps.DataTable)) + { + tableMap[component.ComponentProps.DataTable] = new List(); + } + + if (component.Component == "Grid") + { + //tableMap[component.ComponentProps.DataTable].AddRange(component.children); + //girdTableMap.Add(component.ComponentProps.DataTable, component); + + foreach (var column in component.Columns) + { + tableMap[component.ComponentProps.DataTable].AddRange(column.children); + //girdTableMap.Add(component.ComponentProps.DataTable, column); + if (girdTableMap.ContainsKey(component.ComponentProps.DataTable)) + { + //girdTableMap[component.ComponentProps.DataTable].children.AddRange(column.children); + } + else + { + girdTableMap.Add(component.ComponentProps.DataTable, component); + } + } + + } + else + { + if (component.Field == pkey) + { + pkey = component.Field; + } + tableMap[component.ComponentProps.DataTable].Add(component); + } + } + } + + + + if (isUpdate) + { + list.Add(GetUpDateSqlNew(mainTable, tableMap, dataJson, pkey, pkeyValue)); + foreach (var childTable in childrenTables) + { + if (tableMap.ContainsKey(childTable.Name)) + { + var rComponent = tableMap[childTable.RelationName].Find(t => t.ComponentProps.FieldName == childTable.RelationField); + if (girdTableMap.ContainsKey(childTable.Name)) + { + list.Add(GetDeleteSqlNew(childTable, dataJson, rComponent)); + // 新增 + FormComponentNewModel newComponent = new FormComponentNewModel(); + //newComponent.ComponentProps.FieldName = rComponent.ComponentProps.FieldName; + newComponent.ComponentProps = new ComponentPropsModel(); + newComponent.ComponentProps.DataTable = childTable.Name; + newComponent.ComponentProps.FieldName = childTable.Field; + //newComponent.Field = childTable.Field; + newComponent.Field = rComponent.Field; + tableMap[childTable.Name].Add(newComponent); + //for (int i = 0; i < girdTableMap[childTable.Name].Count; i++) + //{ + // // 编辑表格 + // List girdDataJson = dataJson[girdTableMap[childTable.Name][i].Field].ToString().ToObject>(); + // foreach (var girdData in girdDataJson) + // { + // //girdData.Add(newComponent.ComponentProps.FieldName, dataJson[newComponent.ComponentProps.FieldName]); + // list.Add(GetInsertSqlNew(childTable, tableMap, girdData)); + // } + //} + // List girdDataJson = dataJson[girdTableMap[childTable.Name].ComponentProps.FieldName].ToString().ToObject>(); + List girdDataJson = dataJson[girdTableMap[childTable.Name].Field].ToString().ToObject>(); + foreach (var girdData in girdDataJson) + { + girdData.Add(newComponent.Field, dataJson[newComponent.Field]); + list.Add(GetInsertSqlNew(childTable, tableMap, girdData)); + } + } + else + { + list.Add(GetUpDateSqlNew(childTable, tableMap, dataJson, childTable.Field, dataJson[rComponent.Field].ToString())); + } + } + + } + } + else + { + list.Add(GetInsertSqlNew(mainTable, tableMap, dataJson)); + foreach (var childTable in childrenTables) + { + var rComponent = tableMap[childTable.RelationName].Find(t => t.ComponentProps.FieldName == childTable.RelationField); + FormComponentNewModel newComponent = new FormComponentNewModel() { }; + newComponent.Field = rComponent.Field; + newComponent.ComponentProps = new ComponentPropsModel(); + //newComponent.ComponentProps.DataTable = rComponent.ComponentProps.DataTable; + //newComponent.ComponentProps.FieldName = rComponent.ComponentProps.FieldName; + newComponent.ComponentProps.DataTable = childTable.Name; + newComponent.ComponentProps.FieldName = childTable.Field; + + //newComponent.Field = childTable.Field; + tableMap[childTable.Name].Add(newComponent); + + if (girdTableMap.ContainsKey(childTable.Name)) + { + //for(int i=0;i< girdTableMap[childTable.Name].Count; i++) + //{ + // // 编辑表格 + // List girdDataJson = dataJson[girdTableMap[childTable.Name][i].Field].ToString().ToObject>(); + // foreach (var girdData in girdDataJson) + // { + // //girdData.Add(newComponent.ComponentProps.FieldName, dataJson[newComponent.ComponentProps.FieldName]); + // list.Add(GetInsertSqlNew(childTable, tableMap, girdData)); + // } + //} + List girdDataJson = dataJson[girdTableMap[childTable.Name].Field].ToString().ToObject>(); + foreach (var girdData in girdDataJson) + { + girdData.Add(newComponent.Field, dataJson[newComponent.Field]); + list.Add(GetInsertSqlNew(childTable, tableMap, girdData)); + } + } + else + { + list.Add(GetInsertSqlNew(childTable, tableMap, dataJson)); + } + + } + } + + return list; + } + + /// + /// 获取表单查询方法 + /// + /// + /// + /// + public static FormDbTable GetQuerySql(FormSchemeModel formSchemeModel, string queryJson) + { + FormDbTable res = new FormDbTable(); + res.DbParameter = new List(); + + StringBuilder str = new StringBuilder(); + FormDbTableInfo mainTable = formSchemeModel.Db.Find(t => t.Type == "main"); + int mainTableIndex = formSchemeModel.Db.FindIndex(t => t.Type == "main"); + + if (formSchemeModel.FormType == 1) + { + str.Append($"select * from ({mainTable.Sql})t "); + } + else + { + Dictionary> tableMap = new Dictionary>(); + foreach (var tab in formSchemeModel.FormInfo.TabList) + { + foreach (var component in tab.Components) + { + if (component.Type != "gridtable") + { + if (string.IsNullOrEmpty(component.Table)) + { + continue; + } + if (!tableMap.ContainsKey(component.Table)) + { + tableMap[component.Table] = new List(); + } + tableMap[component.Table].Add(component); + } + } + } + + + str.Append("SELECT "); + var mainComponents = tableMap[mainTable.Name]; + foreach (var component in mainComponents) + { + str.Append(string.Format("t.{1} as {1}{0},", mainTableIndex, component.Field)); + } + + str.Append(GetQueryColumns(formSchemeModel.Db, tableMap, mainTable.Name)); + str.Append("FROM "); + str.Append(string.Format("{0} t ", mainTable.Name)); + str.Append(GetQueryLeftTable(formSchemeModel.Db, tableMap, mainTable.Name, mainTableIndex)); + + + } + + str.Append(" WHERE 1=1 {LEARUN_SASSID} "); + + + + + + // 添加参数 + // 对表组件按表进行分类 + Dictionary componentMap = new Dictionary(); + foreach (var tab in formSchemeModel.FormInfo.TabList) + { + foreach (var component in tab.Components) + { + if (string.IsNullOrEmpty(component.Table)) + { + continue; + } + if (!componentMap.ContainsKey(component.Prop)) + { + componentMap.Add(component.Prop, component); + } + } + } + + var queryParam = queryJson.ToJObject(); + int index = 0; + foreach (var item in queryParam.Properties()) + { + componentMap.TryGetValue(item.Name, out var component); + if (component != null && !string.IsNullOrEmpty(component.Table) && !queryParam[component.Prop].IsEmpty()) + { + int tableIndex = formSchemeModel.Db.FindIndex(t => t.Name == component.Table); + FormDbTableInfo table = formSchemeModel.Db[tableIndex]; + + string tIndex = tableIndex + ""; + if (table.Name == mainTable.Name) + { + tIndex = ""; + } + + + switch (component.Type) + { + case "userSelect": + case "departmentSelect": + case "companySelect": + case "company": + case "department": + case "createuser": + case "modifyuser": + case "select": + str.Append($" AND t{tIndex}.{component.Field} in ('{queryParam[component.Prop].ToString().Replace(",", "','")}') "); + break; + case "input": + case "textarea": + case "texteditor": + res.DbParameter.Add(new SugarParameter($"p_{index}", $"%{queryParam[component.Prop].ToString()}%")); + str.Append($" AND t{tIndex}.{component.Field} like @p_{index} "); + break; + + case "time": + case "datetime": + case "createtime": + case "modifytime": + var value = queryParam[component.Prop].ToString().Split(" - "); + res.DbParameter.Add(GetMyDbParameter($"p_{index}", value[0], component.CsType)); + res.DbParameter.Add(GetMyDbParameter($"p_{index + 1}", value[1], component.CsType)); + str.Append($" AND(t{tIndex}.{component.Field} >= @p_{index} AND t{tIndex}.{component.Field} <= @p_{index + 1} ) "); + index++; + break; + default: + res.DbParameter.Add(GetMyDbParameter($"p_{index}", queryParam[component.Prop].ToString(), component.CsType)); + str.Append($" AND t{tIndex}.{component.Field} = @p_{index} "); + break; + } + + index++; + } + } + + res.Sql = str.ToString().Replace(",FROM", " FROM"); + + return res; + } + public static FormDbTable GetQuerySqlNew(FormSchemeNewModel formSchemeModel, string queryJson) + { + FormDbTable res = new FormDbTable(); + res.DbParameter = new List(); + + StringBuilder str = new StringBuilder(); + FormDbTableInfo mainTable = formSchemeModel.Db.Find(t => t.Type == "main"); + int mainTableIndex = formSchemeModel.Db.FindIndex(t => t.Type == "main"); + + if (formSchemeModel.FormType == 1) + { + str.Append($"select * from ({mainTable.Sql})t "); + } + else + { + Dictionary> tableMap = new Dictionary>(); + foreach (var tab in formSchemeModel.FormInfo.TabList) + { + foreach (var component in tab.Schemas) + { + if (component.Component != "Grid") + { + if (component == null || component.ComponentProps == null || string.IsNullOrEmpty(component.ComponentProps.DataTable)) + { + continue; + } + if (!tableMap.ContainsKey(component.ComponentProps.DataTable)) + { + tableMap[component.ComponentProps.DataTable] = new List(); + } + tableMap[component.ComponentProps.DataTable].Add(component); + } + } + } + + str.Append("SELECT "); + var mainComponents = tableMap[mainTable.Name]; + foreach (var component in mainComponents) + { + if (component.ComponentProps.FieldName == "geom") + { + str.Append(string.Format("st_astext( t{0}.\"{1}\") as \"{2}\",", mainTableIndex, component.ComponentProps.FieldName, component.Field)); + } + else + { + str.Append(string.Format("t{0}.\"{1}\" as \"{2}\",", mainTableIndex, component.ComponentProps.FieldName, component.Field)); + } + + ////str.Append(string.Format("t{0}.\"{1}\" as \"{1}{0}\",", mainTableIndex, component.ComponentProps.FieldName)); + //str.Append(string.Format("t{0}.\"{1}\" as \"{2}\",", mainTableIndex, component.ComponentProps.FieldName, component.Field)); + } + + str.Append(GetQueryColumnsNew(formSchemeModel.Db, tableMap, mainTable.Name)); + //str = new StringBuilder(str.ToString().Substring(0, str.Length - 1)); + str.Append("FROM "); + str.Append(string.Format("\"{0}\" t{1} ", mainTable.Name, mainTableIndex)); + //不查询子表信息 + //str.Append(GetQueryLeftTableNew(formSchemeModel.Db, tableMap, mainTable.Name, mainTableIndex)); + + + } + + str.Append(" WHERE 1=1 {LEARUN_SASSID} "); + + + + + + // 添加参数 + // 对表组件按表进行分类 + Dictionary componentMap = new Dictionary(); + foreach (var tab in formSchemeModel.FormInfo.TabList) + { + foreach (var component in tab.Schemas) + { + if (component == null || component.ComponentProps == null || string.IsNullOrEmpty(component.ComponentProps.DataTable)) + { + continue; + } + if (!componentMap.ContainsKey(component.Field)) + { + componentMap.Add(component.Field, component); + } + } + } + + + + var queryParam = queryJson.ToJObject(); + int index = 0; + foreach (var item in queryParam.Properties()) + { + componentMap.TryGetValue(item.Name, out var component); + if (component != null && !string.IsNullOrEmpty(component.ComponentProps.DataTable) && !queryParam[component.Field].IsEmpty()) + { + //int tableIndex = formSchemeModel.Db.FindIndex(t => t.Name == component.Field); + int tableIndex = formSchemeModel.Db.FindIndex(t => t.Name == component.ComponentProps.DataTable); + FormDbTableInfo table = formSchemeModel.Db[tableIndex]; + + string tIndex = tableIndex + ""; + //if (table.Name == mainTable.Name) + //{ + // tIndex = ""; + //} + + + switch (component.Component) + { + case "userSelect": + case "departmentSelect": + case "companySelect": + case "company": + case "department": + case "createuser": + case "modifyuser": + case "select": + str.Append($" AND t{tIndex}.{component.Field} in ('{queryParam[component.Component].ToString().Replace(",", "','")}') "); + break; + case "Input": + case "InputTextArea": + case "texteditor": + res.DbParameter.Add(new SugarParameter($"p_{index}", $"%{queryParam[component.Field].ToString()}%")); + //str.Append($" AND t{tIndex}.{component.Field} like @p_{index} "); + str.Append($" AND t{tIndex}.{component.ComponentProps.FieldName} like @p_{index} "); + break; + + case "time": + case "datetime": + case "createtime": + case "modifytime": + var value = queryParam[component.Field].ToString().Split(" - "); + res.DbParameter.Add(GetMyDbParameter($"p_{index}", value[0], component.CsType)); + res.DbParameter.Add(GetMyDbParameter($"p_{index + 1}", value[1], component.CsType)); + str.Append($" AND(t{tIndex}.{component.ComponentProps.FieldName} >= @p_{index} AND t{tIndex}.{component.ComponentProps.FieldName} <= @p_{index + 1} ) "); + index++; + break; + case "RangePicker": + var valuep = queryParam[component.Field].ToString().Split(" - "); + res.DbParameter.Add(GetMyDbParameter($"p_{index}", valuep[0], component.CsType)); + res.DbParameter.Add(GetMyDbParameter($"p_{index + 1}", valuep[1], component.CsType)); + str.Append($" AND(t{tIndex}.{component.ComponentProps.FieldName} >= @p_{index} AND t{tIndex}.{component.ComponentProps.FieldName} <= @p_{index + 1} ) "); + index++; + break; + default: + res.DbParameter.Add(GetMyDbParameter($"p_{index}", queryParam[component.Field].ToString(), component.CsType)); + str.Append($" AND t{tIndex}.{component.ComponentProps.FieldName} = @p_{index} "); + break; + } + + index++; + } + } + + res.Sql = str.ToString().Replace(",FROM", " FROM").Replace("{LEARUN_SASSID}", " "); + + return res; + } + + /// + /// 获取更新sql语句 + /// + /// 表 + /// 组件集合 + /// 表单数据 + /// 主键 + /// 主键值 + /// + private static FormDbTable GetUpDateSql(FormDbTableInfo table, Dictionary> tableMap, JObject dataJson, string pkey, string pkeyValue) + { + FormDbTable res = new FormDbTable(); + res.DbParameter = new List(); + res.TableName = table.Name; + res.ExecuteType = ExecuteType.Update; + res.Pkey = pkey.ToUpper(); + bool isPkey = false; + var formComponents = tableMap[table.Name]; + + foreach (var component in formComponents) + { + if (!string.IsNullOrEmpty(component.Field)) + { + if (res.DbParameter.Find(t => t.ParameterName == component.Field.ToUpper()) != null) + { + // 参数添加过则不再添加 + continue; + } + + if (!dataJson[component.Prop].IsEmpty()) + { + res.DbParameter.Add(GetMyDbParameter(component.Field.ToUpper(), dataJson[component.Prop].ToString(), component.CsType)); + } + else + { + res.DbParameter.Add(new SugarParameter(component.Field.ToUpper(), null)); + } + + if (component.Field.ToUpper() == pkey.ToUpper()) + { + isPkey = true; + if (res.DbParameter[res.DbParameter.Count - 1].Value == null) + { + res.DbParameter[res.DbParameter.Count - 1].Value = pkeyValue; + } + } + } + } + + if (!isPkey) + { + res.DbParameter.Add(new SugarParameter(pkey.ToUpper(), pkeyValue)); + } + + return res; + } + private static FormDbTable GetUpDateSqlNew(FormDbTableInfo table, Dictionary> tableMap, JObject dataJson, string pkey, string pkeyValue) + { + FormDbTable res = new FormDbTable(); + res.DbParameter = new List(); + res.TableName = table.Name; + res.ExecuteType = ExecuteType.Update; + res.Pkey = pkey; + bool isPkey = false; + var formComponents = tableMap[table.Name]; + + foreach (var component in formComponents) + { + if (!string.IsNullOrEmpty(component.ComponentProps.FieldName)) + { + if (res.DbParameter.Find(t => t.ParameterName == component.Field) != null) + { + // 参数添加过则不再添加 + continue; + } + + //if (dataJson[component.Field] != null && !dataJson[component.Field].IsEmpty()) + if (!dataJson[component.Field].IsEmpty()) + { + res.DbParameter.Add(GetMyDbParameter(component.ComponentProps.FieldName, dataJson[component.Field].ToString(), component.CsType)); + } + else + { + //如果传过来的值为"",需要判断下字符类型 + if (dataJson[component.Field] != null && dataJson[component.Field].ToString() == "" && component.CsType == "string") + { + res.DbParameter.Add(GetMyDbParameter(component.ComponentProps.FieldName, dataJson[component.Field].ToString(), component.CsType)); + } + //只更新上传的数据,表单其他的不更新 + //res.DbParameter.Add(new SugarParameter(component.ComponentProps.FieldName, null)); + } + + if (component.ComponentProps.FieldName == pkey) + { + isPkey = true; + if (res.DbParameter[res.DbParameter.Count - 1].Value == null) + { + res.DbParameter[res.DbParameter.Count - 1].Value = pkeyValue; + } + } + } + } + + if (!isPkey) + { + res.DbParameter.Add(new SugarParameter(pkey, pkeyValue)); + } + + return res; + } + + private static FormDbTable GetDeleteSql(FormDbTableInfo table, JObject dataJson, FormComponentModel component) + { + FormDbTable res = new FormDbTable(); + res.DbParameter = new List(); + res.TableName = table.Name; + res.ExecuteType = ExecuteType.Delete; + + res.DbParameter.Add(GetMyDbParameter(table.Field.ToUpper(), dataJson[component.Prop].ToString(), component.CsType)); + return res; + } + private static FormDbTable GetDeleteSqlNew(FormDbTableInfo table, JObject dataJson, FormComponentNewModel component) + { + FormDbTable res = new FormDbTable(); + res.DbParameter = new List(); + res.TableName = table.Name; + res.ExecuteType = ExecuteType.Delete; + + res.DbParameter.Add(GetMyDbParameter(table.Field, dataJson[component.Field].ToString(), component.CsType)); + return res; + } + private static void GetDeleteSqlNew(List list, List db, string pTableName) + { + var tables = db.FindAll(t => t.RelationName == pTableName); + foreach (var table in tables) + { + var sql = new StringBuilder(); + sql.Append($" DELETE FROM {table.Name} WHERE {table.Field}= @keyValue "); + list.Add(new FormDbTable + { + Sql = sql.ToString(), + RelationField = table.RelationField, + RelationName = table.RelationName, + TableName = table.Name + }); + GetDeleteSqlNew(list, db, table.Name); + } + } + /// + /// 获取删除语句 + /// + /// + /// + /// + public static List GetDeleteSqlNew(FormSchemeNewModel formSchemeModel, string pkey) + { + List list = new List(); + // 获取主子表 + FormDbTableInfo mainTable = formSchemeModel.Db.Find(t => t.Type == "main"); + + var sql = new StringBuilder(); + //sql.Append($" DELETE FROM {mainTable.Name} WHERE {pkey}= @keyValue " + "{LEARUN_SASSID_NOTT}"); + sql.Append($" DELETE FROM {mainTable.Name} WHERE \"{pkey}\"= @keyValue "); + list.Add(new FormDbTable + { + Sql = sql.ToString(), + TableName = mainTable.Name + }); + GetDeleteSqlNew(list, formSchemeModel.Db, mainTable.Name); + return list; + } + /// + /// 获取新增sql语句 + /// + /// 表 + /// 组件集合 + /// 表单数据 + /// + private static FormDbTable GetInsertSql(FormDbTableInfo table, Dictionary> tableMap, JObject dataJson) + { + FormDbTable res = new FormDbTable(); + res.DbParameter = new List(); + res.TableName = table.Name; + res.ExecuteType = ExecuteType.Insert; + foreach (var component in tableMap[table.Name]) + { + if (!string.IsNullOrEmpty(component.Field) && !dataJson[component.Prop].IsEmpty()) + { + if (res.DbParameter.Find(t => t.ParameterName == component.Field) != null) + { + continue; + } + res.DbParameter.Add(GetMyDbParameter(component.Field, dataJson[component.Prop].ToString(), component.CsType)); + } + } + return res; + } + /// + /// 获取新增sql语句 + /// + /// 表 + /// 组件集合 + /// 表单数据 + /// + private static FormDbTable GetInsertSqlNew(FormDbTableInfo table, Dictionary> tableMap, JObject dataJson) + { + FormDbTable res = new FormDbTable(); + res.DbParameter = new List(); + res.TableName = table.Name; + res.ExecuteType = ExecuteType.Insert; + foreach (var component in tableMap[table.Name]) + { + if (!string.IsNullOrEmpty(component.Field) && !dataJson[component.Field].IsEmpty()) + { + if (res.DbParameter.Find(t => t.ParameterName == component.Field) != null) + { + continue; + } + res.DbParameter.Add(GetMyDbParameter(component.ComponentProps.FieldName, dataJson[component.Field].ToString(), component.CsType)); + } + } + return res; + } + + private static string GetQueryColumns(List db, Dictionary> tableMap, string pTableName) + { + StringBuilder str = new StringBuilder(); + var tables = db.FindAll(t => t.RelationName == pTableName && tableMap.ContainsKey(t.Name)); + foreach (var table in tables) + { + int tableIndex = db.FindIndex(t => t.Name == table.Name); + + var components = tableMap[table.Name]; + + foreach (var component in components) + { + if (!string.IsNullOrEmpty(component.Field)) + { + str.Append(string.Format("t{0}.{1} as {1}{0},", tableIndex, component.Field)); + } + } + + str.Append(GetQueryColumns(db, tableMap, table.Name)); + } + + return str.ToString(); + } + private static string GetQueryColumnsNew(List db, Dictionary> tableMap, string pTableName) + { + StringBuilder str = new StringBuilder(); + var tables = db.FindAll(t => t.RelationName == pTableName && tableMap.ContainsKey(t.Name)); + foreach (var table in tables) + { + //只查主表信息 + if (table.Type == "main") + { + int tableIndex = db.FindIndex(t => t.Name == table.Name); + + var components = tableMap[table.Name]; + + foreach (var component in components) + { + if (!string.IsNullOrEmpty(component.ComponentProps.DataTable)) + { + //str.Append(string.Format("t{0}.\"{1}\" as \"{1}{0}\",", tableIndex, component.ComponentProps.FieldName)); + str.Append(string.Format("t{0}.\"{1}\" as \"{2}\",", tableIndex, component.ComponentProps.FieldName, component.Field)); + } + } + + str.Append(GetQueryColumnsNew(db, tableMap, table.Name)); + } + //int tableIndex = db.FindIndex(t => t.Name == table.Name); + + //var components = tableMap[table.Name]; + + //foreach (var component in components) + //{ + // if (!string.IsNullOrEmpty(component.ComponentProps.DataTable)) + // { + // //str.Append(string.Format("t{0}.\"{1}\" as \"{1}{0}\",", tableIndex, component.ComponentProps.FieldName)); + // str.Append(string.Format("t{0}.\"{1}\" as \"{2}\",", tableIndex, component.ComponentProps.FieldName, component.Field)); + // } + //} + + //str.Append(GetQueryColumnsNew(db, tableMap, table.Name)); + } + + return str.ToString(); + } + + private static string GetQueryLeftTable(List db, Dictionary> tableMap, string pTableName, int pTableIndex) + { + StringBuilder str = new StringBuilder(); + var tables = db.FindAll(t => t.RelationName == pTableName && tableMap.ContainsKey(t.Name)); + foreach (var table in tables) + { + + int tableIndex = db.FindIndex(t => t.Name == table.Name); + str.Append(string.Format(" LEFT JOIN {0} t{1} ON t{1}.{2} = t{3}.{4} ", table.Name, tableIndex, table.Field, pTableIndex, table.RelationField)); + str.Append(GetQueryLeftTable(db, tableMap, table.Name, tableIndex)); + } + + return str.ToString(); + } + private static string GetQueryLeftTableNew(List db, Dictionary> tableMap, string pTableName, int pTableIndex) + { + StringBuilder str = new StringBuilder(); + var tables = db.FindAll(t => t.RelationName == pTableName && tableMap.ContainsKey(t.Name)); + foreach (var table in tables) + { + + int tableIndex = db.FindIndex(t => t.Name == table.Name); + str.Append(string.Format(" LEFT JOIN \"{0}\" t{1} ON t{1}.\"{2}\" = t{3}.\"{4}\" ", table.Name, tableIndex, table.Field, pTableIndex, table.RelationField)); + str.Append(GetQueryLeftTableNew(db, tableMap, table.Name, tableIndex)); + } + + return str.ToString(); + } + public static bool IsEmpty(this object value) + { + if (value != null && !string.IsNullOrEmpty(value.ToString())) + { + return false; + } + + return true; + } + public static SugarParameter GetMyDbParameter(string parameterName, string value, string csType) + { + string _csType = csType; + switch (csType) + { + case "int": + _csType = "System.Int32"; + break; + case "long": + _csType = "System.Int64"; + break; + case "short": + _csType = "System.Int16"; + break; + case "bool": + _csType = "System.Boolean"; + break; + case "float": + _csType = "System.Single"; + break; + case "text": + _csType = ""; + break; + default: + if (!string.IsNullOrEmpty(csType)) + { + _csType = $"System.{StringExtension.FirstUpper(csType)}"; + } + break; + } + if (_csType == "System.TimeSpan") + { + return new SugarParameter(parameterName, TimeSpan.Parse(value)); + } + else if (_csType == "System.Guid") + { + return new SugarParameter(parameterName, Guid.Parse(value)); + } + else + { + return new SugarParameter(parameterName, string.IsNullOrEmpty(_csType) ? value : Convert.ChangeType(value, System.Type.GetType(_csType))); + } + } + /// + /// 获取表单查询方法 + /// + /// + /// + /// + public static List GetQuery(FormSchemeModel formSchemeModel, string pkey) + { + List list = new List(); + + StringBuilder str = new StringBuilder(); + FormDbTableInfo mainTable = formSchemeModel.Db.Find(t => t.Type == "main"); + // 对表组件按表进行分类 + Dictionary> tableMap = new Dictionary>(); + Dictionary girdTableMap = new Dictionary(); + + foreach (var tab in formSchemeModel.FormInfo.TabList) + { + foreach (var component in tab.Components) + { + if (string.IsNullOrEmpty(component.Table)) + { + continue; + } + if (!tableMap.ContainsKey(component.Table)) + { + tableMap[component.Table] = new List(); + } + + if (component.Type == "gridtable") + { + tableMap[component.Table].AddRange(component.children); + girdTableMap.Add(component.Table, component); + } + else + { + if (component.Prop == pkey) + { + pkey = component.Field; + } + tableMap[component.Table].Add(component); + } + } + } + + + str.Append("SELECT "); + var mainComponents = tableMap[mainTable.Name]; + foreach (var component in mainComponents) + { + str.Append($" {component.Field},"); + } + str.Append($"FROM {mainTable.Name} t where {pkey} = @keyValue " + "{LEARUN_SASSID}"); + + list.Add(new FormDbTable + { + Sql = str.ToString().Replace(",FROM", " FROM"), + TableName = mainTable.Name + }); + + + GetQuery(list, formSchemeModel.Db, mainTable.Name, tableMap, girdTableMap); + + return list; + } + /// + /// 获取表单查询方法 + /// + /// + /// + /// + public static List GetQueryNew(FormSchemeNewModel formSchemeModel, string pkey) + { + List list = new List(); + + StringBuilder str = new StringBuilder(); + // 主数据库 + FormDbTableInfo mainTable = formSchemeModel.Db.Find(t => t.Type == "main"); + // 对表组件按表进行分类 + Dictionary> tableMap = new Dictionary>(); + Dictionary girdTableMap = new Dictionary(); + + foreach (var tab in formSchemeModel.FormInfo.TabList) + { + foreach (var component in tab.Schemas) + { + if (component == null || component.ComponentProps == null || string.IsNullOrEmpty(component.ComponentProps.DataTable)) + { + //if(component.Component == "Grid1") + //{ + // foreach (var column in component.Columns) + // { + // tableMap[component.ComponentProps.DataTable].AddRange(column.children); + // } + //} + //else + //{ + // continue; + //} + continue; + } + if (!tableMap.ContainsKey(component.ComponentProps.DataTable)) + { + tableMap[component.ComponentProps.DataTable] = new List(); + } + + if (component.Component == "Grid") + { + //tableMap[component.ComponentProps.DataTable].AddRange(component.children); + //girdTableMap.Add(component.ComponentProps.DataTable, component); + foreach (var column in component.Columns) + { + tableMap[component.ComponentProps.DataTable].AddRange(column.children); + if (girdTableMap.ContainsKey(component.ComponentProps.DataTable)) + { + //girdTableMap[component.ComponentProps.DataTable].AddRange(column); + } + else + { + girdTableMap.Add(component.ComponentProps.DataTable, column); + } + + } + } + else + { + if (component.ComponentProps.FieldName == pkey) + { + pkey = component.ComponentProps.FieldName; + } + //添加关联键 + tableMap[component.ComponentProps.DataTable].Add(component); + } + } + } + + + + + str.Append("SELECT "); + var mainComponents = tableMap[mainTable.Name]; + foreach (var component in mainComponents) + { + if (component.ComponentProps.FieldName == "geom") + { + str.Append($" st_astext(\"{component.ComponentProps.FieldName}\") as " + component.Field + ","); + } + else + { + str.Append($" \"{component.ComponentProps.FieldName}\" as " + component.Field + ","); + } + //str.Append($" \"{component.ComponentProps.FieldName}\" as " + component.Field + ","); + + } + //pkey = mainComponents.Where(t => t.ComponentProps.FieldName == pkey).First()?.Field; + pkey = mainComponents.Where(t => t.ComponentProps.FieldName == pkey).First()?.ComponentProps.FieldName; + //str.Append($"FROM \"{mainTable.Name}\" t where \"{pkey}\" = @keyValue " + "{LEARUN_SASSID}"); + str.Append($"FROM \"{mainTable.Name}\" t where \"{pkey}\" = @keyValue "); + + list.Add(new FormDbTable + { + Sql = str.ToString().Replace(",FROM", " FROM"), + TableName = mainTable.Name + }); + + + GetQueryNew(list, formSchemeModel.Db, mainTable.Name, tableMap, girdTableMap); + + return list; + } + + private static void GetQuery(List list, List db, string pTableName, Dictionary> tableMap, Dictionary girdTableMap) + { + var tables = db.FindAll(t => t.RelationName == pTableName); + foreach (var table in tables) + { + StringBuilder str = new StringBuilder(); + str.Append("SELECT "); + var components = tableMap[table.Name]; + foreach (var column in components) + { + str.Append($" {column.Field},"); + } + str.Append($"FROM {table.Name} where {table.Field} = @keyValue "); + + if (girdTableMap.ContainsKey(table.Name)) + { + var componentModel = girdTableMap[table.Name]; + if (!string.IsNullOrEmpty(componentModel.OrderId)) + { + if (componentModel.isDESC) + { + str.Append($" order by {componentModel.OrderId} DESC "); + } + else + { + str.Append($" order by {componentModel.OrderId} "); + } + } + } + + list.Add(new FormDbTable + { + Sql = str.ToString().Replace(",FROM", " FROM"), + RelationField = table.RelationField, + RelationName = table.RelationName, + TableName = table.Name + }); + + GetQuery(list, db, table.Name, tableMap, girdTableMap); + } + + + } + private static void GetQueryNew(List list, List db, string pTableName, Dictionary> tableMap, Dictionary girdTableMap) + { + var tables = db.FindAll(t => t.RelationName == pTableName); + foreach (var table in tables) + { + StringBuilder str = new StringBuilder(); + str.Append("SELECT "); + var components = tableMap[table.Name]; + foreach (var column in components) + { + //新加 + var field = components.Where(t => t.ComponentProps.FieldName == column.ComponentProps.FieldName).First(); + if (field.ComponentProps.FieldName == "geom") + { + str.Append($" st_astext(\"{column.ComponentProps.FieldName}\") as " + field.Field + ","); + } + else + { + str.Append($" \"{column.ComponentProps.FieldName}\" as " + field.Field + ","); + } + //str.Append($" \"{column.ComponentProps.FieldName}\" as " + field.Field + ","); + } + // var ffield = components.Where(t => t.ComponentProps.FieldName == table.Field).First()?.Field; + //str.Append($"FROM \"{table.Name}\" where \"{table.Field}\" = @keyValue "); + str.Append($"FROM \"{table.Name}\" where \"{table.Field}\" = @keyValue "); + + if (girdTableMap.ContainsKey(table.Name)) + { + var componentModel = girdTableMap[table.Name]; + if (!string.IsNullOrEmpty(componentModel.OrderId)) + { + if (componentModel.isDESC) + { + str.Append($" order by \"{componentModel.OrderId}\" DESC "); + } + else + { + str.Append($" order by \"{componentModel.OrderId}\" "); + } + } + } + + list.Add(new FormDbTable + { + Sql = str.ToString().Replace(",FROM", " FROM"), + //RelationField = table.RelationField, + RelationField = table.Field, + RelationName = table.RelationName, + TableName = table.Name + }); + + GetQueryNew(list, db, table.Name, tableMap, girdTableMap); + } + + + } + public static string GetCsType(DbType dbtype, string dbType) + { + string csType = "string"; + switch (dbType.ToLower()) + { + case "varchar": + csType = "string"; + break; + case "varchar2": + csType = "string"; + break; + case "char": + case "nchar": + case "nvarchar": + case "nvarchar2": + csType = "string"; + break; + case "text": + csType = "string"; + break; + case "ntext": + case "longtext": + case "clob": + case "nclog": + csType = "text"; + break; + case "int2": + csType = "int"; + break; + case "integer": + csType = "int"; + break; + case "int4": + csType = "int"; + break; + case "mediumint":// mysql + case "year": + csType = "int"; + break; + case "bigint": + csType = "long"; + break; + case "smallint": + csType = "short"; + break; + case "bit": + csType = "bool"; + break; + case "tinyint": + csType = "byte"; + break; + case "decimal": + csType = "decimal"; + break; + case "numeric": + csType = "decimal"; + break; + case "number": + csType = "int"; + break; + case "number(8,2)": + csType = "double"; + break; + case "money": + case "bool": + csType = "bool"; + break; + case "smallmoney": + csType = "decimal"; + break; + case "real": + case "double": + if (dbtype == DbType.MySql) + { + csType = "double"; + } + else + { + csType = "float"; + } + break; + case "float": + if (dbtype == DbType.MySql) + { + csType = "float"; + } + else + { + csType = "double"; + } + break; + case "float4": + csType = "double"; + break; + case "float8": + csType = "double"; + break; + case "date": + csType = "DateTime"; + break; + case "datetime": + csType = "DateTime"; + break; + case "datetime2": + csType = "DateTime"; + break; + case "smalldatetime": + csType = "DateTime"; + break; + case "datetimeoffset": + csType = "DateTimeOffset"; + break; + case "time": + //csType = "TimeSpan"; + //break; + csType = "DateTime"; + break; + case "timestamp": + if (dbtype == DbType.MySql) + { + csType = "DateTime"; + + } + else + { + csType = "DateTime"; + } + break; + case "binary": + case "varbinary": + case "image": + + case "tinyblob": + case "blob": + case "mediumblob": + case "longblob": + csType = "byte[]"; + break; + case "uniqueidentifier": + csType = "Guid"; + break; + case "variant": + csType = "Object"; + break; + case "timestamp without time zone": + csType = "TimeSpan"; + break; + // mysql + case "set": + case "enum": + csType = "Enum"; + break; + case "point": + case "linestring": + case "polygon": + case "geometry": + case "multipoint": + case "multilinestring": + case "multipolygon": + //case "geometrycollection": + // csType = "MygisGeometry"; + case "geometrycollection": + csType = "string"; + break; + } + + return csType; + } + /// + /// + /// + /// + /// + public static string SqlFilters(string source) + { + if (string.IsNullOrEmpty(source)) + { + return source; + } + + //去除执行SQL语句的命令关键字 + // source = Regex.Replace(source, "select", "", RegexOptions.IgnoreCase); + source = Regex.Replace(source, "insert", "", RegexOptions.IgnoreCase); + source = Regex.Replace(source, "update", "", RegexOptions.IgnoreCase); + source = Regex.Replace(source, "delete", "", RegexOptions.IgnoreCase); + source = Regex.Replace(source, "drop", "", RegexOptions.IgnoreCase); + source = Regex.Replace(source, "truncate", "", RegexOptions.IgnoreCase); + source = Regex.Replace(source, "declare", "", RegexOptions.IgnoreCase); + source = Regex.Replace(source, "xp_cmdshell", "", RegexOptions.IgnoreCase); + source = Regex.Replace(source, "/add", "", RegexOptions.IgnoreCase); + source = Regex.Replace(source, "net user", "", RegexOptions.IgnoreCase); + //去除执行存储过程的命令关键字 + source = Regex.Replace(source, "exec", "", RegexOptions.IgnoreCase); + source = Regex.Replace(source, "execute", "", RegexOptions.IgnoreCase); + //去除系统存储过程或扩展存储过程关键字 + source = Regex.Replace(source, "xp_", "x p_", RegexOptions.IgnoreCase); + source = Regex.Replace(source, "sp_", "s p_", RegexOptions.IgnoreCase); + //防止16进制注入 + source = Regex.Replace(source, "0x", "0 x", RegexOptions.IgnoreCase); + return source; + } + } +} diff --git a/OpenAuth.App/BaseApp/FormScheme/FormSchemeApp.cs b/OpenAuth.App/BaseApp/FormScheme/FormSchemeApp.cs new file mode 100644 index 0000000..9531ec3 --- /dev/null +++ b/OpenAuth.App/BaseApp/FormScheme/FormSchemeApp.cs @@ -0,0 +1,1776 @@ +using OpenAuth.App.Base; +using OpenAuth.Repository.Domain; +using OpenAuth.Repository; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using SqlSugar; +using OpenAuth.App.Request; +using OpenAuth.App.Response; +using Infrastructure; +using OpenAuth.Repository.Core; +using System.Data; +using System.Text.RegularExpressions; +using OpenAuth.App.FormScheme.Response; +using OpenAuth.App.FormScheme.FormHelpers; +using Autofac.Core; +using Infrastructure.Helpers; +using System.Collections; +using Newtonsoft.Json.Linq; +using OpenAuth.App.FormScheme.Request; +using System.Collections.Concurrent; +using ce.autofac.extension; +using OpenAuth.App.DataCodeRule; +using Infrastructure.Extensions; +using OpenAuth.App.BasicQueryService; +using OpenAuth.App.Permission; +using NPOI.SS.Formula.Functions; +using OpenAuth.App.DataSource; +using System.Data.SqlTypes; +using DocumentFormat.OpenXml.Spreadsheet; +using NPOI.HSSF.UserModel; +using NPOI.SS.UserModel; +using OpenAuth.App.FormModule; +using Microsoft.Extensions.Logging; +using OpenAuth.App.BaseApp.Base; +using Castle.Core.Internal; + +namespace OpenAuth.App.FormScheme +{ + public class FormSchemeApp : SqlSugarBaseApp + { + private ConcurrentDictionary codeDic; + private readonly BaseDataAuthorizeApp _dataAuthorizeApp; + private ILogger _logger; + + public FormSchemeApp(ISugarUnitOfWork unitWork, + ILogger logger, + ISimpleClient repository, BaseDataAuthorizeApp dataAuthorizeApp) + : base(unitWork, repository, null) + { + _logger = logger; + _dataAuthorizeApp = dataAuthorizeApp; + } + + /// + /// 查询表单分页信息 custmerform/scheme/page + /// + /// 关键字 + /// 分类 + /// 状态1启用0未启用2草稿3全部 + /// + /// + /// + /// + public async Task>>> LoadFormPage(string keyword, string category, + int pageIndex, int pageSize, int isEnabled = 3) + { + RefAsync totalCount = 0; + var info = await base.Repository.AsQueryable() + .LeftJoin((a, b) => a.SchemeId == b.Id) + .WhereIF(!string.IsNullOrEmpty(keyword), (a, b) => a.Name.Contains(keyword)) + .WhereIF(!string.IsNullOrEmpty(category), (a, b) => a.Category == category) + //.WhereIF(isEnabled, (a, b) => a.EnabledMark == 1 && b.Type == 1) + .WhereIF(isEnabled != 3, (a, b) => a.EnabledMark == isEnabled) + .OrderByDescending((a, b) => b.CreateDate) + //.WhereIF(type != 0, (a, b) => b.Type == type) + .Select((a, b) => new FormSchemeInfo() + { + Id = a.Id, + Name = a.Name, + Category = a.Category, + SchemeId = a.SchemeId, + EnabledMark = a.EnabledMark, + FormType = a.FormType, + Description = a.Description, + Type = b.Type, + CreateDate = b.CreateDate, + CreateUserId = b.CreateUserId, + CreateUserName = b.CreateUserName + }) + .ToPageListAsync(pageIndex, pageSize, totalCount); + return new Response>> + { + Result = new PageInfo> + { + Items = info, + Total = totalCount + } + }; + } + + #region 添加表单信息 + + /// + /// 保存模板信息 + /// + /// 主键 + /// 模板基础信息 + /// 模板信息 + public async Task> AddForm(string keyValue, FormSchemeInfo schemeInfo, + Repository.Domain.FormScheme formScheme) + { + try + { + if (!string.IsNullOrEmpty(schemeInfo.SchemeId)) + { + var schemeOldEntity = await GetScheme(schemeInfo.SchemeId); + if (schemeOldEntity == null || schemeOldEntity.Type != formScheme.Type || + schemeOldEntity.Scheme != formScheme.Scheme) + { + await SaveEntity(keyValue, schemeInfo, formScheme); + } + else + { + await SaveEntity(keyValue, schemeInfo, null); + } + } + else + { + await SaveEntity(keyValue, schemeInfo, formScheme); + } + + return new Response { Result = true }; + } + catch (Exception ex) + { + return new Response { Result = false }; + } + } + + /// + /// 获取模板的实体 + /// + /// 主键 + /// + public Task GetScheme(string keyValue) + { + return base.Repository.ChangeRepository>() + .GetFirstAsync(r => r.Id == keyValue); + } + + /// + /// 获取模板基础信息的实体 + /// + /// 主键 + /// + public Task GetSchemeInfo(string keyValue) + { + return base.Repository.GetFirstAsync(r => r.Id == keyValue); + } + + /// + /// 保存模板信息 + /// + /// 主键 + /// 模板基础信息 + /// 模板信息 + public async Task SaveSchEntity(string keyValue, FormSchemeInfo schemeInfoEntity, + Repository.Domain.FormScheme schemeEntity) + { + if (!string.IsNullOrEmpty(schemeInfoEntity.SchemeId)) + { + var schemeOldEntity = await GetScheme(schemeInfoEntity.SchemeId); + if (schemeOldEntity == null || schemeOldEntity.Type != schemeEntity.Type || + schemeOldEntity.Scheme != schemeEntity.Scheme) + { + await SaveEntity(keyValue, schemeInfoEntity, schemeEntity); + } + else + { + await SaveEntity(keyValue, schemeInfoEntity, null); + } + } + else + { + await SaveEntity(keyValue, schemeInfoEntity, schemeEntity); + } + } + + /// + /// 保存模板信息 + /// + /// 主键 + /// 模板基础信息 + /// 模板信息 + public async Task> SaveEntity(string keyValue, FormSchemeInfo schemeInfo, + Repository.Domain.FormScheme formScheme) + { + using (SugarDbContext db = base.UnitWork.CreateContext()) + { + if (string.IsNullOrEmpty(keyValue)) + { + schemeInfo.Id = Guid.NewGuid().ToString(); + } + else + { + schemeInfo.Id = keyValue; + } + + #region 模板信息 + + //FormSchemeNewModel formSchemeModel = formScheme.Scheme.ToObject(); + //formSchemeModel.FormType = schemeInfo.FormType; + if (formScheme != null) + { + //formScheme.Scheme = Json.ToJson(formSchemeModel); + formScheme.SchemeInfoId = schemeInfo.Id; + formScheme.Id = Guid.NewGuid().ToString(); + formScheme.CreateDate = DateTime.Now; + formScheme.CreateUserId = schemeInfo.CreateUserId; + formScheme.CreateUserName = schemeInfo.CreateUserName; + await db.Scheme.InsertAsync(formScheme); + schemeInfo.SchemeId = formScheme.Id; + } + + #endregion + + #region 模板基础信息 + + if (!string.IsNullOrEmpty(keyValue)) + { + await db.SchemeInfo.UpdateAsync(schemeInfo); + } + else + { + await db.SchemeInfo.InsertAsync(schemeInfo); + } + + #endregion + + var flag = db.Commit(); + return new Response + { + Result = schemeInfo.Id, + Message = flag == true ? "添加成功" : "添加失败" + }; + } + } + + /// + /// 更新表单模板版本 + /// + /// 模板信息主键 + /// 模板主键 + public async Task> UpdateScheme(string schemeInfoId, string schemeId) + { + Repository.Domain.FormScheme formSchemeEntity = await GetScheme(schemeId); + FormSchemeInfo entity = await base.Repository.GetFirstAsync(r => r.Id == schemeInfoId); + if (formSchemeEntity.Type != 1) + { + entity.EnabledMark = 0; + } + + //忽略为空的数据 + var flag = true; + if (entity != null) + { + entity.SchemeId = schemeId; + flag = await base.Repository.UpdateAsync(entity); + } + + return new Response + { + Result = flag, + Message = flag == true ? "更新成功" : "更新失败" + }; + } + + #endregion + + /// + /// 更新自定义表单模板状态 + /// + /// 模板信息主键 + /// 状态1启用0禁用 + public async Task> UpdateState(string schemeInfoId, int state) + { + FormSchemeInfo entity = await base.Repository.GetFirstAsync(r => r.Id == schemeInfoId); + entity.EnabledMark = state; + var flag = await base.Repository.UpdateAsync(entity); + return new Response + { + Result = flag, + Message = flag == true ? "更新成功" : "更新失败" + }; + } + + /// + /// 删除模板信息 + /// + /// 主键 + public async Task> Delete(string keyValue) + { + using (SugarDbContext db = base.UnitWork.CreateContext()) + { + await db.SchemeInfo.DeleteAsync(t => t.Id == keyValue); + await db.Scheme.DeleteAsync(t => t.SchemeInfoId == keyValue); + var flag = db.Commit(); + return new Response + { + Result = flag, + Message = flag == true ? "删除成功" : "删除失败" + }; + } + } + + /// + /// 获取模板列表 + /// + /// 分页参数 + /// 模板信息主键 + /// + public async Task>>> GetSchemePageList(PageReq pagination, + string schemeInfoId) + { + RefAsync totalCount = 0; + var info = await base.Repository.ChangeRepository>() + .AsQueryable() + .Where(t => t.SchemeInfoId == schemeInfoId) + .OrderByDescending(t => t.CreateDate) + .ToPageListAsync(pagination.page, pagination.limit, totalCount); + + return new Response>> + { + Result = new PageInfo> + { + Items = info, + Total = totalCount + } + }; + } + + #region 扩展方法 + + /// + /// 获取sql列 + /// + /// + /// + /// + public async Task> GetDataColName(string dbCode, string sql, string whereStr = null) + { + sql = this.SqlFilters(sql); + sql = sql.Replace("=@param", " is not null"); + sql = sql.Replace("= @param", " is not null"); + using (SugarDbContext db = base.UnitWork.CreateContext()) + { + DataTable dt = await db.Db.SqlQueryable(sql).Where(whereStr).ToDataTablePageAsync(1, 1); + List res = new List(); + foreach (DataColumn item in dt.Columns) + { + res.Add(item.ColumnName.ToLower()); + } + + return res; + } + } + + /// + /// + /// + /// + /// + public string SqlFilters(string source) + { + if (string.IsNullOrEmpty(source)) + { + return source; + } + + //去除执行SQL语句的命令关键字 + // source = Regex.Replace(source, "select", "", RegexOptions.IgnoreCase); + source = Regex.Replace(source, " insert ", "", RegexOptions.IgnoreCase); + source = Regex.Replace(source, " update ", "", RegexOptions.IgnoreCase); + source = Regex.Replace(source, " delete ", "", RegexOptions.IgnoreCase); + source = Regex.Replace(source, " drop ", "", RegexOptions.IgnoreCase); + source = Regex.Replace(source, " truncate ", "", RegexOptions.IgnoreCase); + source = Regex.Replace(source, " declare ", "", RegexOptions.IgnoreCase); + source = Regex.Replace(source, " xp_cmdshell ", "", RegexOptions.IgnoreCase); + source = Regex.Replace(source, " /add ", "", RegexOptions.IgnoreCase); + source = Regex.Replace(source, " net user ", "", RegexOptions.IgnoreCase); + //去除执行存储过程的命令关键字 + source = Regex.Replace(source, " exec ", "", RegexOptions.IgnoreCase); + source = Regex.Replace(source, " execute ", "", RegexOptions.IgnoreCase); + //去除系统存储过程或扩展存储过程关键字 + source = Regex.Replace(source, "xp_", "x p_", RegexOptions.IgnoreCase); + source = Regex.Replace(source, "sp_", "s p_", RegexOptions.IgnoreCase); + //防止16进制注入 + source = Regex.Replace(source, "0x", "0 x", RegexOptions.IgnoreCase); + return source; + } + + ///// + ///// 获取自定义表单数据权限查询条件 + ///// + ///// 自定义表单功能主键 + ///// + //public async Task GetWhereSql(string code) + //{ + // var numIndex = new string[26] {"A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", + // "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z" }; + + + // // 获取数据权限配置信息 + // List objectIds = new List(); + // objectIds.Add(userInfo.F_UserId); + // objectIds.AddRange(roleIds); + // var list = await service.GetList(code, objectIds.ToArray()); + + // var strRes = new StringBuilder(); + // int sqlNum = 0; + // foreach (var item in list) + // { + + // if (sqlNum > 0) + // { + // strRes.Append(" OR ( "); + // } + // else + // { + // strRes.Append(" ( "); + // } + + // string strSql = ""; + // var itemFormula = item.F_Formula.ToObject(); + // if (!string.IsNullOrEmpty(itemFormula.formula)) + // { + // strSql = itemFormula.formula; + // for (int i = 1; i < itemFormula.Conditions.Count + 1; i++) + // { + // var conditionItem = itemFormula.Conditions[i - 1]; + // conditionItem.index = i; + + // strSql = strSql.Replace("" + i, "{@learun" + i + "learun@}"); + // } + + // if (itemFormula.Formulas != null && itemFormula.Formulas.Count > 0) + // { + // for (int i = 0; i < itemFormula.Formulas.Count; i++) + // { + // strSql = strSql.Replace(numIndex[i], "{@learun" + numIndex[i] + "learun@}"); + // } + // } + // } + // else + // { + // // 默认公式 + // for (int i = 1; i < itemFormula.Conditions.Count + 1; i++) + // { + + // var conditionItem = itemFormula.Conditions[i - 1]; + // conditionItem.index = i; + + // if (string.IsNullOrEmpty(conditionItem.Group)) + // { + // if (strSql != "") + // { + // strSql += " AND "; + // } + // strSql += " {@learun" + i + "learun@} "; + // } + // } + + // if (itemFormula.Formulas != null && itemFormula.Formulas.Count > 0) + // { + // for (int i = 0; i < itemFormula.Formulas.Count; i++) + // { + + // if (itemFormula.Conditions.FindIndex(t => t.Group == numIndex[i]) != -1) + // { + // if (strSql != "") + // { + // strSql += " AND "; + // } + // strSql += " {@learun" + numIndex[i] + "learun@} "; + // } + // } + // } + + // } + + + // // 分组公式处理 + // if (itemFormula.Formulas != null && itemFormula.Formulas.Count > 0) + // { + + + // for (int i = 0; i < itemFormula.Formulas.Count; i++) + // { + // var groupSql = new StringBuilder(); + + // var groupList = itemFormula.Conditions.FindAll(t => t.Group == numIndex[i]); + // if (groupList.Count > 0) + // { + + // groupSql.Append($" {groupList[0].RelationField} in ( SELECT {groupList[0].Field} FROM {groupList[0].Table} WHERE "); + + // // 初始化分组公式 + // var groupSqlWhere = ""; + // if (string.IsNullOrEmpty(itemFormula.Formulas[i].Value)) + // { + // foreach (var groupItem in groupList) + // { + // if (groupSqlWhere != "") + // { + // groupSqlWhere += " AND "; + // } + // groupSqlWhere += " {@learun" + groupItem.index + "learun@} "; + // } + // } + // else + // { + // groupSqlWhere = itemFormula.Formulas[i].Value; + // foreach (var groupItem in groupList) + // { + // groupSqlWhere = groupSqlWhere.Replace("" + groupItem.index, "{@learun" + groupItem.index + "learun@}"); + // } + // } + + + // foreach (var groupItem in groupList) + // { + // var strone = new StringBuilder(); + + + // if (groupItem.F_Symbol == 8) + // { + // strone.Append($" ({groupItem.Cfield} "); + // } + // else + // { + // strone.Append($" {groupItem.Cfield} "); + // } + + + // string value = await GetValue(groupItem.F_FiledValueType, groupItem.F_FiledValue); + + // switch (groupItem.F_Symbol) + // { + // case 1:// 等于 + // strone.Append($" = {value}"); + // break; + // case 2:// 大于 + // strone.Append($" > {value}"); + // break; + // case 3:// 大于等于 + // strone.Append($" >= {value}"); + // break; + // case 4:// 小于 + // strone.Append($" < {value}"); + // break; + // case 5:// 小于等于 + // strone.Append($" <= {value}"); + // break; + // case 6:// 包含 + // value = value.Replace("'", ""); + // strone.Append($" like '%{value}%'"); + // break; + // case 7:// 包含于 + // value = value.Replace(",", "','"); + // strone.Append($" in ({value})"); + // break; + // case 8:// 不等于 + // strone.Append($" != {value} or {groupItem.Cfield} is null ) "); + // break; + // case 9:// 不包含 + // value = value.Replace("'", ""); + // strone.Append($" not like '%{value}%'"); + // break; + // case 10:// 不包含于 + // value = value.Replace(",", "','"); + // strone.Append($" not in ({value})"); + // break; + // default: + // break; + // } + + // groupSqlWhere = groupSqlWhere.Replace("{@learun" + groupItem.index + "learun@}", strone.ToString()); + // } + // groupSql.Append(groupSqlWhere); + // groupSql.Append(" ) "); + + + // strSql = strSql.Replace("{@learun" + numIndex[i] + "learun@}", groupSql.ToString()); + // } + // } + // } + + + // int num = 1; + // foreach (var conditionItem in itemFormula.Conditions) + // { + // if (string.IsNullOrEmpty(conditionItem.Group)) + // { + // var strone = new StringBuilder(); + // if (conditionItem.Type == "glbd") + // { + // strone.Append($" {conditionItem.RelationField} in ( SELECT {conditionItem.Field} FROM {conditionItem.Table} WHERE {conditionItem.Cfield} "); + // } + // else + // { + // if (conditionItem.F_Symbol == 8) + // { + // strone.Append($" ( {conditionItem.F_FieldId}"); + // } + // else + // { + // strone.Append($" {conditionItem.F_FieldId}"); + // } + + // } + + + // string value = await GetValue(conditionItem.F_FiledValueType, conditionItem.F_FiledValue); + + // switch (conditionItem.F_Symbol) + // { + // case 1:// 等于 + // strone.Append($" = {value}"); + // break; + // case 2:// 大于 + // strone.Append($" > {value}"); + // break; + // case 3:// 大于等于 + // strone.Append($" >= {value}"); + // break; + // case 4:// 小于 + // strone.Append($" < {value}"); + // break; + // case 5:// 小于等于 + // strone.Append($" <= {value}"); + // break; + // case 6:// 包含 + // value = value.Replace("'", ""); + // strone.Append($" like '%{value}%'"); + // break; + // case 7:// 包含于 + // value = value.Replace(",", "','"); + // strone.Append($" in ({value})"); + // break; + // case 8:// 不等于 + // strone.Append($" != {value}"); + + // if (conditionItem.Type == "glbd") + // { + // strone.Append($" or {conditionItem.Cfield} is null "); + // } + // else + // { + // strone.Append($" or {conditionItem.F_FieldId} is null ) "); + // } + + + // break; + // case 9:// 不包含 + // value = value.Replace("'", ""); + // strone.Append($" not like '%{value}%'"); + // break; + // case 10:// 不包含于 + // value = value.Replace(",", "','"); + // strone.Append($" not in ({value})"); + // break; + // default: + // break; + // } + // if (conditionItem.Type == "glbd") + // { + // strone.Append(" ) "); + // } + // else + // { + // strone.Append(" "); + // } + + // strSql = strSql.Replace("{@learun" + num + "learun@}", strone.ToString()); + // } + + + // num++; + // } + // strRes.Append(strSql); + // strRes.Append(" ) "); + + // sqlNum++; + // } + + // return strRes.ToString(); + //} + + /// + /// 分页查询方法,返回datatable + /// + /// sql语句 + /// sql参数 + /// 查询条件 + /// 查询参数 + /// 分页参数 + /// + public async Task FindTable(string sqlStr, object sqlparam, PaginationInputDto pagination, + string whereStr = null, object whereParam = null) + { + var (list, total) = await FindTableByPage(sqlStr, sqlparam, whereStr, whereParam, "", pagination.rows, + pagination.page); + pagination.rows = (int)total; + return DBCommonHelper.DtColToLow(list); + } + + /// + /// 分页查询方法,返回datatable + /// + /// datatable数据 + /// sql语句 + /// sql参数 + /// 查询条件 + /// 查询参数 + /// 排序字段 + /// 每页数据条数 + /// 页码 + /// list数据,total 总共条数 + private async Task<(DataTable list, long total)> FindTableByPage(string sqlStr, object sqlparam, + string whereStr, object whereParam, string orderSql, int pageSize, int pageIndex) + { + try + { + using (SugarDbContext db = base.UnitWork.CreateContext()) + { + RefAsync total = 0; + var sqlQueryable = db.Db.SqlQueryable(sqlStr); + if (sqlparam != null) + { + sqlQueryable = sqlQueryable.AddParameters(sqlparam); + } + + if (!string.IsNullOrEmpty(whereStr)) + { + sqlQueryable = sqlQueryable.Where(whereStr, whereParam); + } + + if (!string.IsNullOrEmpty(orderSql)) + { + sqlQueryable = sqlQueryable.OrderBy(orderSql); + } + + var dt = await sqlQueryable.ToDataTablePageAsync(pageIndex, pageSize, total); + return (dt, total); + } + } + catch (Exception ex) + { + throw ex; + } + } + + /// + /// 查询方法,返回datatable + /// + /// datatable数据 + /// sql语句 + /// 参数 + public async Task FindTable(string sql, object sqlparam = null) + { + try + { + using (SugarDbContext db = base.UnitWork.CreateContext()) + { + var sqlQueryable = db.Db.SqlQueryable(sql); + if (sqlparam != null) + { + sqlQueryable = sqlQueryable.AddParameters(sqlparam); + } + var dt = await sqlQueryable.ToDataTableAsync(); + return DBCommonHelper.DtColToLow(dt); + } + } + catch (Exception ex) + { + throw ex; + } + } + + /// + /// 查询方法,返回datatable + /// + /// datatable数据 + /// sql语句 + /// 参数 + public async Task> FindTableNew(string sql, object sqlparam = null) + { + try + { + List list = new List(); + using (SugarDbContext db = base.UnitWork.CreateContext()) + { + var sqlQueryable = db.Db.SqlQueryable(sql); + if (sqlparam != null) + { + sqlQueryable = sqlQueryable.AddParameters(sqlparam); + } + + var dt = await sqlQueryable.ToDataTableAsync(); + // 遍历每一行 + foreach (DataRow row in dt.Rows) + { + // 遍历每一列 + foreach (DataColumn col in dt.Columns) + { + // 获取当前列的值 + var value = row[col]; + + var colvalue = new { col.ColumnName, value }; + list.Add(colvalue); + } + } + //foreach (DataColumn item in dt.Columns) + //{ + // item.ColumnName = item.ColumnName.ToLower(); + // foreach (DataColumn column in dataTable.Columns) + // { + // var colvalue = new { item.ColumnName, dt.Rows.DefaultValue }; + // } + + // list.Add(colvalue); + //} + return list; + } + } + catch (Exception ex) + { + throw ex; + } + } + + /// + /// 获取编码 + /// + /// 字段值 + /// + public async Task GetRuleCodeEx(object value) + { + var code = value; + if (value != null && value.ToString().IndexOf("code_") != -1) + { + if (codeDic == null) + { + codeDic = new ConcurrentDictionary(); + } + + var codeList = value.ToString().Replace("code_", "").Split("|"); + if (codeDic.ContainsKey(codeList[1])) + { + codeDic.TryGetValue(codeList[1], out code); + } + else + { + code = await this.GetRuleCode(codeList[0]); + codeDic.TryAdd(codeList[1], code); + } + } + + return code; + } + + /// + /// 获取编码 + /// + /// 编码规则编码 + /// + public Task GetRuleCode(string code) + { + var codeRuleApp = IocManager.Instance.GetService(); + return codeRuleApp.GetBillCode(code); + } + + #endregion + + #region 代码发布表单 + + /// + /// 获取分页数据 + /// + /// 功能id + /// 桌面参数 + /// 模板信息主键 + /// 分页参数 + /// 查询条件 + /// + public async Task>> GetFormDataPage(string mid, string desktopParam, + string schemeId, PaginationInputDto pagination, string queryJson) + { + Repository.Domain.FormScheme schemeEntity = await GetScheme(schemeId); + if (schemeEntity == null) + { + return new Response> + { + Result = new PageInfo + { + } + }; + } + + FormSchemeModel formSchemeModel = schemeEntity.Scheme.ToObject(); + + + var query = FormHelper.GetQuerySql(formSchemeModel, queryJson); + query.Sql = query.Sql.Replace("{LEARUN_SASSID}", ""); + string sql = ""; + if (!string.IsNullOrEmpty(mid)) + { + sql = await _dataAuthorizeApp.GetWhereSql(mid); + if (!string.IsNullOrEmpty(sql)) + { + query.Sql = $" select * from ({query.Sql})t where {sql} "; + } + } + + //if (!string.IsNullOrEmpty(desktopParam)) + //{ + // var desktopSql = await _iDesktopBLL.GetSql(desktopParam); + + // if (string.IsNullOrEmpty(sql)) + // { + // query.Sql = $" select * from ({query.Sql})t where {desktopSql} "; + // } + // else + // { + // query.Sql = $" {query.Sql} AND ( {desktopSql} ) "; + // } + //} + var dt = await FindTable(query.Sql, query.DbParameter, pagination); + return new Response> + { + Result = new PageInfo + { + Items = dt, + Total = pagination.rows + } + }; + } + + //去掉tablist + public async Task>> GetFormDataNewPage(string mid, string desktopParam, + string schemeId, PaginationInputDto pagination, string queryJson) + { + Repository.Domain.FormScheme schemeEntity = await GetScheme(schemeId); + if (schemeEntity == null) + { + return new Response> + { + Result = new PageInfo + { + } + }; + } + + FormSchemeNewModel formSchemeModel = schemeEntity.Scheme.ToObject(); + + + var query = FormHelper.GetQuerySqlNew(formSchemeModel, queryJson); + query.Sql = query.Sql.Replace("{LEARUN_SASSID}", ""); + string sql = ""; + if (!string.IsNullOrEmpty(mid)) + { + sql = await _dataAuthorizeApp.GetWhereSql(mid); + if (!string.IsNullOrEmpty(sql)) + { + query.Sql = $" select * from ({query.Sql})t where {sql} "; + } + } + + //if (!string.IsNullOrEmpty(desktopParam)) + //{ + // var desktopSql = await _iDesktopBLL.GetSql(desktopParam); + + // if (string.IsNullOrEmpty(sql)) + // { + // query.Sql = $" select * from ({query.Sql})t where {desktopSql} "; + // } + // else + // { + // query.Sql = $" {query.Sql} AND ( {desktopSql} ) "; + // } + //} + var dt = new DataTable(); + if (pagination != null) + { + dt = await FindTable(query.Sql, query.DbParameter, pagination); + return new Response> + { + Result = new PageInfo + { + Items = dt, + Total = pagination.rows + } + }; + } + else + { + dt = await FindTable(query.Sql, query.DbParameter); + return new Response> + { + Result = new PageInfo + { + Items = dt, + } + }; + } + } + + /// + /// 获取列表数据 + /// + /// 功能id + /// 桌面参数 + /// 模板信息主键 + /// 查询条件 + /// 排序字段 + /// + public async Task GetFormDataList(string mid, string schemeId, string queryJson, string sidx) + { + Repository.Domain.FormScheme schemeEntity = await GetScheme(schemeId); + FormSchemeModel formSchemeModel = schemeEntity.Scheme.ToObject(); + var query = FormHelper.GetQuerySql(formSchemeModel, queryJson); + + query.Sql = query.Sql.Replace("{LEARUN_SASSID}", ""); + string sql = ""; + if (!string.IsNullOrEmpty(mid)) + { + //sql = await this.GetDataAuthoritySql(mid); + if (!string.IsNullOrEmpty(sql)) + { + query.Sql = $" select * from ({query.Sql})t where {sql} "; + } + } + + //if (!string.IsNullOrEmpty(desktopParam)) + //{ + // var desktopSql = await _iDesktopBLL.GetSql(desktopParam); + + // if (string.IsNullOrEmpty(sql)) + // { + // query.Sql = $" select * from ({query.Sql})t where {desktopSql} "; + // } + // else + // { + // query.Sql = $" {query.Sql} AND ( {desktopSql} ) "; + // } + //} + + // 排序字段 + if (!string.IsNullOrEmpty(sidx)) + { + query.Sql += $" order by {sidx}"; + } + + + if (formSchemeModel.FormType == 1) + { + Dictionary DbParameter = new Dictionary(); + foreach (var param in query.DbParameter) + { + DbParameter.Add(param.ParameterName, param.Value); + } + + return await FindTable(query.Sql, DbParameter); + } + + return await FindTable(query.Sql, query.DbParameter); + } + + /// + /// 获取表单数据 + /// + /// 模板信息主键 + /// 主键名 + /// 主键值 + /// + public async Task> GetFormData(string schemeId, string key, string keyValue) + { + Repository.Domain.FormScheme schemeEntity = await GetScheme(schemeId); + FormSchemeModel formSchemeModel = schemeEntity.Scheme.ToObject(); + var list = FormHelper.GetQuery(formSchemeModel, key); + var res = new Dictionary(); + foreach (var item in list) + { + item.Sql = item.Sql.Replace("{LEARUN_SASSID}", ""); + if (string.IsNullOrEmpty(item.RelationName)) + { + var dt = await FindTable(item.Sql, new { keyValue }); + res.Add(item.TableName, dt); + } + else + { + var dt = await FindTable(item.Sql, + new { keyValue = res[item.RelationName].Rows[0][item.RelationField.ToLower()] }); + res.Add(item.TableName, dt); + } + } + + return res; + } + + /// + /// 获取表单数据 + /// + /// 模板信息主键 + /// 主键名 + /// 主键值 + /// + public async Task> GetFormDataNew(string schemeId, string key, string keyValue) + { + // 表单 + Repository.Domain.FormScheme schemeEntity = await GetScheme(schemeId); + FormSchemeNewModel formSchemeModel = schemeEntity.Scheme.ToObject(); + var list = FormHelper.GetQueryNew(formSchemeModel, key); + var res = new Dictionary(); + foreach (var item in list) + { + item.Sql = item.Sql.Replace("{LEARUN_SASSID}", ""); + if (string.IsNullOrEmpty(item.RelationName)) + { + var dt = await FindTable(item.Sql, new { keyValue }); + res.Add(item.TableName, dt); + } + else + { + //var dt = await FindTable(item.Sql, new { keyValue = res[item.RelationName].Rows[0][item.RelationField.ToLower()] }); + var dt = await FindTable(item.Sql, new { keyValue }); + res.Add(item.TableName, dt); + } + } + + return res; + } + + /// + /// 保存表单数据 + /// + /// 参数 + /// + public async Task> SaveFormData(FormDataReq dto) + { + Repository.Domain.FormScheme schemeEntity = await GetScheme(dto.SchemeId); + FormSchemeModel formSchemeModel = schemeEntity.Scheme.ToObject(); + var dataJson = dto.Data.ToJObject(); + var list = FormHelper.GetSaveSql(formSchemeModel, dataJson, dto.Pkey, dto.PkeyValue, dto.IsUpdate); + using (SugarDbContext db = base.UnitWork.CreateContext()) + { + foreach (var item in list) + { + // 遍历参数 + foreach (var p in item.DbParameter) + { + p.Value = await GetRuleCodeEx(p.Value); + } + + var properties = dataJson.Properties(); + foreach (var item2 in properties) + { + if (item2.Value.ToString() != null && item2.Value.ToString().IndexOf("code_") != -1) + { + dataJson[item2.Name] = (await GetRuleCodeEx(item2.Value.ToString())).ToString(); + } + } + + switch (item.ExecuteType) + { + case ExecuteType.Insert: + await db.Db.Ado.ExecuteCommandAsync(ExecuteInsert(item.TableName, item.DbParameter), + item.DbParameter); + break; + case ExecuteType.Update: + await db.Db.Ado.ExecuteCommandAsync( + ExecuteUpdate(item.TableName, item.DbParameter, item.Pkey), item.DbParameter); + break; + case ExecuteType.Delete: + await db.Db.Ado.ExecuteCommandAsync(ExecuteDelete(item.TableName, item.DbParameter), + item.DbParameter); + break; + } + } + + + if (dto.DiffFormData != null) + { + //获取表单更改历史记录信息 add by torres + var difflist = GetSaveHistoryList(dto.DiffFormData, dto.Pkey, dto.PkeyValue, dto.SchemeId, + schemeEntity.SchemeInfoId); + if (difflist.Count > 0) + { + if (formSchemeModel.FormInfo.HistoryType == "2") + { + await db.FormDataHistory.InsertRangeAsync(difflist); + } + else if (formSchemeModel.FormInfo.HistoryType == "1") + { + //await formSchemeService.BaseRepository().Inserts(difflist); + await db.FormDataHistory.InsertRangeAsync(difflist); //待定 + } + } + } + + var flag = db.Commit(); + return new Response + { + Result = dataJson, + Message = flag == true ? "提交成功" : "提交失败" + }; + } + } + + /// + /// 保存表单数据 + /// + /// 参数 + /// + public async Task> SaveFormDataNew(FormDataReq dto) + { + Repository.Domain.FormScheme schemeEntity = await GetScheme(dto.SchemeId); + FormSchemeNewModel formSchemeModel = schemeEntity.Scheme.ToObject(); + //数据为空时,不执行保存 + if (string.IsNullOrEmpty(dto.Data) || dto.Data == "{}") + { + throw new Exception("数据不能为空"); + } + var dataJson = dto.Data.ToJObject(); + _logger.LogError("保存表单数据接收: " + Newtonsoft.Json.JsonConvert.SerializeObject(dto)); + var list = FormHelper.GetSaveSqlNew(formSchemeModel, dataJson, dto.Pkey, dto.PkeyValue, dto.IsUpdate); + using (SugarDbContext db = base.UnitWork.CreateContext()) + { + foreach (var item in list) + { + // 遍历参数 + foreach (var p in item.DbParameter) + { + p.Value = await GetRuleCodeEx(p.Value); + } + + var properties = dataJson.Properties(); + foreach (var item2 in properties) + { + if (item2.Value.ToString() != null && item2.Value.ToString().IndexOf("code_") != -1) + { + dataJson[item2.Name] = (await GetRuleCodeEx(item2.Value.ToString())).ToString(); + } + } + + switch (item.ExecuteType) + { + case ExecuteType.Insert: + await db.Db.Ado.ExecuteCommandAsync(ExecuteInsert(item.TableName, item.DbParameter), + item.DbParameter); + break; + case ExecuteType.Update: + await db.Db.Ado.ExecuteCommandAsync( + ExecuteUpdate(item.TableName, item.DbParameter, item.Pkey), item.DbParameter); + break; + case ExecuteType.Delete: + await db.Db.Ado.ExecuteCommandAsync(ExecuteDelete(item.TableName, item.DbParameter), + item.DbParameter); + break; + } + } + + + if (dto.DiffFormData != null) + { + //获取表单更改历史记录信息 add by torres + var difflist = GetSaveHistoryList(dto.DiffFormData, dto.Pkey, dto.PkeyValue, dto.SchemeId, + schemeEntity.SchemeInfoId); + if (difflist.Count > 0) + { + await db.FormDataHistory.InsertRangeAsync(difflist); + //if (formSchemeModel.FormInfo.HistoryType == "2") + //{ + // await db.FormDataHistory.InsertRangeAsync(difflist); + //} + //else if (formSchemeModel.FormInfo.HistoryType == "1") + //{ + // //await formSchemeService.BaseRepository().Inserts(difflist); + // await db.FormDataHistory.InsertRangeAsync(difflist);//待定 + //} + } + } + + var flag = db.Commit(); + return new Response + { + Result = dataJson, + Message = flag == true ? "提交成功" : "提交失败" + }; + } + } + + #region 历史记录实体列表 + + /// + /// 获取历史记录实体列表 + /// + /// 更新的字段信息 + /// 主键字段 + /// 主键值 + /// 表单配置id + /// 表单信息id + /// + public List GetSaveHistoryList(List historyInfo, string pkey, + string pkeyValue, string schemeId, string f_schemeInfoId) + { + if (historyInfo != null && historyInfo.Count > 0) + { + foreach (var item in historyInfo) + { + item.Id = Guid.NewGuid().ToString(); + item.TablePKey = pkey; + item.TablePKeyValue = pkeyValue; + item.SchemeId = schemeId; + item.SchemeInfoId = f_schemeInfoId; + item.CreaterTime = DateTime.Now; + } + } + + return historyInfo; + } + + #endregion + + #region 拼接字符串 + + /// + /// 插入数据 + /// + /// 表名 + /// 参数 + /// + public string ExecuteInsert(string tableName, List dbParameters) + { + var sql = new StringBuilder(); + var sqlValue = new StringBuilder(); + + sql.Append($" INSERT INTO {tableName} ( \""); + sqlValue.Append($" ( "); + bool isfirt = true; + foreach (var item in dbParameters) + { + if (!isfirt) + { + sql.Append(",\""); + sqlValue.Append(","); + } + + sql.Append($"{item.ParameterName}\""); + sqlValue.Append($"@{item.ParameterName}"); + isfirt = false; + } + + sql.Append($" ) VALUES {sqlValue} )"); + + //var strSql = DBCommonHelper.SqlFormat(sql.ToString(), _dataType); + + return sql.ToString(); + } + + /// + /// 更新数据 + /// + /// 表名 + /// 参数 + /// 主键名 + /// + public string ExecuteUpdate(string tableName, List dbParameters, string pkey) + { + var sql = new StringBuilder(); + sql.Append($" UPDATE {tableName} SET "); + + bool isfirt = true; + foreach (var item in dbParameters) + { + if (item.Value == null) + { + if (!isfirt) + { + sql.Append(","); + } + + sql.Append($"\"{item.ParameterName}\" = null "); + isfirt = false; + } + else + { + if (!isfirt) + { + sql.Append(","); + } + + sql.Append($"\"{item.ParameterName}\" = @{item.ParameterName} "); + isfirt = false; + } + } + + sql.Append($" WHERE \"{pkey}\" = @{pkey} "); + + return sql.ToString(); + } + + /// + /// 删除表数据 + /// + /// 表名 + /// 参数 + /// + public string ExecuteDelete(string tableName, List dbParameters) + { + var sql = new StringBuilder(); + sql.Append($" DELETE FROM {tableName} WHERE 1=1 "); + + foreach (var item in dbParameters) + { + sql.Append($" AND \"{item.ParameterName}\" = @{item.ParameterName} "); + } + + return sql.ToString(); + } + + #endregion + + /// + /// 删除表单数据 + /// + /// 模板信息主键 + /// 主键名 + /// 主键值 + /// + public async Task> DeleteFormData(string schemeId, string key, string keyValue) + { + Repository.Domain.FormScheme schemeEntity = await GetScheme(schemeId); + FormSchemeNewModel formSchemeModel = schemeEntity.Scheme.ToObject(); + + var list = FormHelper.GetQueryNew(formSchemeModel, key); + var res = new Dictionary(); + foreach (var item in list) + { + if (string.IsNullOrEmpty(item.RelationName)) + { + var dt = await FindTable(item.Sql, new { keyValue }); + res.Add(item.TableName, dt); + } + else + { + var dt = await FindTable(item.Sql, new { keyValue }); + res.Add(item.TableName, dt); + } + } + + var deleteList = FormHelper.GetDeleteSqlNew(formSchemeModel, key); + var flag = false; + using (SugarDbContext db = base.UnitWork.CreateContext()) + { + try + { + foreach (var item in deleteList) + { + if (string.IsNullOrEmpty(item.RelationName)) + { + await db.Db.Ado.ExecuteCommandAsync(item.Sql, new { keyValue }); + } + else + { + await db.Db.Ado.ExecuteCommandAsync(item.Sql, new { keyValue }); + } + } + + flag = db.Commit(); + } + catch + { + flag = false; + } + + return new Response + { + Result = flag == true ? "success" : "fail", + Message = flag == true ? "删除成功" : "删除失败" + }; + } + } + + #endregion + + #region 获取矢量切片 + + /// + /// 根据表名获取矢量切片 + /// + /// + /// + public byte[] QueryVectorTileByTable(QueryVectorTileByTableReq req) + { + VectorTileSearchModel searchModel = new VectorTileSearchModel + { + x = req.x, + y = req.y, + z = req.z, + field = req.field, + table = req.table, + filter = req.filter, + }; + var result = new CommonDataManager(base.UnitWork.CreateContext().Db).VectorTile(searchModel); + return result; + } + + #endregion + + #region 导入 + + /// + /// + /// + /// 表单设计id + /// 数据 + /// + public async Task<(DataTable elist, int snum, int fnum)> ImportTable(string id, DataTable dt, string pkey) + { + int snum = 0; + int fnum = 0; + //解析表单 + List list = new List(); + Repository.Domain.FormScheme schemeEntity = await GetScheme(id); + FormSchemeNewModel formSchemeModel = schemeEntity.Scheme.ToObject(); + FormDbTableInfo mainTable = formSchemeModel.Db.Find(t => t.Type == "main"); + foreach (var tab in formSchemeModel.FormInfo.TabList) + { + foreach (var component in tab.Schemas) + { + if (component == null || component.ComponentProps == null || + string.IsNullOrEmpty(component.ComponentProps.DataTable)) + { + continue; + } + + ExcelImportFileds excelImportFileds = new ExcelImportFileds(); + excelImportFileds.ColName = component.Label; + excelImportFileds.Name = component.ComponentProps.FieldName; + excelImportFileds.CsType = component.CsType; + excelImportFileds.RelationType = component.ComponentProps.FieldName == pkey ? 1 : 0; + list.Add(excelImportFileds); + } + } + + DataTable failDt = new DataTable(); + dt.Columns.Add("导入错误", typeof(string)); + foreach (DataColumn dc in dt.Columns) + { + failDt.Columns.Add(dc.ColumnName, dc.DataType); + } + + if (dt.Rows.Count > 0) + { + using (SugarDbContext db = base.UnitWork.CreateContext()) + { + foreach (DataRow dr in dt.Rows) + { + // 行数据循环 + try + { + List dbParameters = new List(); + foreach (var col in list) + { + object paramValue = null; + + switch (col.RelationType) + { + case 0: //无关联 + paramValue = dr[col.ColName].ToString(); + break; + case 1: //GUID + paramValue = Guid.NewGuid().ToString(); + break; + case 2: //数据字典 + + break; + case 3: //数据表 + + break; + case 4: //固定值 + paramValue = col.Value; + break; + case 5: //操作人ID + paramValue = ""; + break; + case 6: //操作人名字 + paramValue = ""; + break; + case 7: //操作时间 + paramValue = DateTime.Now; + break; + } + + dbParameters.Add(FormHelper.GetMyDbParameter(col.Name, paramValue.ToString(), + col.CsType)); + } + + await db.Db.Ado.ExecuteCommandAsync(ExecuteInsert(mainTable.Name, dbParameters), + dbParameters); + db.Commit(); + snum++; + } + catch (Exception ex) + { + fnum++; + + dr["导入错误"] = ex.Message; + failDt.Rows.Add(dr.ItemArray); + } + } + } + } + + return (failDt, snum, fnum); + } + + //导出模板 + public async Task> ExportTemplate(string id, DataTable dt, string pkey) + { + Response response = new Response(); + try + { + //解析表单 + List list = new List(); + Repository.Domain.FormScheme schemeEntity = await GetScheme(id); + FormSchemeNewModel formSchemeModel = schemeEntity.Scheme.ToObject(); + FormDbTableInfo mainTable = formSchemeModel.Db.Find(t => t.Type == "main"); + foreach (var tab in formSchemeModel.FormInfo.TabList) + { + foreach (var component in tab.Schemas) + { + if (component == null || component.ComponentProps == null || + string.IsNullOrEmpty(component.ComponentProps.DataTable)) + { + continue; + } + + ExcelImportFileds excelImportFileds = new ExcelImportFileds(); + excelImportFileds.ColName = component.Label; + excelImportFileds.Name = component.ComponentProps.FieldName; + excelImportFileds.CsType = component.CsType; + excelImportFileds.RelationType = component.ComponentProps.FieldName == pkey ? 1 : 0; + list.Add(excelImportFileds); + } + } + + HSSFWorkbook workbook = new HSSFWorkbook(); + ISheet sheet = workbook.CreateSheet(); + + #region 内容样式 + + IFont font1 = workbook.CreateFont(); //创建一个字体样式对象 + font1.FontName = "Microsoft YaHei"; //和excel里面的字体对应 + //font1.Boldweight = short.MaxValue;//字体加粗 + font1.FontHeightInPoints = 12; //字体大小 + ICellStyle style = workbook.CreateCellStyle(); //创建样式对象 + style.BorderBottom = BorderStyle.Thin; + style.BorderLeft = BorderStyle.Thin; + style.BorderRight = BorderStyle.Thin; + style.BorderTop = BorderStyle.Thin; + style.Alignment = NPOI.SS.UserModel.HorizontalAlignment.Center; + style.VerticalAlignment = VerticalAlignment.Center; + style.SetFont(font1); //将字体样式赋给样式对象 + style.WrapText = true; + + #endregion + + #region 标题样式 + + IFont font = workbook.CreateFont(); //创建一个字体样式对象 + font.FontName = "Microsoft YaHei"; //和excel里面的字体对应 + font.Boldweight = (short)FontBoldWeight.Bold; //字体加粗 + font.FontHeightInPoints = 12; //字体大小 + ICellStyle style1 = workbook.CreateCellStyle(); //创建样式对象 + style1.BorderBottom = BorderStyle.Thin; + style1.BorderLeft = BorderStyle.Thin; + style1.BorderRight = BorderStyle.Thin; + style1.BorderTop = BorderStyle.Thin; + style1.Alignment = NPOI.SS.UserModel.HorizontalAlignment.Center; + style1.VerticalAlignment = VerticalAlignment.Center; + style1.SetFont(font); //将字体样式赋给样式对象 + + #endregion + + #region 创建表头 + + IRow rowHeader = sheet.CreateRow(0); + rowHeader.Height = 20 * 30; + int j = 0; + foreach (var i in list) + { + rowHeader.CreateCell(j); + rowHeader.Cells[j].CellStyle = style1; + rowHeader.Cells[j].SetCellValue(i.ColName); + sheet.SetColumnWidth(j, 20 * 350); + j++; + } + + #endregion + + response.Result = new MemoryStream(); + workbook.Write(response.Result); + workbook = null; + response.Result.Close(); + response.Result.Dispose(); + response.Code = 200; + response.Message = "获取成功"; + } + catch (Exception ex) + { + response.Code = 500; + response.Message = ex.Message; + } + + return response; + } + + public async Task> ExportTemplateNew(string id, DataTable dt, string pkey) + { + Response response = new Response(); + try + { + //解析表单 + List list = new List(); + Repository.Domain.FormScheme schemeEntity = await GetScheme(id); + FormSchemeNewModel formSchemeModel = schemeEntity.Scheme.ToObject(); + FormDbTableInfo mainTable = formSchemeModel.Db.Find(t => t.Type == "main"); + foreach (var tab in formSchemeModel.FormInfo.TabList) + { + foreach (var component in tab.Schemas) + { + if (component == null || component.ComponentProps == null || + string.IsNullOrEmpty(component.ComponentProps.DataTable)) + { + continue; + } + + ExcelImportFileds excelImportFileds = new ExcelImportFileds(); + excelImportFileds.ColName = component.Label; + excelImportFileds.Name = component.ComponentProps.FieldName; + excelImportFileds.CsType = component.CsType; + excelImportFileds.RelationType = component.ComponentProps.FieldName == pkey ? 1 : 0; + list.Add(excelImportFileds); + } + } + + response.Code = 200; + response.Message = "获取成功"; + } + catch (Exception ex) + { + response.Code = 500; + response.Message = ex.Message; + } + + return response; + } + + #endregion + } +} \ No newline at end of file diff --git a/OpenAuth.App/BaseApp/FormScheme/Request/FormDataReq.cs b/OpenAuth.App/BaseApp/FormScheme/Request/FormDataReq.cs new file mode 100644 index 0000000..76e86ec --- /dev/null +++ b/OpenAuth.App/BaseApp/FormScheme/Request/FormDataReq.cs @@ -0,0 +1,37 @@ +using OpenAuth.Repository.Domain; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace OpenAuth.App.FormScheme.Request +{ + public class FormDataReq + { + /// + /// 模板Id + /// + public string SchemeId { get; set; } + /// + /// 是否更新 + /// + public bool IsUpdate { get; set; } + /// + /// 表单数据 + /// + public string Data { get; set; } + /// + /// 主键(更新数据时采用) + /// + public string Pkey { get; set; } + /// + /// 主键值(更新数据时采用) + /// + public string PkeyValue { get; set; } + /// + /// 变更数据 + /// + public List DiffFormData { get; set; } + } +} diff --git a/OpenAuth.App/BaseApp/FormScheme/Request/FormQueryReq.cs b/OpenAuth.App/BaseApp/FormScheme/Request/FormQueryReq.cs new file mode 100644 index 0000000..9235b9f --- /dev/null +++ b/OpenAuth.App/BaseApp/FormScheme/Request/FormQueryReq.cs @@ -0,0 +1,30 @@ +using OpenAuth.App.FormScheme.Response; +using OpenAuth.App.Request; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace OpenAuth.App.FormScheme.Request +{ + public class FormQueryReq + { + /// + /// 分页参数 + /// + public PaginationInputDto PaginationInputDto { get; set; } + /// + /// 查询参数 + /// + public string QueryJson { get; set; } + /// + /// 桌面参数 + /// + public string Ldparam { get; set; } + /// + /// 排序字段 + /// + public string Sidx { get; set; } + } +} diff --git a/OpenAuth.App/BaseApp/FormScheme/Request/FormSchemeReq.cs b/OpenAuth.App/BaseApp/FormScheme/Request/FormSchemeReq.cs new file mode 100644 index 0000000..b3d31c4 --- /dev/null +++ b/OpenAuth.App/BaseApp/FormScheme/Request/FormSchemeReq.cs @@ -0,0 +1,24 @@ +using OpenAuth.Repository.Domain; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace OpenAuth.App.FormScheme.Request +{ + /// + /// 自定义表单模板保存数据 + /// + public class FormSchemeReq + { + /// + /// 模板基础信息 + /// + public FormSchemeInfo Info { get; set; } + /// + /// 模板信息 + /// + public Repository.Domain.FormScheme Scheme { get; set; } + } +} diff --git a/OpenAuth.App/BaseApp/FormScheme/Response/ComponentPropsModel.cs b/OpenAuth.App/BaseApp/FormScheme/Response/ComponentPropsModel.cs new file mode 100644 index 0000000..c190bf0 --- /dev/null +++ b/OpenAuth.App/BaseApp/FormScheme/Response/ComponentPropsModel.cs @@ -0,0 +1,20 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace OpenAuth.App.FormScheme.Response +{ + public class ComponentPropsModel + { + /// + /// 表名 + /// + public string DataTable { get; set; } + /// + /// 字段 + /// + public string FieldName { get; set; } + } +} diff --git a/OpenAuth.App/BaseApp/FormScheme/Response/ExecuteType.cs b/OpenAuth.App/BaseApp/FormScheme/Response/ExecuteType.cs new file mode 100644 index 0000000..d31b9dd --- /dev/null +++ b/OpenAuth.App/BaseApp/FormScheme/Response/ExecuteType.cs @@ -0,0 +1,28 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace OpenAuth.App.FormScheme.Response +{ + public enum ExecuteType + { + /// + /// 新增 + /// + [Description("新增")] + Insert = 0, + /// + /// 更新 + /// + [Description("更新")] + Update = 1, + /// + /// 删除 + /// + [Description("删除")] + Delete = 2 + } +} diff --git a/OpenAuth.App/BaseApp/FormScheme/Response/FormComponentModel.cs b/OpenAuth.App/BaseApp/FormScheme/Response/FormComponentModel.cs new file mode 100644 index 0000000..bc17cc3 --- /dev/null +++ b/OpenAuth.App/BaseApp/FormScheme/Response/FormComponentModel.cs @@ -0,0 +1,52 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace OpenAuth.App.FormScheme.Response +{ + /// + /// 表单组件 + /// + public class FormComponentModel + { + /// + /// 组件类型 + /// + public string Type { get; set; } + /// + /// 标识 + /// + public string Prop { get; set; } + /// + /// 绑定表 + /// + public string Table { get; set; } + /// + /// 绑定字段 + /// + public string Field { get; set; } + /// + /// 单据编码 + /// + public string Code { get; set; } + /// + /// 排序字段 + /// + public string OrderId { get; set; } + /// + /// 是否倒叙 + /// + public bool isDESC { get; set; } + /// + /// 数据字段类型 + /// + public string CsType { get; set; } + + /// + /// 子组件列表 + /// + public List children { get; set; } + } +} diff --git a/OpenAuth.App/BaseApp/FormScheme/Response/FormComponentNewModel.cs b/OpenAuth.App/BaseApp/FormScheme/Response/FormComponentNewModel.cs new file mode 100644 index 0000000..8633dd1 --- /dev/null +++ b/OpenAuth.App/BaseApp/FormScheme/Response/FormComponentNewModel.cs @@ -0,0 +1,53 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace OpenAuth.App.FormScheme.Response +{ + public class FormComponentNewModel + { + /// + /// 组件类型 + /// + public string Component { get; set; } + /// + /// 标识 + /// + public string Field { get; set; } + /// + /// 名称 + /// + public string Label { get; set; } + /// + /// 绑定表 + /// + public ComponentPropsModel ComponentProps { get; set; } + /// + /// 单据编码 + /// + public string Code { get; set; } + /// + /// 排序字段 + /// + public string OrderId { get; set; } + /// + /// 是否倒叙 + /// + public bool isDESC { get; set; } + /// + /// 数据字段类型 + /// + public string CsType { get; set; } + + /// + /// 子组件列表 + /// + public List children { get; set; } + /// + /// 子表组件列表 + /// + public List Columns { get; set; } + } +} diff --git a/OpenAuth.App/BaseApp/FormScheme/Response/FormDbTable.cs b/OpenAuth.App/BaseApp/FormScheme/Response/FormDbTable.cs new file mode 100644 index 0000000..4f5bdec --- /dev/null +++ b/OpenAuth.App/BaseApp/FormScheme/Response/FormDbTable.cs @@ -0,0 +1,41 @@ +using SqlSugar; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace OpenAuth.App.FormScheme.Response +{ + public class FormDbTable + { + /// + /// 数据库表名 + /// + public string TableName { get; set; } + /// + /// (更新,查询)条件字段名 + /// + public string Pkey { get; set; } + /// + /// 执行操作类型 + /// + public ExecuteType ExecuteType { get; set; } + /// + /// 执行参数 + /// + public List DbParameter { get; set; } + /// + /// 查询语句 + /// + public string Sql { get; set; } + /// + /// 关联表 + /// + public string RelationName { get; set; } + /// + /// 关联表字段 + /// + public string RelationField { get; set; } + } +} diff --git a/OpenAuth.App/BaseApp/FormScheme/Response/FormDbTableInfo.cs b/OpenAuth.App/BaseApp/FormScheme/Response/FormDbTableInfo.cs new file mode 100644 index 0000000..c10a8f4 --- /dev/null +++ b/OpenAuth.App/BaseApp/FormScheme/Response/FormDbTableInfo.cs @@ -0,0 +1,57 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace OpenAuth.App.FormScheme.Response +{ + /// + /// 表单数据表信息 + /// + public class FormDbTableInfo + { + /// + /// 唯一标识 + /// + public string Id { get; set; } + + /// + /// SqlServer下是Owner、PostgreSQL下是Schema、MySql下是数据库名 + /// + public string Schema { get; set; } + + /// + /// 表名 + /// + public string Name { get; set; } + + /// + /// 表备注,SqlServer下是扩展属性 MS_Description + /// + public string Comment { get; set; } + + /// + /// main 主表 chlid 子表 + /// + public string Type { get; set; } + /// + /// 关联字段 + /// + public string Field { get; set; } + + /// + /// 关联表 + /// + public string RelationName { get; set; } + /// + /// 关联表字段 + /// + public string RelationField { get; set; } + + /// + /// 视图表单sql语句 + /// + public string Sql { get; set; } + } +} diff --git a/OpenAuth.App/BaseApp/FormScheme/Response/FormInfoModel.cs b/OpenAuth.App/BaseApp/FormScheme/Response/FormInfoModel.cs new file mode 100644 index 0000000..67ebdb8 --- /dev/null +++ b/OpenAuth.App/BaseApp/FormScheme/Response/FormInfoModel.cs @@ -0,0 +1,21 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace OpenAuth.App.FormScheme.Response +{ + public class FormInfoModel + { + /// + /// 表单页签集合 + /// + public List TabList { get; set; } + + /// + /// 历史记录存储位置 0 无 1 公共 2 私有 + /// + public string HistoryType { get; set; } + } +} diff --git a/OpenAuth.App/BaseApp/FormScheme/Response/FormInfoNewModel.cs b/OpenAuth.App/BaseApp/FormScheme/Response/FormInfoNewModel.cs new file mode 100644 index 0000000..13877d5 --- /dev/null +++ b/OpenAuth.App/BaseApp/FormScheme/Response/FormInfoNewModel.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace OpenAuth.App.FormScheme.Response +{ + public class FormInfoNewModel + { + /// + /// 表单页签集合 + /// + public List TabList { get; set; } + // public List Schemas { get; set; } + } +} diff --git a/OpenAuth.App/BaseApp/FormScheme/Response/FormSchemeModel.cs b/OpenAuth.App/BaseApp/FormScheme/Response/FormSchemeModel.cs new file mode 100644 index 0000000..98087e5 --- /dev/null +++ b/OpenAuth.App/BaseApp/FormScheme/Response/FormSchemeModel.cs @@ -0,0 +1,31 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace OpenAuth.App.FormScheme.Response +{ + /// + /// 表单模板 + /// + public class FormSchemeModel + { + /// + /// 数据库编码 + /// + public string DbCode { get; set; } + /// + /// 数据表单信息 + /// + public List Db { get; set; } + /// + /// 表单信息 + /// + public FormInfoModel FormInfo { get; set; } + /// + /// 1 视图表单 0常规表单 + /// + public int FormType { get; set; } + } +} diff --git a/OpenAuth.App/BaseApp/FormScheme/Response/FormSchemeNewModel.cs b/OpenAuth.App/BaseApp/FormScheme/Response/FormSchemeNewModel.cs new file mode 100644 index 0000000..81e3565 --- /dev/null +++ b/OpenAuth.App/BaseApp/FormScheme/Response/FormSchemeNewModel.cs @@ -0,0 +1,28 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace OpenAuth.App.FormScheme.Response +{ + public class FormSchemeNewModel + { + /// + /// 数据库编码 + /// + public string DbCode { get; set; } + /// + /// 数据表单信息 + /// + public List Db { get; set; } + /// + /// 表单信息 + /// + public FormInfoNewModel FormInfo { get; set; } + /// + /// 1 视图表单 0常规表单 + /// + public int? FormType { get; set; } + } +} diff --git a/OpenAuth.App/BaseApp/FormScheme/Response/FormTabModel.cs b/OpenAuth.App/BaseApp/FormScheme/Response/FormTabModel.cs new file mode 100644 index 0000000..f04b0e3 --- /dev/null +++ b/OpenAuth.App/BaseApp/FormScheme/Response/FormTabModel.cs @@ -0,0 +1,19 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace OpenAuth.App.FormScheme.Response +{ + /// + /// 表单页签模型 + /// + public class FormTabModel + { + /// + /// 表单组件集合 + /// + public List Components { get; set; } + } +} diff --git a/OpenAuth.App/BaseApp/FormScheme/Response/FormTabNewModel.cs b/OpenAuth.App/BaseApp/FormScheme/Response/FormTabNewModel.cs new file mode 100644 index 0000000..6baa70e --- /dev/null +++ b/OpenAuth.App/BaseApp/FormScheme/Response/FormTabNewModel.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace OpenAuth.App.FormScheme.Response +{ + public class FormTabNewModel + { + /// + /// 表单组件集合 + /// + public List Schemas { get; set; } + } +} diff --git a/OpenAuth.App/BaseApp/FormScheme/Response/PaginationInputDto.cs b/OpenAuth.App/BaseApp/FormScheme/Response/PaginationInputDto.cs new file mode 100644 index 0000000..694fa46 --- /dev/null +++ b/OpenAuth.App/BaseApp/FormScheme/Response/PaginationInputDto.cs @@ -0,0 +1,31 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace OpenAuth.App.FormScheme.Response +{ + public class PaginationInputDto + { + // + // 摘要: + // 每页行数 + public int rows { get; set; } + + // + // 摘要: + // 当前页 + public int page { get; set; } + + // + // 摘要: + // 排序列 + public string sidx { get; set; } + + // + // 摘要: + // 排序类型 + public string sord { get; set; } + } +} diff --git a/OpenAuth.App/BaseApp/HostedService/QuartzService.cs b/OpenAuth.App/BaseApp/HostedService/QuartzService.cs new file mode 100644 index 0000000..3aca26a --- /dev/null +++ b/OpenAuth.App/BaseApp/HostedService/QuartzService.cs @@ -0,0 +1,49 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; +using Quartz; + +namespace OpenAuth.App.HostedService +{ + /// + /// 自启动服务,本服务用于启动所有状态为【正在运行】的定时任务 + /// + public class QuartzService : IHostedService, IDisposable + { + private readonly ILogger _logger; + private IScheduler _scheduler; + private OpenJobApp _openJobApp; + + public QuartzService(ILogger logger, IScheduler scheduler, OpenJobApp openJobApp) + { + _logger = logger; + _scheduler = scheduler; + _openJobApp = openJobApp; + } + + public Task StartAsync(CancellationToken cancellationToken) + { + _scheduler.Start(); + var result = _openJobApp.StartAll(); + return result; + + //return Task.CompletedTask; + } + + public Task StopAsync(CancellationToken cancellationToken) + { + var result = _scheduler.Shutdown(); + _logger.LogInformation("关闭定时job"); + return result; + + //return Task.CompletedTask; + } + + public void Dispose() + { + + } + } +} \ No newline at end of file diff --git a/OpenAuth.App/BaseApp/ImMsgManager/ChatsHub.cs b/OpenAuth.App/BaseApp/ImMsgManager/ChatsHub.cs new file mode 100644 index 0000000..fb129d6 --- /dev/null +++ b/OpenAuth.App/BaseApp/ImMsgManager/ChatsHub.cs @@ -0,0 +1,79 @@ +using DocumentFormat.OpenXml.InkML; +using DocumentFormat.OpenXml.Spreadsheet; +using Microsoft.AspNetCore.SignalR; +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace OpenAuth.App.BaseApp.ImMsgManager +{ + public class ChatsHub : Hub + { + #region 重载Hub方法 + /// + /// 建立连接 + /// + /// + public override async Task OnConnectedAsync() + { + //await AddOnline(); + await base.OnConnectedAsync(); + } + /// + /// 断开连接 + /// + /// 异常信息 + /// + public override async Task OnDisconnectedAsync(Exception exception) + { + await RemoveOnline(); + await base.OnDisconnectedAsync(exception); + } + #endregion + + private static readonly ConcurrentDictionary clientIdMap = + new ConcurrentDictionary(); + + #region 客户端操作 + ///// + ///// 添加在线用户 + ///// + //public async Task AddOnline() + //{ + //} + /// + /// 移除在线用户 + /// + public async Task RemoveOnline() + { + string clientId = Context.ConnectionId; + clientIdMap.TryGetValue(clientId, out string userId); + await Groups.RemoveFromGroupAsync(clientId, userId); + } + /// + /// 发送消息 + /// + /// 我的UserId + /// 对方UserId + /// 消息 + /// 是否系统消息0不是1是 + public async Task SendMsg(string myUserId, string toUserId, string msg, string id, int isSystem) + { + await Clients.Group(toUserId).SendAsync("RevMsg", myUserId, msg, DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"), id, isSystem); + } + + + public async Task SendInfo(string userId) + { + string clientId = Context.ConnectionId; + clientIdMap.GetOrAdd(clientId, userId); + await Groups.AddToGroupAsync(clientId, userId); + } + + #endregion + + } +} diff --git a/OpenAuth.App/BaseApp/ImMsgManager/ImMsgApp.cs b/OpenAuth.App/BaseApp/ImMsgManager/ImMsgApp.cs new file mode 100644 index 0000000..188a368 --- /dev/null +++ b/OpenAuth.App/BaseApp/ImMsgManager/ImMsgApp.cs @@ -0,0 +1,303 @@ +using OpenAuth.App.Base; +using OpenAuth.Repository.Domain; +using OpenAuth.Repository; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Infrastructure; +using SqlSugar; +using OpenAuth.App.BaseApp.Request; +using DocumentFormat.OpenXml.Office2010.Excel; +using Infrastructure.Helpers; +using OpenAuth.Repository.Core; +using OpenAuth.App.BaseApp.WFTask; +using OpenAuth.App.BaseApp.ImMsgManager; +using Microsoft.Extensions.Configuration; +using DocumentFormat.OpenXml.EMMA; +using OpenAuth.App.BaseApp.Base; + +namespace OpenAuth.App.BaseApp +{ + public class ImMsgApp : SqlSugarBaseApp + { + private ChatsHub _hub; + private readonly IConfiguration _configuration; + private readonly ISqlSugarClient client; + #region 构造函数 + public ImMsgApp(ChatsHub hub, ISqlSugarClient sqlSugarClient, ISugarUnitOfWork unitWork, ISimpleClient repository, IConfiguration configuration) : base(unitWork, repository, null) + { + _hub = hub; + _configuration = configuration; + this.client = sqlSugarClient; + } + #endregion + + #region 数据查询 + /// + /// 分页获取列表数据 + /// + /// + /// + /// + /// + public async Task>>> LoadImMsgList(string sendUserId, string recvUserId, string keyword, int pageindex, int pagesize) + { + //定义且实例化分页数据 + RefAsync totalCount = 0; + //数据查询并返回 + var info = await this.Repository.AsQueryable() + .Where(t => (t.SendUserid == sendUserId && t.RecvUserid == recvUserId)) + .WhereIF(!string.IsNullOrEmpty(keyword), t => t.Content.Contains(keyword)) + .OrderByDescending(t => t.CreateDate) + .ToPageListAsync(pageindex, pagesize, totalCount); + return new Response>> + { + Result = new PageInfo> + { + Items = info, + Total = totalCount + } + }; + } + + /// + /// 获取收到的未读消息 + /// + ///当前登录用户id + /// + public async Task>> GetLastList(string userId) + { + //数据查询并返回 + var info = await this.Repository.AsQueryable() + .Where(t => t.RecvUserid == userId&&t.IsRead==0) + .OrderByDescending(t => t.CreateDate) + .ToListAsync(); + return new Response> + { + Result = info + }; + } + + /// + /// 分页获取系统消息 + /// + /// + /// + /// + /// + /// + /// + /// + public async Task>>> LoadSysImMsgList(string myId, string sysCode, string keyword, int? isDelete, int pageindex, int pagesize) + { + //定义且实例化分页数据 + RefAsync totalCount = 0; + //数据查询并返回 + var info = await this.Repository.AsQueryable() + .Where(r=>r.Issystem==1) + .WhereIF(!string.IsNullOrEmpty(myId),r=>r.RecvUserid==myId) + .WhereIF(!string.IsNullOrEmpty(sysCode),r=>r.SendUserid==sysCode) + .WhereIF(!string.IsNullOrEmpty(keyword), t => t.Content.Contains(keyword)) + .WhereIF(isDelete!=null,r=>r.DeleteMark==isDelete) + .OrderByDescending(t => t.CreateDate) + .ToPageListAsync(pageindex, pagesize, totalCount); + return new Response>> + { + Result = new PageInfo> + { + Items = info, + Total = totalCount + } + }; + } + + //根据contentid获取任务信息 + public async Task GetInfoByContentId(string contentid) + { + var info= await client.Queryable().Where(r=>r.Token==contentid) + .Select(r => new + { + r.Id, + r.ProcessCode, + r.Type, + r.ProcessId + }).ToListAsync(); + return new Response + { + Result = info.Count > 0 ? info.First() : null + }; + } + #endregion + + #region 增删改 + /// + /// 保存(新增) + /// + /// 数据源实体 + /// + public async Task> SaveEntity(ImMsgReq imsg) + { + var flag = false; + ImMsg entity = imsg.MapTo(); + entity.MsgId = Guid.NewGuid().ToString(); + entity.CreateDate = DateTime.Now; + entity.Issystem = 0; + entity.DeleteMark = 0; + + flag = await this.Repository.InsertAsync(entity); + return new Response + { + Result = flag, + Message = flag == true ? "success" : "error" + }; + } + /// + /// 保存(新增) + /// + /// 数据源实体 + /// + public Response SaveEntitys(List mglist) + { + var flag = false; + flag = this.Repository.InsertRange(mglist); + return new Response + { + Result = flag, + Message = flag == true ? "success" : "error" + }; + } + /// + /// 删除数据源 + /// + /// 主键 + public async Task> DeleteEntity(string id) + { + var flag = await this.Repository.DeleteByIdAsync(id); + return new Response + { + Result = flag, + Message = flag == true ? "success" : "error" + }; + } + + /// + /// 删除数据源 + /// + /// 主键list + public async Task> DeleteEntitys(List ids) + { + List list = new List(); + list = this.Repository.AsQueryable().Where(r => ids.Contains(r.MsgId)).ToList(); + var flag = await this.Repository.DeleteAsync(list); + return new Response + { + Result = flag, + Message = flag == true ? "success" : "error" + }; + } + + /// + /// 删除数据源(虚拟删除) + /// + /// 主键 + public async Task> VirtualDeleteEntity(string id) + { + using (var uow = base.UnitWork.CreateContext()) + { + await uow.ImMsg.UpdateAsync(r => new ImMsg { DeleteMark = 1 }, r => r.MsgId == id); + var flag = uow.Commit(); + return new Response + { + Result = flag, + Message = flag == true ? "success" : "error" + }; + } + } + + /// + /// 删除数据源(虚拟删除) + /// + /// 主键list + public async Task> VirtualDeleteEntitys(List ids) + { + using (var uow = base.UnitWork.CreateContext()) + { + foreach(var id in ids) + { + await uow.ImMsg.UpdateAsync(r => new ImMsg { DeleteMark = 1 }, r => r.MsgId == id); + } + var flag = uow.Commit(); + return new Response + { + Result = flag, + Message = flag == true ? "success" : "error" + }; + } + } + #endregion + + #region 确认阅读 + /// + /// 确认阅读 + /// + /// 主键 + /// + public async Task> ReadMsg(string id) + { + using (var uow = base.UnitWork.CreateContext()) + { + + await uow.ImMsg.UpdateAsync(r => new ImMsg { IsRead = 1 }, r => r.MsgId == id); + var flag = uow.Commit(); + return new Response + { + Result = flag, + Message = flag == true ? "success" : "error" + }; + } + } + #endregion + + #region 扩展方法 + /// + /// 发送消息 + /// + /// 编码 + /// 用户列表 + /// 消息内容 + /// 消息类型1.短信 2.邮箱 3.微信 4.IM(站内消息) + /// 消息内容id + public async Task SendMsg(string code, IEnumerable userIdList, string content, string messageType, string contentId = "") + { + if (!string.IsNullOrEmpty(content) && userIdList != null) + { + foreach (var userId in userIdList) + { + if (messageType.IndexOf("4") != -1) // 站内消息 + { + ImMsg iMMsgEntity = new ImMsg(); + iMMsgEntity.SendUserid = code; + iMMsgEntity.RecvUserid = userId; + iMMsgEntity.Content = content; + iMMsgEntity.ContentId = contentId; + iMMsgEntity.Issystem = 1; + iMMsgEntity.IsRead = 0; + iMMsgEntity.DeleteMark = 0; + + iMMsgEntity.MsgId = Guid.NewGuid().ToString(); + iMMsgEntity.CreateDate = DateTime.Now; + await this.Repository.InsertAsync(iMMsgEntity); + + var url = _configuration.GetSection("AppSetting:IMUrl").Value; + var imopen = _configuration.GetSection("AppSetting:IMOpen").Value; + await SendHubs.callMethod(url, imopen, "SendMsg", code, userId, content, iMMsgEntity.MsgId, 1); + //await _hub.SendMsg(url,imopen,code, userId, content, iMMsgEntity.MsgId, 1); + } + } + } + } + #endregion + } +} diff --git a/OpenAuth.App/BaseApp/ImMsgManager/Request/ImMsgReq.cs b/OpenAuth.App/BaseApp/ImMsgManager/Request/ImMsgReq.cs new file mode 100644 index 0000000..8901354 --- /dev/null +++ b/OpenAuth.App/BaseApp/ImMsgManager/Request/ImMsgReq.cs @@ -0,0 +1,62 @@ +using SqlSugar; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace OpenAuth.App.BaseApp.Request +{ + public class ImMsgReq + { + /// + /// Desc:消息主键 + /// + public string MsgId { get; set; } + + /// + /// Desc:发送者ID + /// + public string SendUserid { get; set; } + + /// + /// Desc:接收者ID + /// + public string RecvUserid { get; set; } + + /// + /// Desc:消息内容 + /// + public string Content { get; set; } + + /// + /// Desc:创建时间 + /// + public DateTime? CreateDate { get; set; } + + /// + /// Desc:是否是系统消息 0 不是 1 是 + /// + public int? Issystem { get; set; } + + /// + /// Desc:内容id + /// + public string ContentId { get; set; } + + /// + /// Desc:消息是否已读 1 是 0 不是;系统消息起作用 + /// + public int? IsRead { get; set; } + + /// + /// Desc:是否删除1 是 0 不是 + /// + public int? DeleteMark { get; set; } + + /// + /// Desc:租户ID + /// + public string TenantId { get; set; } + } +} diff --git a/OpenAuth.App/BaseApp/ImMsgManager/SendHubs.cs b/OpenAuth.App/BaseApp/ImMsgManager/SendHubs.cs new file mode 100644 index 0000000..3131e90 --- /dev/null +++ b/OpenAuth.App/BaseApp/ImMsgManager/SendHubs.cs @@ -0,0 +1,42 @@ +using ce.autofac.extension; +using Infrastructure.Helpers; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.SignalR.Client; + +namespace OpenAuth.App.BaseApp.ImMsgManager +{ + public static class SendHubs + { + // + // 摘要: + // 调用hub方法 + // + // 参数: + // methodName: + // + // args: + // 参数 + public static async Task callMethod(string imurl2, string imopen,string methodName, params object[] args) + { + if (imopen=="false") + { + return; + } + + if (string.IsNullOrEmpty(imurl2)) + { + IHttpContextAccessor accessor = IocManager.Instance.GetService(); + if (accessor.HttpContext != null) + { + imurl2 = accessor.HttpContext.Request.Host.Value; + imurl2 = ((!accessor.HttpContext.Request.IsHttps) ? ("http://" + imurl2) : ("https://" + imurl2)); + } + } + + HubConnection connection = new HubConnectionBuilder().WithUrl(imurl2 + "/chathub").Build(); + await connection.StartAsync(); + await connection.InvokeCoreAsync(methodName, args); + await connection.StopAsync(); + } + } +} diff --git a/OpenAuth.App/BaseApp/Import/ImportApp.cs b/OpenAuth.App/BaseApp/Import/ImportApp.cs new file mode 100644 index 0000000..aa1f133 --- /dev/null +++ b/OpenAuth.App/BaseApp/Import/ImportApp.cs @@ -0,0 +1,337 @@ +using Infrastructure; +using OpenAuth.App.Base; +using OpenAuth.App.FormScheme; +using OpenAuth.App.Permission; +using OpenAuth.Repository; +using OpenAuth.Repository.Domain; +using SqlSugar; +using System; +using System.Collections.Generic; +using System.Data; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using OpenAuth.App.BaseApp.Base; + +namespace OpenAuth.App.Import +{ + public class ImportApp : SqlSugarBaseApp + { + SysDataItemDetailApp _dataItemDetailApp; + FormSchemeApp _formSchemeApp; + public ImportApp(ISugarUnitOfWork unitWork, SysDataItemDetailApp dataItemDetailApp, FormSchemeApp formSchemeApp, + ISimpleClient repository) + : base(unitWork, repository, null) + { + _dataItemDetailApp = dataItemDetailApp; + _formSchemeApp = formSchemeApp; + } + + /// + /// 获取导入的分页数据 + /// + /// 查询关键字 + /// 菜单id + /// + /// + /// + public async Task>>> LoadExcelImportPage(string keyWord, string moduleId, int page, int limit) + { + RefAsync totalCount = 0; + var expression = Expressionable.Create() + .AndIF(!string.IsNullOrEmpty(keyWord), t => t.Name.Contains(keyWord)) + .AndIF(!string.IsNullOrEmpty(moduleId), t => t.ModuleId == moduleId); + var info = await base.Repository.AsQueryable() + .Where(expression.ToExpression()) + .ToPageListAsync(page, limit, totalCount); + return new Response>> + { + Result = new PageInfo> + { + Items = info, + Total = totalCount + } + }; + } + + /// + /// 获取表单数据 + /// + /// id + /// + public async Task GetEntity(string id) + { + var entity = await base.Repository.AsQueryable() + .Where(t => t.Id == id) + .FirstAsync(); + return entity; + } + /// + /// 获取配置字段列表 + /// + /// 配置信息主键 + /// + public Task> GetFieldList(string importId) + { + return base.Repository.ChangeRepository>().AsQueryable().Where(t => t.ImportId == importId).ToListAsync(); + } + /// + /// 获取导入配置列表根据模块ID + /// + /// 功能模块主键 + /// + public async Task> GetModuleList(string moduleId) + { + var expression = Expressionable.Create(); + expression = expression.And(t => t.ModuleId == moduleId && t.EnabledMark == 1); + return await base.Repository.AsQueryable().Where(expression.ToExpression()).ToListAsync(); + } + /// + /// 删除数据 + /// + /// 主键 + public async Task> DeleteEntity(string keyValue) + { + using (var uow = base.UnitWork.CreateContext()) + { + await uow.ExcelImport.DeleteByIdAsync(keyValue); + await uow.ExcelImportFileds.DeleteAsync(a => a.ImportId == keyValue); + + var falg = uow.Commit(); + return new Response + { + Result = falg, + Message = (falg == true ? "success" : "error") + }; + } + } + + /// + /// 保存表单(新增、修改) + /// + /// 主键值 + /// 实体数据 + /// 字段列表 + /// + public async Task> SaveEntity(string keyValue, ExcelImport entity, List filedList) + { + using (var uow = base.UnitWork.CreateContext()) + { + if (!string.IsNullOrEmpty(keyValue)) + { + entity.Id = keyValue; + entity.ModifyDate = DateTime.Now; + await uow.ExcelImport.UpdateAsync(entity); + } + else + { + entity.Id = Guid.NewGuid().ToString(); + entity.CreateDate = DateTime.Now; + entity.EnabledMark = 1; + await uow.ExcelImport.InsertAsync(entity); + } + await uow.ExcelImportFileds.DeleteAsync(t => t.ImportId == entity.Id); + foreach (var item in filedList) + { + item.ImportId = entity.Id; + await uow.ExcelImportFileds.InsertAsync(item); + } + var falg = uow.Commit(); + return new Response + { + Result = falg, + Message = (falg == true ? "success" : "error") + }; + } + } + + #region 扩展方法 + /// + /// excel 数据导入(未导入数据写入缓存) + /// + /// 导入模板主键 + /// 导入数据 + /// + public async Task<(DataTable elist, int snum, int fnum)> ImportTable(string templateId, DataTable dt) + { + int snum = 0; + int fnum = 0; + // 创建一个datatable容器用于保存导入失败的数据 + DataTable failDt = new DataTable(); + dt.Columns.Add("导入错误", typeof(string)); + foreach (DataColumn dc in dt.Columns) + { + failDt.Columns.Add(dc.ColumnName, dc.DataType); + } + + if (dt.Rows.Count > 0) + { + ExcelImport entity = await GetEntity(templateId); + List list = (List)await GetFieldList(templateId); + + if (entity != null && list.Count > 0) + { + string sqlonly = " select * from " + entity.DbTable + " where 1=1 {LEARUN_SASSID} "; + // 数据字典数据 + Dictionary> dataItemMap = new Dictionary>(); + Dictionary dataSourceMap = new Dictionary(); + + foreach (DataRow dr in dt.Rows) + { // 行数据循环 + try + { + List dbParameters = new List(); + foreach (var col in list) + { + object paramValue = null; + + switch (col.RelationType) + { + case 0://无关联 + paramValue = dr[col.ColName].ToString(); + await IsOnlyOne(col, sqlonly, dr[col.ColName].ToString(), entity.DbId); + break; + case 1://GUID + paramValue = Guid.NewGuid().ToString(); + break; + case 2://数据字典 + string dataItemName = ""; + if (!dataItemMap.ContainsKey(col.DataItemCode)) + { + List dataItemList = (List)await _dataItemDetailApp.Load(col.DataItemCode, ""); + dataItemMap.Add(col.DataItemCode, dataItemList); + } + dataItemName = FindDataItemValue(dataItemMap[col.DataItemCode], dr[col.ColName].ToString(), col.ColName); + paramValue = dataItemName; + await IsOnlyOne(col, sqlonly, dataItemName, entity.DbId); + break; + case 3://数据表 + string v = ""; + try + { + //string queryJson = "{" + col.F_DSourceExcelId + ":\"" + dr[col.F_ColName].ToString() + "\"}"; + if (!dataSourceMap.ContainsKey(col.DSourceId)) + { + //DataTable sourceDt = await _dataSourceIBLL.GetDataTable(col.DSourceId); + DataTable sourceDt = new DataTable(); + dataSourceMap.Add(col.DSourceId, sourceDt); + } + v = FindDataSourceValue(dataSourceMap[col.DSourceId], col.DSourceExcelId.ToLower(), dr[col.ColName].ToString(), col.DSourceDBId.ToLower()); + paramValue = v; + } + catch (Exception) + { + throw (new Exception("【" + col.ColName + "】 找不到对应的数据")); + } + await IsOnlyOne(col, sqlonly, v, entity.DbId); + break; + case 4://固定值 + paramValue = col.Value; + break; + case 5://操作人ID + paramValue = ""; + break; + case 6://操作人名字 + paramValue = ""; + break; + case 7://操作时间 + paramValue = DateTime.Now; + break; + } + + dbParameters.Add(new SugarParameter(col.Name, paramValue)); + } + + //await excelImportService.BaseRepository(entity.DbId).ExecuteInsert(entity.DbTable, dbParameters); + _formSchemeApp.ExecuteInsert(entity.DbTable, dbParameters); + snum++; + } + catch (Exception ex) + { + fnum++; + if (entity.ErrorType == 0)// 如果错误机制是终止 + { + dr["导入错误"] = $"{ex.Message} 【之后数据未被导入】"; + failDt.Rows.Add(dr.ItemArray); + break; + } + else + { + dr["导入错误"] = ex.Message; + failDt.Rows.Add(dr.ItemArray); + } + } + } + } + } + return (failDt, snum, fnum); + } + + /// + /// 数据字典查找Value + /// + /// 数据字典数据 + /// 项目名 + /// 列名 + /// + private string FindDataItemValue(List dataItemList, string itemName, string colName) + { + SysDataItemDetail dataItem = dataItemList.Find(t => t.ItemName == itemName); + if (dataItem != null) + { + return dataItem.ItemValue; + } + else + { + throw (new Exception("【" + colName + "】数据字典找不到对应值")); + } + } + + + /// + /// 获取数据源数据 + /// + /// 数据源 + /// 名称 + /// 值 + /// 列名 + /// + private string FindDataSourceValue(DataTable dt, string name, string value, string colname) + { + + foreach (DataRow dr in dt.Rows) + { + if (dr[name].ToString() == value) + { + return dr[colname].ToString(); + } + } + + return ""; + } + + /// + /// 判断是否数据有重复 + /// + /// + /// + /// + /// + private async Task IsOnlyOne(ExcelImportFileds col, string sqlonly, string value, string dbId) + { + if (col.OnlyOne == 1) + { + var dp = new Dictionary(); + sqlonly += col.Name + " = @" + col.Name; + dp.Add(col.Name, value); + var d = await _formSchemeApp.FindTableNew(sqlonly, dp); + if (d.Count > 0) + { + throw new Exception("【" + col.ColName + "】此项数据不能重复"); + } + } + } + + #endregion + } +} diff --git a/OpenAuth.App/BaseApp/Import/Response/ExcelImportDto.cs b/OpenAuth.App/BaseApp/Import/Response/ExcelImportDto.cs new file mode 100644 index 0000000..253a573 --- /dev/null +++ b/OpenAuth.App/BaseApp/Import/Response/ExcelImportDto.cs @@ -0,0 +1,24 @@ +using OpenAuth.Repository.Domain; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace OpenAuth.App.Import.Response +{ + /// + /// 提交参数 + /// + public class ExcelImportDto + { + /// + /// Excel数据导入设置 + /// + public ExcelImport Entity { get; set; } + /// + /// Excel数据导入设置字段 + /// + public List List { get; set; } + } +} diff --git a/OpenAuth.App/BaseApp/Interface/IAuth.cs b/OpenAuth.App/BaseApp/Interface/IAuth.cs new file mode 100644 index 0000000..6c5807a --- /dev/null +++ b/OpenAuth.App/BaseApp/Interface/IAuth.cs @@ -0,0 +1,45 @@ +/* + *单独提取这个接口,为了以下几点: + * 1、可以方便的实现webapi 和本地登录相互切换 + * 2、可以方便的使用mock进行单元测试 + */ + +using Infrastructure; +using OpenAuth.App.SSO; + +namespace OpenAuth.App.Interface +{ + public interface IAuth + { + /// + /// 检验token是否有效 + /// + /// token值 + /// + /// + bool CheckLogin(string token = "", string otherInfo = ""); + AuthStrategyContext GetCurrentUser(); + string GetUserName(string otherInfo = ""); + string GetUserNickName(string otherInfo = ""); + string GetUserId(string otherInfo = ""); + + /// + /// 登录接口 + /// + /// 登录的应用appkey + /// 用户名 + /// 密码 + /// + Response Login(string appKey, string username, string pwd); + /// + /// 退出登录 + /// + /// + bool Logout(); + + + void CoverToken(string account, string name); + + bool IsSystem(); + } +} diff --git a/OpenAuth.App/BaseApp/Interface/IAuthStrategy.cs b/OpenAuth.App/BaseApp/Interface/IAuthStrategy.cs new file mode 100644 index 0000000..8154e06 --- /dev/null +++ b/OpenAuth.App/BaseApp/Interface/IAuthStrategy.cs @@ -0,0 +1,28 @@ +using System; +using System.Collections.Generic; +using Infrastructure; +using OpenAuth.App.Response; +using OpenAuth.Repository.Domain; + +namespace OpenAuth.App +{ + public interface IAuthStrategy + { + List Modules { get; } + + List ModuleElements { get; } + + List Roles { get; } + + List Resources { get; } + + List Orgs { get; } + + List Positions { get; } + + SysUser User + { + get; set; + } + } +} \ No newline at end of file diff --git a/OpenAuth.App/BaseApp/Interface/ICustomerForm.cs b/OpenAuth.App/BaseApp/Interface/ICustomerForm.cs new file mode 100644 index 0000000..e387de3 --- /dev/null +++ b/OpenAuth.App/BaseApp/Interface/ICustomerForm.cs @@ -0,0 +1,23 @@ +namespace OpenAuth.App.Interface +{ + /// + /// 开发者自定义表单的统一接口 + /// 例如:FrmLeaveReqApp为请假表单对应的应用层,实现该接口,可以自动向数据库中插入对应的记录 + /// + public interface ICustomerForm + { + /// + /// 该接口定义自定义表单模式时需实现向数据库写入表单数据 + /// + /// 表单对应的流程实例ID + /// 表单数据 + void Add(string flowInstanceId, string frmData); + + /// + /// 该接口定义流程自定义表单模式时需实现向数据库更新表单数据 + /// + /// 更新的时候前端没有存储Id,需要用流程实例ID来关联,如果前端设计了Id字段,则可以不用 + /// 表单数据 + void Update(string flowInstanceId, string frmData); + } +} diff --git a/OpenAuth.App/BaseApp/Jobs/OpenJobApp.cs b/OpenAuth.App/BaseApp/Jobs/OpenJobApp.cs new file mode 100644 index 0000000..0e73753 --- /dev/null +++ b/OpenAuth.App/BaseApp/Jobs/OpenJobApp.cs @@ -0,0 +1,241 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Infrastructure; +using Infrastructure.Const; +using Infrastructure.Extensions; +using Microsoft.Extensions.Logging; +using OpenAuth.App.Base; +using OpenAuth.App.BaseApp.Base; +using OpenAuth.App.Extensions; +using OpenAuth.App.Interface; +using OpenAuth.App.Request; +using OpenAuth.App.Response; +using OpenAuth.Repository; +using OpenAuth.Repository.Domain; +using Quartz; +using SqlSugar; + +namespace OpenAuth.App +{ + /// + /// 系统定时任务管理 + /// + public class OpenJobApp : SqlSugarBaseApp + { + private SysLogApp _sysLogApp; + private IScheduler _scheduler; + private ILogger _logger; + + public OpenJobApp( + ISugarUnitOfWork unitWork, + ISimpleClient repository, + IAuth auth, + SysLogApp sysLogApp, + IScheduler scheduler, + ILogger logger) : base(unitWork, repository, auth) + { + _sysLogApp = sysLogApp; + _scheduler = scheduler; + _logger = logger; + } + + /// + /// 加载列表 + /// + public async Task Load(QueryOpenJobListReq request) + { + int totalCount = 0; + var result = new TableData(); + var objs = Repository.AsQueryable() + .WhereIF(!string.IsNullOrEmpty(request.key), u => u.JobName.Contains(request.key)); + + result.data = await objs.OrderBy(u => u.Id).ToPageListAsync(request.page, request.limit, totalCount); + result.count = totalCount; + + return result; + } + + /// + /// 启动所有状态为正在运行的任务 + /// 通常应用在系统加载的时候 + /// + /// + public async Task StartAll() + { + var jobs = await Repository.GetListAsync(u => u.Status == (int)JobStatus.Running); + foreach (var job in jobs) + { + job.Start(_scheduler); + } + _logger.LogInformation("所有状态为正在运行的任务已启动"); + } + + public void Add(AddOrUpdateOpenJobReq req) + { + var obj = req.MapTo(); + obj.Id = Guid.NewGuid().ToString(); + obj.CreateTime = DateTime.Now; + var user = _auth.GetCurrentUser().User; + obj.CreateUserId = user.Id.ToString(); + obj.CreateUserName = user.Name; + Repository.Insert(obj); + } + + public void AddStart(AddOrUpdateOpenJobReq req) + { + var obj = req.MapTo(); + obj.Id = Guid.NewGuid().ToString(); + obj.CreateTime = DateTime.Now; + obj.CreateUserId = "-1"; + obj.CreateUserName = "System"; + var flag=Repository.Insert(obj); + if (flag == true) + { + var job = Repository.GetFirst(r=>r.Id==obj.Id); + job.Start(_scheduler); + } + } + + public void Update(AddOrUpdateOpenJobReq obj) + { + var user = _auth.GetCurrentUser().User; + base.Repository.Update(u => new SysOpenJob + { + JobName = obj.JobName, + JobType = obj.JobType, + JobCall = obj.JobCall, + JobCallParams = obj.JobCallParams, + Cron = obj.Cron, + Status = obj.Status, + Remark = obj.Remark, + UpdateTime = DateTime.Now, + UpdateUserId = user.Id.ToString(), + UpdateUserName = user.Name + }, u => u.Id == obj.Id); + } + + #region 定时任务运行相关操作 + + /// + /// 返回系统的job接口 + /// + /// + public List QueryLocalHandlers() + { + var types = AppDomain.CurrentDomain.GetAssemblies() + .SelectMany(a => a.GetTypes().Where(t => t.GetInterfaces() + .Contains(typeof(IJob)))) + .ToArray(); + return types.Select(u => u.FullName).ToList(); + } + + public void ChangeJobStatus(ChangeJobStatusReq req) + { + var job = Repository.GetFirst(u => u.Id == req.Id); + if (job == null) + { + throw new Exception("任务不存在"); + } + + + if (req.Status == (int)JobStatus.NotRun) //停止 + { + job.Stop(_scheduler); + } + else //启动 + { + job.Start(_scheduler); + } + + + var user = _auth.GetCurrentUser().User; + + job.Status = req.Status; + job.UpdateTime = DateTime.Now; + job.UpdateUserId = user.Id.ToString(); + job.UpdateUserName = user.Name; + Repository.Update(job); + } + + public void ChangeJobStatus1(ChangeJobStatusReq req) + { + var job = Repository.GetFirst(u => u.Id == req.Id); + if (job == null) + { + throw new Exception("任务不存在"); + } + + + if (req.Status == (int)JobStatus.NotRun) //停止 + { + job.Stop(_scheduler); + } + else //启动 + { + job.Start(_scheduler); + } + + job.Status = req.Status; + job.UpdateTime = DateTime.Now; + job.UpdateUserId = "-1"; + job.UpdateUserName = "System"; + Repository.Update(job); + } + /// + /// 记录任务运行结果 + /// + /// + public void RecordRun(string jobId) + { + var job = Repository.GetFirst(u => u.Id == jobId); + if (job == null) + { + _sysLogApp.Add(new SysLog + { + TypeName = "定时任务", + TypeId = "AUTOJOB", + Content = $"未能找到定时任务:{jobId}" + }); + return; + } + + job.RunCount++; + job.LastRunTime = DateTime.Now; + Repository.Update(job); + + _sysLogApp.Add(new SysLog + { + CreateName = "Quartz", + CreateId = -1, + TypeName = "定时任务", + TypeId = "AUTOJOB", + Content = $"运行了自动任务:{job.JobName}" + }); + _logger.LogInformation($"运行了自动任务:{job.JobName}"); + } + + /// + /// 按id批量删除 + /// + /// + public virtual void Delete(string[] ids) + { + Repository.Delete(u => ids.Contains(u.Id)); + } + + public void DeleteEntity(SysOpenJob job) + { + Repository.Delete(job); + JobKey jbk = new JobKey(job.Id); + + _scheduler.DeleteJob(jbk); + } + public SysOpenJob Get(string id) + { + return Repository.GetFirst(u => u.Id == id); + } + #endregion + } +} \ No newline at end of file diff --git a/OpenAuth.App/BaseApp/Jobs/Request/AddOrUpdateOpenJobReq.cs b/OpenAuth.App/BaseApp/Jobs/Request/AddOrUpdateOpenJobReq.cs new file mode 100644 index 0000000..6ccbc4e --- /dev/null +++ b/OpenAuth.App/BaseApp/Jobs/Request/AddOrUpdateOpenJobReq.cs @@ -0,0 +1,59 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a CodeSmith Template. +// +// DO NOT MODIFY contents of this file. Changes to this +// file will be lost if the code is regenerated. +// Author:Yubao Li +// +//------------------------------------------------------------------------------ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.ComponentModel.DataAnnotations.Schema; +using System.Text; +using OpenAuth.Repository.Core; + +namespace OpenAuth.App.Request +{ + /// + /// 定时任务 + /// + [Table("OpenJob")] + public partial class AddOrUpdateOpenJobReq + { + + /// + /// Id + /// + public string Id { get; set; } + /// + /// 任务名称 + /// + public string JobName { get; set; } + /// + /// 任务执行方式0:本地任务;1:外部接口任务 + /// + public int JobType { get; set; } + /// + /// 任务地址 + /// + public string JobCall { get; set; } + /// + /// 任务参数,JSON格式 + /// + public string JobCallParams { get; set; } + /// + /// CRON表达式 + /// + public string Cron { get; set; } + /// + /// 任务运行状态(0:停止,1:正在运行,2:暂停) + /// + public int Status { get; set; } + /// + /// 备注 + /// + public string Remark { get; set; } + } +} \ No newline at end of file diff --git a/OpenAuth.App/BaseApp/Jobs/Request/ChangeJobStatusReq.cs b/OpenAuth.App/BaseApp/Jobs/Request/ChangeJobStatusReq.cs new file mode 100644 index 0000000..c63ac55 --- /dev/null +++ b/OpenAuth.App/BaseApp/Jobs/Request/ChangeJobStatusReq.cs @@ -0,0 +1,16 @@ +namespace OpenAuth.App.Request +{ + public class ChangeJobStatusReq + { + /// + /// 任务ID + /// + public string Id { get; set; } + + /// + /// 改变任务状态 + /// 0:停止;1:启动(任务变成正在运行) + /// + public int Status { get; set; } + } +} diff --git a/OpenAuth.App/BaseApp/Jobs/Request/QueryOpenJobListReq.cs b/OpenAuth.App/BaseApp/Jobs/Request/QueryOpenJobListReq.cs new file mode 100644 index 0000000..0753001 --- /dev/null +++ b/OpenAuth.App/BaseApp/Jobs/Request/QueryOpenJobListReq.cs @@ -0,0 +1,7 @@ +namespace OpenAuth.App.Request +{ + public class QueryOpenJobListReq : PageReq + { + //todo:添加自己的请求字段 + } +} \ No newline at end of file diff --git a/OpenAuth.App/BaseApp/Jobs/SysLogJob.cs b/OpenAuth.App/BaseApp/Jobs/SysLogJob.cs new file mode 100644 index 0000000..19dfa75 --- /dev/null +++ b/OpenAuth.App/BaseApp/Jobs/SysLogJob.cs @@ -0,0 +1,26 @@ +using System.Threading.Tasks; +using Infrastructure; +using Quartz; + +namespace OpenAuth.App.Jobs +{ + public class SysLogJob : IJob + { + private SysLogApp _sysLogApp; + private OpenJobApp _openJobApp; + + public SysLogJob(SysLogApp sysLogApp, OpenJobApp openJobApp) + { + _sysLogApp = sysLogApp; + _openJobApp = openJobApp; + } + + public Task Execute(IJobExecutionContext context) + { + var jobId = context.MergedJobDataMap.GetString(Define.JOBMAPKEY); + //todo:这里可以加入自己的自动任务逻辑 + _openJobApp.RecordRun(jobId); + return Task.Delay(1); + } + } +} \ No newline at end of file diff --git a/OpenAuth.App/BaseApp/Jobs/TaskOverTimeJob.cs b/OpenAuth.App/BaseApp/Jobs/TaskOverTimeJob.cs new file mode 100644 index 0000000..6ea21bf --- /dev/null +++ b/OpenAuth.App/BaseApp/Jobs/TaskOverTimeJob.cs @@ -0,0 +1,78 @@ +using Infrastructure; +using Newtonsoft.Json.Linq; +using Newtonsoft.Json; +using OpenAuth.App.BaseApp.WFTask; +using OpenAuth.App.Interface; +using Quartz; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace OpenAuth.App.BaseApp.Jobs +{ + public class TaskOverTimeJob:IJob + { + private ImMsgApp _immsgApp; + private OpenJobApp _openJobApp; + private WFTaskApp _wfTaskApp; + private IAuth _auth; + + public TaskOverTimeJob(ImMsgApp immsgApp, OpenJobApp openJobApp, WFTaskApp wfTaskApp, IAuth auth) + { + _immsgApp = immsgApp; + _openJobApp = openJobApp; + _wfTaskApp = wfTaskApp; + _auth = auth; + } + + public async Task Execute(IJobExecutionContext context) + { + var jobId = context.MergedJobDataMap.GetString(Define.JOBMAPKEY); + //todo:这里可以加入自己的自动任务逻辑 + try + { + var job = _openJobApp.Get(jobId); + if (job != null) + { + var parms = (JObject)JsonConvert.DeserializeObject(job.JobCallParams); + string taskid = parms["TaskId"].ToString(); + + //更新任务状态 + var taskInfo = _wfTaskApp.GetEntity(taskid); + if(taskInfo != null && (taskInfo.State == 3 || taskInfo.State == 4)) + { + _openJobApp.DeleteEntity(job); + } + else + { + taskInfo.IsOutTime = 2; + _wfTaskApp.Update(taskInfo); + + //创建消息接收用户集合 + List userlist = new List(); + userlist.Add(taskInfo.UserId); + var users = _wfTaskApp.GetUsers(taskid); + userlist = userlist.Union(users).ToList(); + //创建消息发送内容 + string msg = $"任务{taskInfo.ProcessTitle}{taskInfo.UnitName}已严重超时,请及时办理"; + + //更新Job信息,停止job,运行次数+1 + _openJobApp.RecordRun(jobId); + _openJobApp.ChangeJobStatus1(new App.Request.ChangeJobStatusReq { Id = jobId, Status = 0 }); + + //发送消息 + await _immsgApp.SendMsg("ChaoShi", userlist, msg, "4", taskInfo.Token); + } + } + } + catch + { + + } + + await Task.CompletedTask; + } + } +} diff --git a/OpenAuth.App/BaseApp/Jobs/TaskTimeoutJob.cs b/OpenAuth.App/BaseApp/Jobs/TaskTimeoutJob.cs new file mode 100644 index 0000000..e8de900 --- /dev/null +++ b/OpenAuth.App/BaseApp/Jobs/TaskTimeoutJob.cs @@ -0,0 +1,80 @@ +using Infrastructure; +using Newtonsoft.Json.Linq; +using Newtonsoft.Json; +using Quartz; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using OpenAuth.App.BaseApp.WFTask; +using DocumentFormat.OpenXml.Drawing.Diagrams; +using OpenAuth.Repository.Domain; +using OpenAuth.App.Interface; + +namespace OpenAuth.App.BaseApp.Jobs +{ + public class TaskTimeoutJob : IJob + { + private ImMsgApp _immsgApp; + private OpenJobApp _openJobApp; + private WFTaskApp _wfTaskApp; + private IAuth _auth; + + public TaskTimeoutJob(ImMsgApp immsgApp, OpenJobApp openJobApp,WFTaskApp wfTaskApp, IAuth auth) + { + _immsgApp = immsgApp; + _openJobApp = openJobApp; + _wfTaskApp= wfTaskApp; + _auth = auth; + } + + public async Task Execute(IJobExecutionContext context) + { + var jobId = context.MergedJobDataMap.GetString(Define.JOBMAPKEY); + //todo:这里可以加入自己的自动任务逻辑 + try + { + var job = _openJobApp.Get(jobId); + if (job != null) + { + var parms = (JObject)JsonConvert.DeserializeObject(job.JobCallParams); + string taskid = parms["TaskId"].ToString(); + + //更新任务状态 + var taskInfo = _wfTaskApp.GetEntity(taskid); + if (taskInfo != null && (taskInfo.State == 3 || taskInfo.State == 4)) + { + _openJobApp.DeleteEntity(job); + } + else + { + taskInfo.IsOutTime = 1; + _wfTaskApp.Update(taskInfo); + + //创建消息接收用户集合 + List userlist = new List(); + userlist.Add(taskInfo.UserId); + var users = _wfTaskApp.GetUsers(taskid); + userlist = userlist.Union(users).ToList(); + //创建消息发送内容 + string msg = $"任务{taskInfo.ProcessTitle}{taskInfo.UnitName}已超时,请及时办理"; + + //更新Job信息,停止job,运行次数+1 + _openJobApp.RecordRun(jobId); + _openJobApp.ChangeJobStatus1(new App.Request.ChangeJobStatusReq { Id = jobId, Status = 0 }); + + //发送消息 + await _immsgApp.SendMsg("ChaoShi", userlist, msg, "4", taskInfo.Token); + } + } + } + catch + { + return; + } + + await Task.CompletedTask; + } + } +} diff --git a/OpenAuth.App/BaseApp/ModuleManager/ModuleManagerApp.cs b/OpenAuth.App/BaseApp/ModuleManager/ModuleManagerApp.cs new file mode 100644 index 0000000..1e757a4 --- /dev/null +++ b/OpenAuth.App/BaseApp/ModuleManager/ModuleManagerApp.cs @@ -0,0 +1,286 @@ +using System.Collections.Generic; +using System.Linq; +using Infrastructure; +using Microsoft.AspNetCore.Mvc; +using Microsoft.VisualStudio.TestPlatform.ObjectModel.Utilities; +using OpenAuth.App.Base; +using OpenAuth.App.Interface; +using OpenAuth.App.ModuleManager; +using OpenAuth.App.ModuleManager.Response; +using OpenAuth.App.Request; +using OpenAuth.Repository; +using OpenAuth.Repository.Core; +using OpenAuth.Repository.Domain; +using SqlSugar; + +namespace OpenAuth.App +{ + public class ModuleManagerApp : StringTreeApp + { + private ISqlSugarClient client; + + public ModuleManagerApp( + ISugarUnitOfWork unitWork, + ISimpleClient repository + , IAuth auth) : base(unitWork, repository, auth) + { + client = base.Repository.AsSugarClient(); + } + + #region 菜单 + + #region 查询 + + #region 菜单树 + + /// + /// 菜单树【全部数据】 + /// + /// + /// + public Response> ModulesTree(string name) + { + var result = new Response>(); + + if (!string.IsNullOrEmpty(name)) + { + object[] orgIds = base.Repository.AsQueryable() + .Where(a => a.Name.Contains(name)) + .Select(it => it.Id).ToList().Cast().ToArray(); + + result.Result = base.Repository.AsQueryable() + .OrderBy(a => a.SortNo) + .Select() + .ToTree(a => a.Children, a => a.ParentId, 0, orgIds); + } + else + { + result.Result = base.Repository.AsQueryable() + .OrderBy(a => a.SortNo) + .Select() + .ToTree(a => a.Children, a => a.ParentId, 0, a => a.Id); + } + + return result; + } + + #endregion + + #region 单个菜单 + + public async Task> ModuleById(string id) + { + var model = await base.Repository.GetByIdAsync(id); + return new Response { Result = model }; + } + + #endregion + + #endregion + + #region 增加 + + public Response Add(SysModule model) + { + var loginContext = _auth.GetCurrentUser(); + if (loginContext == null) + { + throw new CommonException("登录已过期", Define.INVALID_TOKEN); + } + + //后面再看 + //CalculateCascade(model); + model.Id = Guid.NewGuid().ToString(); + var flag = Repository.Insert(model); + return new Response + { + Result = flag, + Message = flag == true ? "success" : "error" + }; + } + + #endregion + + #region 修改 + + public Response Update(SysModule obj) + { + try + { + //var flag = base.Repository.Update(obj); + //if (flag) + //{ + // return new Response { Result = true, Message = "success" }; + //} + //else + //{ + // return new Response { Result = false, Message = "error" }; + //} + UpdateTreeObj(obj); + return new Response { Result = true, Message = "success" }; + } + catch (Exception ex) + { + return new Response { Result = false, Message = "error:" + ex.Message.ToString() }; + } + } + + #endregion + + #region 删除 + + /// + /// 删除指定的菜单 + /// + /// + public async Task> Delete(string[] ids) + { + using (var uow = base.UnitWork.CreateContext()) + { + await uow.Module.DeleteByIdAsync(ids.Cast().ToArray()); + await uow.ModuleElement.DeleteAsync(a => ids.Contains(a.ModuleId)); + var flag = uow.Commit(); + + return new Response + { + Result = flag, + Message = flag == true ? "success" : "error" + }; + } + } + + #endregion + + #endregion + + #region 按钮 + + #region 查询 + + #region 根据 ModuleId 获取 Elements + + /// + /// 根据 ModuleId 获取 Elements + /// + /// + /// + public async Task>>> ElementsByModule( + [FromQuery] QueryElementListReq req) + { + RefAsync totalCount = 0; + + var elements = await base.Repository.ChangeRepository>().AsQueryable() + .WhereIF(!string.IsNullOrEmpty(req.key), el => el.Name.Contains(req.key)) + .WhereIF(!string.IsNullOrEmpty(req.ModuleId), el => el.ModuleId == req.ModuleId) + .ToPageListAsync(req.page, req.limit, totalCount); + + return new Response>> + { + Result = new PageInfo> + { + Items = elements, + Total = totalCount + } + }; + } + + #endregion + + #endregion + + #region 添加 + + public Response AddMenu(SysModuleElement model) + { + var loginContext = _auth.GetCurrentUser(); + if (loginContext == null) + { + throw new CommonException("登录已过期", Define.INVALID_TOKEN); + } + + using (var uow = base.UnitWork.CreateContext()) + { + model.Id = Guid.NewGuid().ToString(); + uow.ModuleElement.Insert(model); + var flag = uow.Commit(); + + return new Response + { + Result = flag, + Message = flag == true ? "success" : "error" + }; + } + } + + #endregion + + #region 修改 + + public Response UpdateMenu(SysModuleElement model) + { + var flag = base.Repository.ChangeRepository>().Update(model); + return new Response + { + Result = flag, + Message = flag == true ? "success" : "error" + }; + } + + #endregion + + #region 删除 + + public async Task> DelMenu(string[] ids) + { + var flag = await base.Repository.ChangeRepository>() + .DeleteByIdAsync(ids.Cast().ToArray()); + return new Response + { + Result = flag, + Message = flag == true ? "success" : "error" + }; + } + + #endregion + + #endregion + + #region 用户/角色分配模块 + + /// + /// 加载特定角色的模块 + /// + /// The role unique identifier. + public async Task> LoadForRole(long roleId) + { + var respone = new Response(); + + using (var uwo = UnitWork.CreateContext()) + { + respone.Result = new ModulesWithElements + { + ElementIds = (await uwo.SysRoleElement.GetListAsync(a => a.RoleId == roleId)) + .Select(a => a.ElementId).ToList(), + ModuleIds = (await uwo.SysRoleModule.GetListAsync(a => a.RoleId == roleId)) + .Select(a => a.ModuleId).ToList() + }; + } + + return respone; + } + + #endregion 用户/角色分配模块 + + public async Task>> AllModule() + { + var modules = + await Repository.AsQueryable().Select(a => new SysModule + { + Id = a.Id, + ModuleTypeId = a.ModuleTypeId + }).Where(a => a.Status == 1).OrderBy(a => a.SortNo) + .ToListAsync(); + return new Response> { Result = modules }; + } + } +} \ No newline at end of file diff --git a/OpenAuth.App/BaseApp/ModuleManager/Request/QueryElementListReq.cs b/OpenAuth.App/BaseApp/ModuleManager/Request/QueryElementListReq.cs new file mode 100644 index 0000000..fb2512e --- /dev/null +++ b/OpenAuth.App/BaseApp/ModuleManager/Request/QueryElementListReq.cs @@ -0,0 +1,14 @@ +using OpenAuth.App.Request; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace OpenAuth.App.ModuleManager +{ + public class QueryElementListReq : PageReq + { + public string ModuleId { get; set; } + } +} diff --git a/OpenAuth.App/BaseApp/ModuleManager/Response/ModuleElementVM.cs b/OpenAuth.App/BaseApp/ModuleManager/Response/ModuleElementVM.cs new file mode 100644 index 0000000..a95b493 --- /dev/null +++ b/OpenAuth.App/BaseApp/ModuleManager/Response/ModuleElementVM.cs @@ -0,0 +1,49 @@ +namespace OpenAuth.App.Response +{ + /// + /// 用户ID + /// + public class ModuleElementVM + { + /// + /// 用户ID + /// + /// + public int Id { get; set; } + + /// + /// DOM ID + /// + /// + public string DomId { get; set; } + + /// + /// 组织名称 + /// + /// + public string Name { get; set; } + + //模块ID + public int ModuleId { get; set; } + + /// + /// 所属模块名称 + /// + public string ModuleName { get; set; } + + /// + /// 授权状态 + /// + public bool Accessed { get; set; } + + public ModuleElementVM() + { + this.Id = 0; + this.DomId = string.Empty; + this.Name = string.Empty; + this.ModuleId = 0; + this.ModuleName = string.Empty; + this.Accessed = false; + } + } +} \ No newline at end of file diff --git a/OpenAuth.App/BaseApp/ModuleManager/Response/ModuleView.cs b/OpenAuth.App/BaseApp/ModuleManager/Response/ModuleView.cs new file mode 100644 index 0000000..6ca865e --- /dev/null +++ b/OpenAuth.App/BaseApp/ModuleManager/Response/ModuleView.cs @@ -0,0 +1,84 @@ +using System.Collections.Generic; +using Infrastructure; +using OpenAuth.Repository.Domain; + +namespace OpenAuth.App.Response +{ + public class ModuleView + { + /// + /// ID + /// + /// + public string Id { get; set; } + + /// + /// 节点语义ID + /// + public string CascadeId { get; set; } + + /// + /// 名称 + /// + /// + public string Name { get; set; } + + /// + /// 主页面URL + /// + /// + public string Url { get; set; } + + /// + /// 父节点流水号 + /// + /// + public string ParentId { get; set; } + + /// + /// 父节点流水号 + /// + /// + public string ParentName { get; set; } + + /// + /// 节点图标文件名称 + /// + /// + public string IconName { get; set; } + + + /// + /// 当前状态,0:正常,-1:隐藏,不在导航列表中显示 + /// + public int Status { get; set; } + + + public bool Checked { get; set; } + + /// + /// 排序号 + /// + public int SortNo { get; set; } + + public string Code { get; set; } + + public bool IsSys { get; set; } + public string ModuleTypeId { get; set; } + + /// + /// 模块中的元素 + /// + public List Elements { get; set; } + + public static implicit operator ModuleView(SysModule module) + { + return module.MapTo(); + } + + public static implicit operator SysModule(ModuleView view) + { + return view.MapTo(); + } + } +} \ No newline at end of file diff --git a/OpenAuth.App/BaseApp/ModuleManager/Response/ModulesWithElements.cs b/OpenAuth.App/BaseApp/ModuleManager/Response/ModulesWithElements.cs new file mode 100644 index 0000000..79b4910 --- /dev/null +++ b/OpenAuth.App/BaseApp/ModuleManager/Response/ModulesWithElements.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace OpenAuth.App.ModuleManager.Response +{ + public class ModulesWithElements + { + public List ModuleIds { get; set; } + + public List ElementIds { get; set; } + } +} diff --git a/OpenAuth.App/BaseApp/OrgManager/OrgManagerApp.cs b/OpenAuth.App/BaseApp/OrgManager/OrgManagerApp.cs new file mode 100644 index 0000000..424ead0 --- /dev/null +++ b/OpenAuth.App/BaseApp/OrgManager/OrgManagerApp.cs @@ -0,0 +1,633 @@ +using Infrastructure; +using Infrastructure.Extensions; +using OpenAuth.App.Base; +using OpenAuth.App.Base.Tree; +using OpenAuth.App.BasicQueryService; +using OpenAuth.App.Interface; +using OpenAuth.App.Request; +using OpenAuth.Repository; +using OpenAuth.Repository.Core; +using OpenAuth.Repository.Domain; +using SqlSugar; +using SqlSugar.Extensions; +using System.Collections.Generic; +using System.Diagnostics; + +namespace OpenAuth.App +{ + public class OrgManagerApp : LongTreeApp + { + UserManager userManager; + + private ISqlSugarClient client; + public OrgManagerApp( + ISugarUnitOfWork unitWork, + ISimpleClient repository, + ISqlSugarClient sqlSugarClient, + UserManager userManager, + IAuth auth) : base(unitWork, repository, auth) + { + this.client = sqlSugarClient; + this.userManager = userManager; + + } + + #region 查询 + + #region 部门树 + /// + /// name 为空查全部 + /// + /// + /// + public async Task>> OrgsAllTree(string name) + { + var result = new Response>(); + var user = _auth.GetCurrentUser().User; + string sql = "select \"min\"(\"Level\") from sys_userorg where \"UserId\"='" + user.Id + "' "; + var level = client.Ado.GetInt(sql); + if (level>0) + { + + string sql2 = "select \"OrgId\" from sys_userorg where \"Level\"="+level + " and \"UserId\"='" + user.Id + "' "; + List orgIds1 = client.Ado.SqlQuery(sql2); + int count = orgIds1.Count; + for (int i = 0; i < count; i++) { + List longs = getAllChildOrgId(orgIds1[i]); + orgIds1.AddRange(longs); + } + + var orglist = base.Repository.AsSugarClient().Queryable() + .Where(a => a.UserId == user.Id).Select(a => a.OrgId) + .ToList(); + var query = base.Repository.AsQueryable().Where(a => orgIds1.Contains(a.Id) || a.CreateId == user.Id) + .Select(); + + if (!string.IsNullOrEmpty(name)) + { + object[] orgIds = (await base.Repository.AsQueryable() + .Where(a => a.Name.Contains(name)) + .Select(it => it.Id).ToListAsync()).Cast().ToArray(); + + result.Result = await query.ToTreeAsync(a => a.Children, a => a.ParentId, 0, orgIds); + } + else + { + result.Result = await query.ToTreeAsync(a => a.Children, a => a.ParentId, 0, a => a.Id); + } + + return result; + } + else { + var query = base.Repository.AsQueryable() + .Select(); + + if (!string.IsNullOrEmpty(name)) + { + object[] orgIds = (await base.Repository.AsQueryable() + .Where(a => a.Name.Contains(name)) + .Select(it => it.Id).ToListAsync()).Cast().ToArray(); + + result.Result = await query.ToTreeAsync(a => a.Children, a => a.ParentId, 0, orgIds); + } + else + { + result.Result = await query.ToTreeAsync(a => a.Children, a => a.ParentId, 0, a => a.Id); + } + + return result; + } + } + public List getAllChildOrgId( long parentId) { + var query = base.Repository.AsSugarClient().Queryable() + .Where(c => c.ParentId == parentId) + .Select(c => new { c.Id, c.ParentId }) + .ToList(); + + var childIds = query.Select(c => c.Id).ToList(); + var grandChildIds = query.SelectMany(c => getAllChildOrgId( c.Id)).ToList(); + + childIds.AddRange(grandChildIds); + return childIds; + } + /// + /// name 为空查全部 + /// + /// + /// + public async Task>> OrgsTree(string name) + { + /* var result = new Response>(); + + var userId = long.Parse(_auth.GetUserId()); + + var query = userManager.UserOrgs(userId); + + var orgs = await query + .WhereIF(!string.IsNullOrEmpty(name), (u, o) => o.Name.Contains(name)) + .Select().ToListAsync(); + + //if (!string.IsNullOrEmpty(name)) + //{ + // object[] orgIds = (await query + // .Where((u, o) => o.Name.Contains(name)) + // .Select((u, o) => o.Id).ToListAsync()).Cast().ToArray(); + + // result.Result = await query.Select() + // .ToTreeAsync(a => a.Children, a => a.ParentId, 0, orgIds); + //} + //else + //{ + // result.Result = await query.Select() + // .ToTreeAsync(a => a.Children, a => a.ParentId, 0, a => a.Id); + //} + + result.Result = orgs.BuildTree().ToList(); + + return result;*/ + var result = new Response>(); + var user = _auth.GetCurrentUser().User; + string sql = "select \"min\"(\"Level\") from sys_userorg where \"UserId\"='" + user.Id + "' "; + var level = client.Ado.GetInt(sql); + if ( level > 0) + { + + string sql2 = "select \"OrgId\" from sys_userorg where \"Level\"=" + level + " and \"UserId\"='" + user.Id + "' "; + List orgIds1 = client.Ado.SqlQuery(sql2); + int count = orgIds1.Count; + for (int i = 0; i < count; i++) + { + List longs = getAllChildOrgId(orgIds1[i]); + orgIds1.AddRange(longs); + } + + + var query = await base.Repository.AsQueryable().Where(a => orgIds1.Contains(a.Id) || a.CreateId == user.Id) + .Select().ToListAsync(); + + if (!string.IsNullOrEmpty(name)) + { + object[] orgIds = (await base.Repository.AsQueryable() + .Where(a => a.Name.Contains(name)) + .Select(it => it.Id).ToListAsync()).Cast().ToArray(); + + result.Result = query.BuildTree().ToList(); + } + else + { + result.Result = query.BuildTree().ToList(); + } + + return result; + } + else + { + var query = base.Repository.AsQueryable() + .Select(); + + if (!string.IsNullOrEmpty(name)) + { + object[] orgIds = (await base.Repository.AsQueryable() + .Where(a => a.Name.Contains(name)) + .Select(it => it.Id).ToListAsync()).Cast().ToArray(); + + result.Result = await query.ToTreeAsync(a => a.Children, a => a.ParentId, 0, orgIds); + } + else + { + result.Result = await query.ToTreeAsync(a => a.Children, a => a.ParentId, 0, a => a.Id); + } + + return result; + } + } + + /// + /// 部门和职级组合树 + /// + /// + public async Task>> OrgPositonTree() + { + var client = base.Repository.AsSugarClient(); + + var orgQuery = base.Repository.AsQueryable() + .Select(a => new TreeItemLong { Key = 0, Tag = "" }, true); + + var posQuery = client.Queryable((p, o) => + new JoinQueryInfos(JoinType.Left, p.GroupId == o.PosGroupId)) + .Where((p, o) => o.Id != null) + .Select((p, o) => new TreeItemLong + { + Key = p.Id, + Tag = o.Id.ToString(), + Id = p.Id, + Name = p.Name, + ParentId = p.ParentId == 0 ? o.Id : p.ParentId, + }); + + var poses = posQuery.ToList(); + + var list = await client.UnionAll(orgQuery, posQuery) + .Mapper(a => + { + if (a.Id == a.Key) + { + var item = poses.Where(m => m.Id == a.Id).First(); + item.Id = Yitter.IdGenerator.YitIdHelper.NextId(); + + a.Id = item.Id; + + var parent = poses.Where(m => m.Key == a.ParentId && m.Tag == a.Tag).FirstOrDefault(); + if (parent != null) + { + a.ParentId = parent.Id; + } + } + }) + .ToTreeAsync(a => a.Children, a => a.ParentId, 0, a => a.Id); + + return new Response> + { + Result = list + }; + + } + #endregion + + #region 分页 + /// + /// 查询部门列表 + /// + /// + /// + public async Task>>> LoadAllPage(QueryOrgListReq req) + { + var user = _auth.GetCurrentUser().User; + string sql = "select \"min\"(\"Level\") from sys_userorg where \"UserId\"='" + user.Id + "' "; + var level = client.Ado.GetInt(sql); + if ( level > 0) + { + + string sql2 = "select \"OrgId\" from sys_userorg where \"Level\"=" + level + " and \"UserId\"='" + user.Id + "' "; + List orgIds1 = client.Ado.SqlQuery(sql2); + int count = orgIds1.Count; + for (int i = 0; i < count; i++) + { + List longs = getAllChildOrgId(orgIds1[i]); + orgIds1.AddRange(longs); + } + RefAsync totalCount = 0; + + var orgs = await base.Repository.AsQueryable() + .WhereIF(req.OrgId != 0, o => o.ParentId == req.OrgId) + .WhereIF(!string.IsNullOrEmpty(req.key), o => o.Name.Contains(req.key)) + .Where(a => orgIds1.Contains(a.Id) || a.CreateId == user.Id) + .OrderByDescending(o => o.CreateTime) + .ToPageListAsync(req.page, req.limit, totalCount); + + return new Response>> + { + Result = new PageInfo> + { + Items = orgs, + Total = totalCount + } + }; + } + else { + RefAsync totalCount = 0; + + var orgs = await base.Repository.AsQueryable() + .WhereIF(req.OrgId != 0, o => o.ParentId == req.OrgId) + .WhereIF(!string.IsNullOrEmpty(req.key), o => o.Name.Contains(req.key)) + .OrderByDescending(o => o.CreateTime) + .ToPageListAsync(req.page, req.limit, totalCount); + + return new Response>> + { + Result = new PageInfo> + { + Items = orgs, + Total = totalCount + } + }; + } + + } + + public async Task>>> LoadPage(QueryOrgListReq req) + { + + var user = _auth.GetCurrentUser().User; + string sql = "select \"min\"(\"Level\") from sys_userorg where \"UserId\"='" + user.Id + "' "; + var level = client.Ado.GetInt(sql); + if (level > 0) + { + + string sql2 = "select \"OrgId\" from sys_userorg where \"Level\"=" + level + " and \"UserId\"='" + user.Id + "' "; + List orgIds1 = client.Ado.SqlQuery(sql2); + int count = orgIds1.Count; + for (int i = 0; i < count; i++) + { + List longs = getAllChildOrgId(orgIds1[i]); + orgIds1.AddRange(longs); + } + RefAsync totalCount = 0; + + var orgs = await base.Repository.AsQueryable() + .WhereIF(req.OrgId != 0, o => o.ParentId == req.OrgId) + .WhereIF(!string.IsNullOrEmpty(req.key), o => o.Name.Contains(req.key)) + .Where(a => orgIds1.Contains(a.Id) || a.CreateId == user.Id) + .OrderByDescending(o => o.CreateTime) + .ToPageListAsync(req.page, req.limit, totalCount); + + return new Response>> + { + Result = new PageInfo> + { + Items = orgs, + Total = totalCount + } + }; + } + else + { + RefAsync totalCount = 0; + + var userId = long.Parse(_auth.GetUserId()); + + var orgs = await userManager.UserOrgs(userId) + .WhereIF(req.OrgId != 0, (u, o) => o.ParentId == req.OrgId) + .WhereIF(!string.IsNullOrEmpty(req.key), (u, o) => o.Name.Contains(req.key)) + .OrderByDescending((u, o) => o.CreateTime) + .Select((u, o) => o) + .ToPageListAsync(req.page, req.limit, totalCount); + + return new Response>> + { + Result = new PageInfo> + { + Items = orgs, + Total = totalCount + } + }; + } + + } + #endregion + + #region 实体 + public SysOrg Get(string id) + { + return Repository.GetById(id); + } + #endregion + + #region 子节点 + public async Task> LoadChildren(long? parentId) + { + SysUser user = _auth.GetCurrentUser().User; + //string sql = "select \"min\"(\"Level\") from sys_userorg where \"UserId\"='"+user.Id+"' "; + //var level = client.Ado.GetInt(sql); + /*if (user.Id == -1||level==0) + {*/ + List children = new List(); + if (parentId != null) + { + children= await base.Repository.AsQueryable().Where(r=>r.ParentId==parentId).OrderBy(r=>r.SortNo).ToListAsync(); + } + return children; + /* } + else + { + if (parentId == 0) + { + return await base.Repository.GetListAsync(a => a.ParentId == parentId); + } else if(parentId== 371300) + { + var orglist = base.Repository.AsSugarClient().Queryable() + .Where(a => a.UserId == user.Id).Select(a => a.OrgId) + .ToList(); + return await base.Repository.GetListAsync(a => orglist.Contains(a.Id) || a.CreateId == user.Id); + } + else + { + var orglist = base.Repository.AsSugarClient().Queryable() + .Where(a => a.UserId == user.Id).Select(a => a.OrgId) + .ToList(); + return await base.Repository.GetListAsync(a => a.ParentId == parentId && orglist.Contains(a.Id) || a.CreateId == user.Id); + } + + }*/ + } + + #region 用户关联的部分和职级 + public async Task> UserOrgs(long userId) + { + return await base.Repository.AsSugarClient().Queryable() + .Where(a => a.UserId == userId) + .ToListAsync(); + } + #endregion + #endregion + + #region 父节点 + public async Task> LoadParents(long childId) + { + return await client.Queryable().ToParentListAsync(it => it.ParentId, childId); + } + #endregion + + #endregion + + #region 增删改 + + #region 增加 + /// + /// 添加部门 + /// + /// The org. + /// System.Int32. + /// 未能找到该组织的父节点信息 + public Response Add(OrgForm org) + { + var loginContext = _auth.GetCurrentUser(); + if (loginContext == null) + { + throw new CommonException("登录已过期", Define.INVALID_TOKEN); + } + + SysOrg model = org.MapTo(); + + //后边再看 + //CalculateCascade(model); + + using (var uow = base.UnitWork.CreateContext()) + { + if (model.Id <= 0) { + model.Id = Yitter.IdGenerator.YitIdHelper.NextId(); + } + + model.ParentName = getParentName(model.ParentId); + model.CreateId = loginContext.User.Id; + model.CreateTime = DateTime.Now; + if (model.ParentId == 0) { + model.Level = 0; + model.CascadeId = "0."+model.Id; + }else + { + long parentId = model.ParentId; + int Level = 0; + string cascade = ""; + while (true) + { + String sql = "select \"ParentId\" from sys_org where \"Id\"=" + parentId; + long id = client.Ado.GetLong(sql); + // long id = base.Repository.AsSugarClient().Queryable().Where(a => a.Id == parentId).Select(a => a.ParentId).ToLong(); + if (!string.IsNullOrEmpty(cascade)) { + cascade = id + "." + cascade; + }else + { + cascade = id + cascade; + } + + parentId = id; + Level++; + if (id == 0) { + break; }} + model.Level = Level; + model.CascadeId = cascade+"."+model.ParentId+"."+model.Id; + } + + uow.SysOrg.Insert(model); + var falg = uow.Commit(); + return new Response + { + Result = falg, + Message = (falg == true ? "添加成功" : "添加失败") + }; + } + } + #endregion + + #region 修改 + public Response Update(SysOrg sysOrg) + { + if (sysOrg.Id == sysOrg.ParentId) + { + throw new Exception("上级节点不能为自己"); + } + try + { + var user = _auth.GetCurrentUser().User; + sysOrg.ParentName=getParentName(sysOrg.ParentId); + sysOrg.CreateId = user.Id; + UpdateSysOrg(sysOrg); + + return new Response { Result = true, Message = "success" }; + } + catch (Exception ex) + { + return new Response { Result = false, Message = "error:" + ex.Message.ToString() }; + } + } + #endregion + + #region 删除 + /// + /// 删除指定ID的部门及其所有子部门 + /// + public Response DelOrgCascade(string[] ids) + { + var delOrgCascadeIds = base.Repository.GetList(u => ids.Contains(u.Id.ToString())).Select(u => u.CascadeId).ToArray(); + var delOrgIds = new List(); + foreach (var cascadeId in delOrgCascadeIds) + { + delOrgIds.AddRange(base.Repository.GetList(u => u.CascadeId.Contains(cascadeId)).Select(u => u.Id).ToArray()); + } + + using (var uow = base.UnitWork.CreateContext()) + { + //得改 代码先注释 + //uow.Relevance.Delete(u => u.Key == Define.USERORG && delOrgIds.Contains(u.SecondId)); + uow.SysOrg.Delete(u => delOrgIds.Contains(u.Id)); + var flag = uow.Commit(); + + return new Response + { + Result = flag, + Message = flag == true ? "success" : "error" + }; + } + } + #endregion + + #region 分配职级组 + /// + /// 为部门分配职级组 + /// + /// + /// + public async Task> OrgPosGroup(AssignOrgPosGroup assignOrgPos) + { + var flag = await base.Repository.UpdateAsync(a => new SysOrg { PosGroupId = assignOrgPos.PosGroupId }, a => a.Id == assignOrgPos.OrgId); + return new Response + { + Result = flag, + Message = flag == true ? "success" : "error" + }; + } + #endregion + + #endregion + public string getParentName(long parentId) { + string name = base.Repository.AsSugarClient().Queryable().First(a => a.Id == parentId).Name; + return name; + } + + + + + public void UpdateSysOrg(SysOrg obj) + { + if (obj.ParentId == 0) + { + obj.Level = 0; + obj.CascadeId = "0." + obj.Id; + } + else + { + long parentId = obj.ParentId; + int Level = 0; + string cascade = ""; + while (true) + { + string sql = "select \"ParentId\" from sys_org where \"Id\"=" + parentId; + long id = base.Repository.AsSugarClient().Ado.GetLong(sql); + //long id = base.Repository.AsSugarClient().Queryable().Where(a => a.Id == parentId).Select(a => a.ParentId).ToLong(); + if (!string.IsNullOrEmpty(cascade)) + { + cascade = id + "." + cascade; + } + else + { + cascade = id + cascade; + } + + parentId = id; + Level++; + if (id == 0) + { + break; + } + } + obj.Level = Level; + obj.CascadeId = cascade + "." + obj.ParentId + "." + obj.Id; + } + base.Repository.AsSugarClient().Updateable(obj).ExecuteCommand(); + } + + + + } +} \ No newline at end of file diff --git a/OpenAuth.App/BaseApp/OrgManager/Request/AssignOrgPosGroup.cs b/OpenAuth.App/BaseApp/OrgManager/Request/AssignOrgPosGroup.cs new file mode 100644 index 0000000..b8b8e15 --- /dev/null +++ b/OpenAuth.App/BaseApp/OrgManager/Request/AssignOrgPosGroup.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace OpenAuth.App.Request +{ + public class AssignOrgPosGroup + { + public long OrgId { get; set; } + + public long PosGroupId { get; set; } + } +} diff --git a/OpenAuth.App/BaseApp/OrgManager/Request/OrgForm.cs b/OpenAuth.App/BaseApp/OrgManager/Request/OrgForm.cs new file mode 100644 index 0000000..76928d6 --- /dev/null +++ b/OpenAuth.App/BaseApp/OrgManager/Request/OrgForm.cs @@ -0,0 +1,19 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Xml.Linq; + +namespace OpenAuth.App.Request +{ + public class OrgForm + { + public long Id { get; set; } + public string CascadeId { get; set; } + public string Name { get; set; } + public int Status { get; set; } + public int SortNo { get; set; } + public long ParentId { get; set; } + } +} diff --git a/OpenAuth.App/BaseApp/OrgManager/Request/OrgPos.cs b/OpenAuth.App/BaseApp/OrgManager/Request/OrgPos.cs new file mode 100644 index 0000000..1b93994 --- /dev/null +++ b/OpenAuth.App/BaseApp/OrgManager/Request/OrgPos.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace OpenAuth.App.Request +{ + public class OrgPos + { + public long OrgId { get; set; } + + public long PosId { get; set; } + + public int Level { get; set; } + } +} diff --git a/OpenAuth.App/BaseApp/OrgManager/Request/QueryOrgListReq.cs b/OpenAuth.App/BaseApp/OrgManager/Request/QueryOrgListReq.cs new file mode 100644 index 0000000..29895fd --- /dev/null +++ b/OpenAuth.App/BaseApp/OrgManager/Request/QueryOrgListReq.cs @@ -0,0 +1,14 @@ +using OpenAuth.App.Request; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace OpenAuth.App.Request +{ + public class QueryOrgListReq : PageReq + { + public long OrgId { get; set; } + } +} diff --git a/OpenAuth.App/BaseApp/Permission/BaseDataAuthorizeApp.cs b/OpenAuth.App/BaseApp/Permission/BaseDataAuthorizeApp.cs new file mode 100644 index 0000000..8420223 --- /dev/null +++ b/OpenAuth.App/BaseApp/Permission/BaseDataAuthorizeApp.cs @@ -0,0 +1,507 @@ +using System.Text; +using Infrastructure; +using OpenAuth.App.Base; +using OpenAuth.App.BaseApp.Base; +using OpenAuth.App.Interface; +using OpenAuth.App.Permission.Request; +using OpenAuth.Repository; +using OpenAuth.Repository.Domain; +using SqlSugar; + +namespace OpenAuth.App.Permission; + +/// +/// 数据权限业务类 +/// +public class BaseDataAuthorizeApp : SqlSugarBaseApp +{ + /// + /// 构造函数 + /// + /// + /// + /// + public BaseDataAuthorizeApp(ISugarUnitOfWork unitWork, ISimpleClient repository, + IAuth auth) : base(unitWork, repository, auth) + { + } + + + #region 获取数据 + + /// + /// 获取数据权限对应关系数据列表 + /// + /// 编码 + /// 用户或角色主键 + /// + public Task> GetList(string code, string[] objectIds) + { + return Repository.AsQueryable() + .Where(t => t.Code.Equals(code) + && (objectIds.Contains(t.ObjectId) || t.ObjectType == 3) + ).ToListAsync(); + } + + /// + /// 获取数据权限列表(分页) + /// + public async Task>>> GetPageList(BaseDataAuthListReq req) + { + RefAsync totalCount = 0; + // 实现查询及分页 + var result = await Repository.AsQueryable() + .WhereIF(req.Type != null, t => t.Type == req.Type) + .WhereIF(!string.IsNullOrEmpty(req.ObjectId), t => t.ObjectId == req.ObjectId) + .WhereIF(!string.IsNullOrEmpty(req.key), t => t.Name.Contains(req.key) || t.Code.Contains(req.key)) + .ToPageListAsync(req.page, req.limit, totalCount); + + return new Response>> + { + Result = new PageInfo> + { + Items = result, + Total = totalCount + } + }; + } + + /// + /// 获取实体 + /// + /// 主键 + /// + public Task GetEntity(string keyValue) + { + return Repository.GetByIdAsync(keyValue); + } + + #endregion + + #region 提交数据 + + /// + /// 删除实体数据 + /// + /// 主键 + /// + public async Task DeleteEntity(string keyValue) + { + await Repository.DeleteByIdAsync(keyValue); + } + + /// + /// 保存实体数据(新增、修改) + /// + /// 主键 + /// 实体 + /// + public async Task SaveEntity(string keyValue, BaseDataAuthReq record) + { + var user = _auth.GetCurrentUser(); + var entity = record.MapTo(); + entity.Formula = record.Formula.ToJson(); + if (string.IsNullOrEmpty(keyValue)) + { + entity.Id = Guid.NewGuid().ToString(); + entity.CreateDate = DateTime.Now; + entity.CreateUserId = user.User.Id.ToString(); + entity.CreateUserName = user.User.Name; + } + else + { + entity.Id = keyValue; + entity.ModifyDate = DateTime.Now; + entity.ModifyUserId = user.User.Id.ToString(); + entity.ModifyUserName = user.User.Name; + } + // 全部值都更新了 + await Repository.InsertOrUpdateAsync(entity); + } + + #endregion + + #region 扩展方法 + + /// + /// 获取自定义表单数据权限查询条件 + /// + /// 自定义表单功能主键 + /// + /// todo 可以优化,使代码逻辑清晰,减少代码量,比如有无公式,可以合并部分逻辑 + /// 逻辑:1. 超级用户不做过滤 2. 先看有无公式(默认条件使用and连接),再看有无分组公式(分组公式使用and连接) + public async Task GetWhereSql(string code) + { + // 取得当前用户信息 + var userInfo = _auth.GetCurrentUser(); + // 超级用户不做过滤 todo 暂时注释 + /*if (userInfo.User.Id.Equals(-1)) + { + return ""; + }*/ + + // 26个字母干嘛用的?别名? + var numIndex = new[] + { + "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", + "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z" + }; + + // 获取数据权限配置信息 + // 查询当前用户所拥有的角色或者岗位 + var roleIds = userInfo.Roles.Select(t => t.Id).MapToList(); + var objectIds = new List { userInfo.User.Id + "", }; + objectIds.AddRange(roleIds); + // code 自定义表单功能主键 查询当前用户或者用户所属角色是否有设置的权限规则 + var baseDataAuths = await GetList(code, objectIds.ToArray()); + + // 保存拼接的sql + var whereSqlSb = new StringBuilder(); + var sqlNum = 0; + foreach (var auth in baseDataAuths) + { + //{"formula":"A and B", + //"conditions":[ + //{"f_FieldId":"F_text0","f_Symbol":1,"f_FieldValueType":1,"f_FieldValue":"xxx","group":"","type":"","table":"","cfield":"","field":"","relationField":""}], + //"formulas":[1 or 2]} + // 判断是否是开头 + // 先运行这里 + // 不同数据权限规则使用or ,很可能只有一条吧 + whereSqlSb.Append(sqlNum > 0 ? " AND ( " : " ( "); + var strSql = ""; + // 反序列化数据权限公式 + var itemFormula = auth.Formula.ToObject(); + // formula 不存在时,直接使用and连接 + if (string.IsNullOrEmpty(itemFormula.Formula)) + { + // Conditions + // 默认公式 + for (var i = 1; i < itemFormula.Conditions.Count + 1; i++) + { + var conditionItem = itemFormula.Conditions[i - 1]; + conditionItem.index = i; // 赋值索引,后面应该会用到,从1开始 + // 这里的Group是字符串,大概是字母 A B 之类 ,如何没有Group,条件之间用 and 连接 + if (!string.IsNullOrEmpty(conditionItem.Group)) + { + continue; + } + + if (strSql != "") + { + strSql += " AND "; + } + + strSql += " {@hopetry" + i + "hopetry@} "; + } + + // 公式为空,分组公式不为空 + if (itemFormula.Formulas != null && itemFormula.Formulas.Count > 0) + { + // 假设分组公式只有1个 【{“value”: "1 or 2"}】 多个分组公式之间,使用and连接 + for (var i = 0; i < itemFormula.Formulas.Count; i++) + { + if (itemFormula.Conditions.FindIndex(t => t.Group == numIndex[i]) != -1) + { + if (strSql != "") + { + strSql += " AND "; + } + + strSql += " {@hopetry" + numIndex[i] + "hopetry@} "; + } + } + } + } + // 存在公式 + else + { + // 当分组公式存在时,公式含有分组信息,如: A and B + strSql = itemFormula.Formula; + // 为什么从1开头 + for (var i = 1; i < itemFormula.Conditions.Count + 1; i++) + { + var conditionItem = itemFormula.Conditions[i - 1]; + // 取了条件没有使用 + conditionItem.index = i; + strSql = strSql.Replace("" + i, "{@hopetry" + i + "hopetry@}"); + } + + if (itemFormula.Formulas != null && itemFormula.Formulas.Count > 0) + { + for (var i = 0; i < itemFormula.Formulas.Count; i++) + { + strSql = strSql.Replace(numIndex[i], "{@hopetry" + numIndex[i] + "hopetry@}"); + } + } + } + + // 针对公组公式存在,再做部分处理,todo 暂时可以忽略,不实现分组 + if (itemFormula.Formulas != null && itemFormula.Formulas.Count > 0) + { + // 分组公式 + for (var i = 0; i < itemFormula.Formulas.Count; i++) + { + var groupSql = new StringBuilder(); + var groupList = itemFormula.Conditions.FindAll(t => t.Group == numIndex[i]); + if (groupList.Count > 0) + { + groupSql.Append( + // relationField 关联字段(外键) 这里的Table应该是子表吧 + $" {groupList[0].RelationField} in ( SELECT {groupList[0].Field} FROM {groupList[0].Table} WHERE "); + + // 初始化分组公式 + var groupSqlWhere = ""; + if (string.IsNullOrEmpty(itemFormula.Formulas[i].Value)) + { + foreach (var groupItem in groupList) + { + if (groupSqlWhere != "") + { + groupSqlWhere += " AND "; + } + + groupSqlWhere += " {@hopetry" + groupItem.index + "hopetry@} "; + } + } + else + { + groupSqlWhere = itemFormula.Formulas[i].Value; + foreach (var groupItem in groupList) + { + groupSqlWhere = groupSqlWhere.Replace("" + groupItem.index, + "{@hopetry" + groupItem.index + "hopetry@}"); + } + } + + foreach (var groupItem in groupList) + { + var strone = new StringBuilder(); + if (groupItem.F_Symbol.Equals("8")) + { + strone.Append($" ({groupItem.Cfield} "); + } + else + { + strone.Append($" {groupItem.Cfield} "); + } + + var value = await GetValue(int.Parse(groupItem.F_FieldValueType), groupItem.F_FieldValue); + switch (int.Parse(groupItem.F_Symbol)) + { + case 1: // 等于 + strone.Append($" = {value}"); + break; + case 2: // 大于 + strone.Append($" > {value}"); + break; + case 3: // 大于等于 + strone.Append($" >= {value}"); + break; + case 4: // 小于 + strone.Append($" < {value}"); + break; + case 5: // 小于等于 + strone.Append($" <= {value}"); + break; + case 6: // 包含 + value = value.Replace("'", ""); + strone.Append($" like '%{value}%'"); + break; + case 7: // 包含于 + value = value.Replace(",", "','"); + strone.Append($" in ({value})"); + break; + case 8: // 不等于 + strone.Append($" != {value} or {groupItem.Cfield} is null ) "); + break; + case 9: // 不包含 + value = value.Replace("'", ""); + strone.Append($" not like '%{value}%'"); + break; + case 10: // 不包含于 + value = value.Replace(",", "','"); + strone.Append($" not in ({value})"); + break; + default: + break; + } + + groupSqlWhere = groupSqlWhere.Replace("{@hopetry" + groupItem.index + "hopetry@}", + strone.ToString()); + } + + groupSql.Append(groupSqlWhere); + groupSql.Append(" ) "); + strSql = strSql.Replace("{@hopetry" + numIndex[i] + "hopetry@}", groupSql.ToString()); + } + } + } + + // 填充占位符,生成实际wheresql + var num = 1; + foreach (var conditionItem in itemFormula.Conditions) + { + // 这里的group应该是 A B C 之类的字母 + if (string.IsNullOrEmpty(conditionItem.Group)) + { + var strone = new StringBuilder(); + if (conditionItem.Type == "glbd") // 未使用 + { + strone.Append( + $" {conditionItem.RelationField} in ( SELECT {conditionItem.Field} FROM {conditionItem.Table} WHERE {conditionItem.Cfield} "); + } + else + { + // 8 不等于 + if (conditionItem.F_Symbol.Equals("8")) + { + strone.Append($" ( {conditionItem.F_FieldId}"); + } + else + { + strone.Append($" {conditionItem.F_FieldId}"); + } + } + + var value = await GetValue(int.Parse(conditionItem.F_FieldValueType), conditionItem.F_FieldValue); + // 如果 value 值是 userId + switch (int.Parse(conditionItem.F_Symbol)) + { + case 1: // 等于 + strone.Append($" = {value}"); + break; + case 2: // 大于 + strone.Append($" > {value}"); + break; + case 3: // 大于等于 + strone.Append($" >= {value}"); + break; + case 4: // 小于 + strone.Append($" < {value}"); + break; + case 5: // 小于等于 + strone.Append($" <= {value}"); + break; + case 6: // 包含 + value = value.Replace("'", ""); + strone.Append($" like '%{value}%'"); + break; + case 7: // 包含于 + value = value.Replace(",", "','"); + value = value.Replace("#", ","); + strone.Append($" in ({value})"); + break; + case 8: // 不等于 + strone.Append($" != {value}"); + + if (conditionItem.Type == "glbd") + { + strone.Append($" or {conditionItem.Cfield} is null "); + } + else + { + strone.Append($" or {conditionItem.F_FieldId} is null ) "); + } + + break; + case 9: // 不包含 + value = value.Replace("'", ""); + strone.Append($" not like '%{value}%'"); + break; + case 10: // 不包含于 + value = value.Replace(",", "','"); + strone.Append($" not in ({value})"); + break; + default: + break; + } + + strone.Append(conditionItem.Type == "glbd" ? " ) " : " "); + strSql = strSql.Replace("{@hopetry" + num + "hopetry@}", strone.ToString()); + } + + num++; + } + + whereSqlSb.Append(strSql); + whereSqlSb.Append(" ) "); + + sqlNum++; + } + + Console.WriteLine("strRes:" + whereSqlSb.ToString()); + return whereSqlSb.ToString(); + } + + /// + /// 获取数据 where 值部分 + /// + /// 数据类型 + /// 数据值 + /// + /// + + // todo 扩展改造 + private async Task GetValue(int? type, string value) + { + var userInfo = _auth.GetCurrentUser(); + //1.文本(string) 2.登录者ID 3.登录者账号 4.登录者部门 5. 登录者部门及下属部门 6.登录者岗位 7.登录者角色 10. 文本(int) + string text; + switch (type) + { + case 1: // 文本(string) + text = $"'{value}'"; + break; + case 10: // 文本(int) + text = value; + break; + case 2: // 登录者ID(本地创建数据) + text = userInfo.User.Id + ""; + break; + case 3: // 登录者账号 + text = $"'{userInfo.User.Account}'"; + break; + // todo 扩展实现 + case 4: // 本部门 + text = + $"( SELECT CAST ( \"OrgId\" AS VARCHAR ) AS \"OrgId\" FROM sys_userorg WHERE \"UserId\" = '{userInfo.User.Id}' )"; + break; + case 5: // 本部门及子部门 + text = + $"select CAST(\"Id\" AS VARCHAR) from sys_org where string_to_array(\"CascadeId\"#'.') && ARRAY['{string.Join(",", userInfo.Orgs.Select(t => t.Id).MapToList())}']"; + break; + case 6: + text = ""; + break; + case 7: + text = $"'{string.Join(",", userInfo.Roles.Select(t => t.Id).MapToList())}'"; + break; + default: + text = $"'{value}'"; + break; + } + + return text; + } + + #endregion + + public async Task> GetEntityByCode(string code) + { + var formModuleEntity = await Repository + .ChangeRepository>() + .GetFirstAsync(r => r.Id == code); + if (formModuleEntity != null) + { + var formScheme = await base.Repository.ChangeRepository>() + .GetFirstAsync(r => r.Id == formModuleEntity.FormVerison); + return new Response() + { + Result = formScheme, + Message = "查询成功" + }; + } + + throw new Exception("code无效"); + } +} \ No newline at end of file diff --git a/OpenAuth.App/BaseApp/Permission/Request/BaseDataAuthListReq.cs b/OpenAuth.App/BaseApp/Permission/Request/BaseDataAuthListReq.cs new file mode 100644 index 0000000..1d6ee96 --- /dev/null +++ b/OpenAuth.App/BaseApp/Permission/Request/BaseDataAuthListReq.cs @@ -0,0 +1,16 @@ +using OpenAuth.App.Request; + +namespace OpenAuth.App.Permission.Request; + +public class BaseDataAuthListReq : PageReq +{ + /// + /// 用户或角色主键 + /// + public string ObjectId { get; set; } + + /// + /// 1.普通权限 2.自定义表单权限 + /// + public int? Type { get; set; } +} \ No newline at end of file diff --git a/OpenAuth.App/BaseApp/Permission/Request/BaseDataAuthReq.cs b/OpenAuth.App/BaseApp/Permission/Request/BaseDataAuthReq.cs new file mode 100644 index 0000000..f07a018 --- /dev/null +++ b/OpenAuth.App/BaseApp/Permission/Request/BaseDataAuthReq.cs @@ -0,0 +1,53 @@ +namespace OpenAuth.App.Permission.Request; + +/// +/// 数据权限对应表的实体类。 +/// +public class BaseDataAuthReq +{ + /// + /// 主键ID。 + /// + public string Id { get; set; } + + /// + /// 权限名称。 + /// + public string Name { get; set; } + + /// + /// 权限类型。1表示普通权限,2表示自定义表单权限。 + /// + public int Type { get; set; } + + /// + /// 权限编码。 + /// + public string Code { get; set; } + + /// + /// 对象主键。 + /// + public string ObjectId { get; set; } + + /// + /// 对象类型。1表示角色,2表示用户。 + /// + public int ObjectType { get; set; } + + /// + /// 用户名/角色名 + /// + public string ObjectName { get; set; } + + /// + /// 自定义表单名称 + /// + public string CodeName { get; set; } + + // 使用实体实现 + /// + /// 条件公式。 + /// + public FormulaObject Formula { get; set; } +} \ No newline at end of file diff --git a/OpenAuth.App/BaseApp/Permission/Request/Condition.cs b/OpenAuth.App/BaseApp/Permission/Request/Condition.cs new file mode 100644 index 0000000..080cd78 --- /dev/null +++ b/OpenAuth.App/BaseApp/Permission/Request/Condition.cs @@ -0,0 +1,77 @@ +using Newtonsoft.Json; + +namespace OpenAuth.App.Permission.Request; + +/// +/// 设置条件 +/// +public class Condition +{ + /// + /// 编号 + /// + public int index { get; set; } + + /// + /// 字段ID + /// + /// + [JsonProperty("f_FieldId")] + public string F_FieldId { get; set; } + + /// + /// 比较符 1.等于 2.包含 3.包含于 4.不等于 5.不包含 6.不包含于 + /// + /// + [JsonProperty("f_Symbol")] + public string F_Symbol { get; set; } + + /// + /// 字段值类型1.文本 2.登录者ID 3.登录者账号 4.登录者部门 5. 登录都部门及下属部门 6.登录者岗位 7.登录者角色 + /// + [JsonProperty("f_FieldValueType")] + public string F_FieldValueType { get; set; } + + /// + /// 字段值 + /// + /// + [JsonProperty("f_FieldValue")] + public string F_FieldValue { get; set; } + + /// + /// 类型 glbd关联表单数据类型,需要特殊处理 + /// + [JsonProperty("type")] + public string Type { get; set; } + + /// + /// 表名称 + /// + [JsonProperty("table")] + public string Table { get; set; } + + /// + /// 比较字段 + /// + [JsonProperty("cfield")] + public string Cfield { get; set; } + + /// + /// 外键 + /// + [JsonProperty("field")] + public string Field { get; set; } + + /// + /// 关联表字段 + /// + [JsonProperty("relationField")] + public string RelationField { get; set; } + + /// + /// 分组编号 这里的Group是属性名称和下面的Group类不是一回事 + /// + [JsonProperty("group")] + public string Group { get; set; } +} diff --git a/OpenAuth.App/BaseApp/Permission/Request/FormulaObject.cs b/OpenAuth.App/BaseApp/Permission/Request/FormulaObject.cs new file mode 100644 index 0000000..0ae1d52 --- /dev/null +++ b/OpenAuth.App/BaseApp/Permission/Request/FormulaObject.cs @@ -0,0 +1,28 @@ +using Newtonsoft.Json; + +namespace OpenAuth.App.Permission.Request; + +/// +/// 公式 +/// +public class FormulaObject +{ + /// + /// 设置公式 1 and 2 + /// + [JsonProperty("formula")] + public string Formula { get; set; } + + /// + /// 设置条件 + /// + [JsonProperty("conditions")] + public List Conditions { get; set; } + + + /// + /// 分组公式 (A or B) + /// + [JsonProperty("formulas")] + public List Formulas { get; set; } +} \ No newline at end of file diff --git a/OpenAuth.App/BaseApp/Permission/Request/Group.cs b/OpenAuth.App/BaseApp/Permission/Request/Group.cs new file mode 100644 index 0000000..bac8cf8 --- /dev/null +++ b/OpenAuth.App/BaseApp/Permission/Request/Group.cs @@ -0,0 +1,9 @@ +namespace OpenAuth.App.Permission.Request; + +/// +/// 分组公式 +/// +public class Group +{ + public string Value { get; set; } +} \ No newline at end of file diff --git a/OpenAuth.App/BaseApp/Relevance/Request/AssignDataReq.cs b/OpenAuth.App/BaseApp/Relevance/Request/AssignDataReq.cs new file mode 100644 index 0000000..f19c83f --- /dev/null +++ b/OpenAuth.App/BaseApp/Relevance/Request/AssignDataReq.cs @@ -0,0 +1,21 @@ +namespace OpenAuth.App.Request +{ + /// + /// 为角色分配数据字段权限 + /// + public class AssignDataReq + { + /// + /// 角色ID + /// + public string RoleId { get; set; } + /// + /// 模块的Code,比如Category/Resource + /// + public string ModuleCode { get; set; } + /// + /// 字段名称列表 + /// + public string[] Properties { get; set; } + } +} \ No newline at end of file diff --git a/OpenAuth.App/BaseApp/Relevance/Request/AssignOrgUsers.cs b/OpenAuth.App/BaseApp/Relevance/Request/AssignOrgUsers.cs new file mode 100644 index 0000000..cf0c5bc --- /dev/null +++ b/OpenAuth.App/BaseApp/Relevance/Request/AssignOrgUsers.cs @@ -0,0 +1,17 @@ +namespace OpenAuth.App.Request +{ + /// + /// 部门分配用户 + /// + public class AssignOrgUsers + { + /// + /// 部门id + /// + public string OrgId { get; set; } + /// + /// 用户id列表 + /// + public string[] UserIds { get; set; } + } +} \ No newline at end of file diff --git a/OpenAuth.App/BaseApp/Relevance/Request/AssignReq.cs b/OpenAuth.App/BaseApp/Relevance/Request/AssignReq.cs new file mode 100644 index 0000000..fff707d --- /dev/null +++ b/OpenAuth.App/BaseApp/Relevance/Request/AssignReq.cs @@ -0,0 +1,18 @@ +namespace OpenAuth.App.Request +{ + /// + /// 比如给用户分配资源,那么firstId就是用户ID,secIds就是资源ID列表 + /// + public class AssignReq + { + /// + /// 分配的关键字,比如:UserRole + /// + public string type { get; set; } + /// + /// 比如给用户分配角色,那么firstId就是用户ID,secIds就是角色ID列表 + /// + public string firstId { get; set; } + public string[] secIds { get; set; } + } +} \ No newline at end of file diff --git a/OpenAuth.App/BaseApp/Relevance/Request/AssignRoleUsers.cs b/OpenAuth.App/BaseApp/Relevance/Request/AssignRoleUsers.cs new file mode 100644 index 0000000..e1fa8f7 --- /dev/null +++ b/OpenAuth.App/BaseApp/Relevance/Request/AssignRoleUsers.cs @@ -0,0 +1,17 @@ +namespace OpenAuth.App.Request +{ + /// + /// 角色分配用户 + /// + public class AssignRoleUsers + { + /// + /// 角色id + /// + public string RoleId { get; set; } + /// + /// 用户id列表 + /// + public string[] UserIds { get; set; } + } +} \ No newline at end of file diff --git a/OpenAuth.App/BaseApp/Relevance/RevelanceManagerApp.cs b/OpenAuth.App/BaseApp/Relevance/RevelanceManagerApp.cs new file mode 100644 index 0000000..35c3b54 --- /dev/null +++ b/OpenAuth.App/BaseApp/Relevance/RevelanceManagerApp.cs @@ -0,0 +1,240 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Infrastructure; +using Microsoft.Extensions.Logging; +using OpenAuth.App.Base; +using OpenAuth.App.BaseApp.Base; +using OpenAuth.App.Interface; +using OpenAuth.App.Request; +using OpenAuth.Repository; +using OpenAuth.Repository.Domain; +using SqlSugar; + +namespace OpenAuth.App +{ + public class RevelanceManagerApp : SqlSugarBaseApp + { + private readonly ILogger _logger; + + public RevelanceManagerApp(ISugarUnitOfWork unitWork, ISimpleClient repository, IAuth auth) : base(unitWork, repository, auth) + { + + } + + /// + /// 添加关联 + /// 比如给用户分配资源,那么firstId就是用户ID,secIds就是资源ID列表 + /// + /// 关联的类型,如Define.USERRESOURCE + public void Assign(AssignReq request) + { + Assign(request.type, request.secIds.ToLookup(u => request.firstId)); + } + + /// + /// 添加关联,需要人工删除以前的关联 + /// + /// + /// + public void Assign(string key, ILookup idMaps) + { + Repository.InsertRange((from sameVals in idMaps + from value in sameVals + select new SysRelevance + { + Key = key, + FirstId = sameVals.Key, + SecondId = value, + OperateTime = DateTime.Now + }).ToArray()); + } + + /// + /// 取消关联 + /// + /// 关联的类型,如Define.USERRESOURCE + /// The first identifier. + /// The sec ids. + public void UnAssign(AssignReq req) + { + if (req.secIds == null || req.secIds.Length == 0) + { + DeleteBy(req.type, req.firstId); + } + else + { + DeleteBy(req.type, req.secIds.ToLookup(u => req.firstId)); + } + } + + /// + /// 删除关联 + /// + /// 关联标识 + /// 关联的<firstId, secondId>数组 + private void DeleteBy(string key, ILookup idMaps) + { + foreach (var sameVals in idMaps) + { + foreach (var value in sameVals) + { + _logger.LogInformation($"start=> delete {key} {sameVals.Key} {value}"); + try + { + Repository.Delete(u => u.Key == key && u.FirstId == sameVals.Key && u.SecondId == value); + } + catch (Exception e) + { + _logger.LogError(e, e.Message); + } + _logger.LogInformation($"end=> {key} {sameVals.Key} {value}"); + } + } + } + + public void DeleteBy(string key, params string[] firstIds) + { + Repository.Delete(u => firstIds.Contains(u.FirstId) && u.Key == key); + } + + + /// + /// 根据关联表的一个键获取另外键的值 + /// + /// 映射标识 + /// 返回的是否为映射表的第二列,如果不是则返回第一列 + /// 已知的ID列表 + /// List<System.String>. + public List Get(string key, bool returnSecondIds, params string[] ids) + { + if (returnSecondIds) + { + return Repository.GetList(u => u.Key == key + && ids.Contains(u.FirstId)).Select(u => u.SecondId).ToList(); + } + else + { + return Repository.GetList(u => u.Key == key + && ids.Contains(u.SecondId)).Select(u => u.FirstId).ToList(); + } + } + + /// + /// 根据key ,firstId,secondId获取thirdId + /// + /// + /// + /// + /// + public List Get(string key, string firstId, string secondId) + { + return Repository.GetList(u => u.Key == key && u.FirstId == firstId && u.SecondId == secondId) + .Select(u => u.ThirdId).ToList(); + } + + /// + /// 分配数据字段权限 + /// + /// + public void AssignData(AssignDataReq request) + { + if (!request.Properties.Any()) + { + return; + } + + var relevances = new List(); + foreach (var requestProperty in request.Properties) + { + relevances.Add(new SysRelevance + { + Key = Define.ROLEDATAPROPERTY, + FirstId = request.RoleId, + SecondId = request.ModuleCode, + ThirdId = requestProperty, + OperateTime = DateTime.Now + }); + } + + Repository.InsertRange(relevances.ToArray()); + + } + + /// + /// 取消数据字段分配 + /// + /// + public void UnAssignData(AssignDataReq request) + { + if (request.Properties == null || request.Properties.Length == 0) + { + if (string.IsNullOrEmpty(request.ModuleCode)) //模块为空,直接把角色的所有授权删除 + { + DeleteBy(Define.ROLEDATAPROPERTY, request.RoleId); + } + else //把角色的某一个模块权限全部删除 + { + DeleteBy(Define.ROLEDATAPROPERTY, new[] { request.ModuleCode }.ToLookup(u => request.RoleId)); + } + } + else //按具体的id删除 + { + foreach (var property in request.Properties) + { + Repository.Delete(u => u.Key == Define.ROLEDATAPROPERTY + && u.FirstId == request.RoleId + && u.SecondId == request.ModuleCode + && u.ThirdId == property); + } + } + } + + /// + /// 为角色分配用户,需要统一提交,会删除以前该角色的所有用户 + /// + /// + public void AssignRoleUsers(AssignRoleUsers request) + { + using (var uow = UnitWork.CreateContext()) + { + //删除以前的所有用户 + uow.Relevance.Delete(u => u.SecondId == request.RoleId && u.Key == Define.USERROLE); + //批量分配用户角色 + uow.Relevance.InsertRange((from firstId in request.UserIds + select new SysRelevance + { + Key = Define.USERROLE, + FirstId = firstId, + SecondId = request.RoleId, + OperateTime = DateTime.Now + }).ToArray()); + uow.Commit(); + + } + } + + /// + /// 为部门分配用户,需要统一提交,会删除以前该部门的所有用户 + /// + /// + public void AssignOrgUsers(AssignOrgUsers request) + { + using (var uow = UnitWork.CreateContext()) + { + //删除以前的所有用户 + uow.Relevance.Delete(u => u.SecondId == request.OrgId && u.Key == Define.USERORG); + //批量分配用户角色 + uow.Relevance.InsertRange((from firstId in request.UserIds + select new SysRelevance + { + Key = Define.USERORG, + FirstId = firstId, + SecondId = request.OrgId, + OperateTime = DateTime.Now + }).ToArray()); + uow.Commit(); + } + } + } +} \ No newline at end of file diff --git a/OpenAuth.App/BaseApp/Resources/Request/AddOrUpdateResReq.cs b/OpenAuth.App/BaseApp/Resources/Request/AddOrUpdateResReq.cs new file mode 100644 index 0000000..fbf59da --- /dev/null +++ b/OpenAuth.App/BaseApp/Resources/Request/AddOrUpdateResReq.cs @@ -0,0 +1,64 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a CodeSmith Template. +// +// DO NOT MODIFY contents of this file. Changes to this +// file will be lost if the code is regenerated. +// Author:Yubao Li +// +//------------------------------------------------------------------------------ + + namespace OpenAuth.Repository.Domain +{ + /// + /// 资源表 + /// + public class AddOrUpdateResReq + { + public string Id { get; set; } + /// + /// 节点语义ID + /// + public string CascadeId { get; set; } + /// + /// 名称 + /// + public string Name { get; set; } + /// + /// 排序号 + /// + public int SortNo { get; set; } + /// + /// 描述 + /// + public string Description { get; set; } + /// + /// 父节点名称 + /// + public string ParentName { get; set; } + /// + /// 父节点流ID + /// + public string ParentId { get; set; } + /// + /// 资源所属应用ID + /// + public string AppId { get; set; } + /// + /// 所属应用名称 + /// + public string AppName { get; set; } + /// + /// 分类名称 + /// + public string TypeName { get; set; } + /// + /// 分类ID + /// + public string TypeId { get; set; } + /// + /// 是否可用 + /// + public bool Disable { get; set; } + } +} \ No newline at end of file diff --git a/OpenAuth.App/BaseApp/Resources/Request/QueryResourcesReq.cs b/OpenAuth.App/BaseApp/Resources/Request/QueryResourcesReq.cs new file mode 100644 index 0000000..f6c3a9b --- /dev/null +++ b/OpenAuth.App/BaseApp/Resources/Request/QueryResourcesReq.cs @@ -0,0 +1,11 @@ +namespace OpenAuth.App.Request +{ + public class QueryResourcesReq : PageReq + { + /// + /// TypeID + /// + public string appId { get; set; } + + } +} diff --git a/OpenAuth.App/BaseApp/Resources/ResourceApp.cs b/OpenAuth.App/BaseApp/Resources/ResourceApp.cs new file mode 100644 index 0000000..8b6ccc3 --- /dev/null +++ b/OpenAuth.App/BaseApp/Resources/ResourceApp.cs @@ -0,0 +1,74 @@ +using System; +using System.Collections.Generic; +using Infrastructure; +using OpenAuth.App.Base; +using OpenAuth.App.BaseApp.Base; +using OpenAuth.App.Interface; +using OpenAuth.Repository; +using OpenAuth.Repository.Domain; +using SqlSugar; + +namespace OpenAuth.App +{ + /// + /// 分类管理 + /// + public class ResourceApp : SqlSugarBaseApp + { + private RevelanceManagerApp _revelanceApp; + + public ResourceApp( + ISugarUnitOfWork unitWork, + ISimpleClient repository, + RevelanceManagerApp app, + IAuth auth) : base(unitWork, repository, auth) + { + _revelanceApp = app; + } + public void Add(AddOrUpdateResReq resource) + { + var obj = resource.MapTo(); + //先注释,后边再看具体原因 + //CaculateCascade(obj); + obj.CreateTime = DateTime.Now; + var user = _auth.GetCurrentUser().User; + obj.CreateUserId = user.Id; + obj.CreateUserName = user.Name; + obj.Id = Guid.NewGuid().ToString(); + Repository.Insert(obj); + } + + public void Update(AddOrUpdateResReq obj) + { + var user = _auth.GetCurrentUser().User; + base.Repository.Update(u => new SysResource + { + Name = obj.Name, + Disable = obj.Disable, + CascadeId = obj.CascadeId, + AppId = obj.AppId, + AppName = obj.AppName, + ParentId = obj.ParentId, + ParentName = obj.ParentName, + TypeId = obj.TypeId, + TypeName = obj.TypeName, + Description = obj.Description, + UpdateTime = DateTime.Now, + UpdateUserId = user.Id, + UpdateUserName = user.Name + //todo:要修改的字段赋值 + }, u => u.Id == obj.Id); + } + + public IEnumerable LoadForRole(string appId, string roleId) + { + var elementIds = _revelanceApp.Get(Define.ROLERESOURCE, true, roleId); + return base.Repository.GetList(u => elementIds.Contains(u.Id) && (appId == null || appId == "" || u.AppId == appId)); + } + + public void Delete(string[] ids) + { + Repository.DeleteByIds(ids); + } + } +} \ No newline at end of file diff --git a/OpenAuth.App/BaseApp/RoleManager/Request/ModuleElementWithRole.cs b/OpenAuth.App/BaseApp/RoleManager/Request/ModuleElementWithRole.cs new file mode 100644 index 0000000..59b2feb --- /dev/null +++ b/OpenAuth.App/BaseApp/RoleManager/Request/ModuleElementWithRole.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace OpenAuth.App.Request +{ + public class ModuleElementWithRole + { + public long RoleId { get; set; } + public List ModuleIds { get; set; } + public List ElementIds { get; set; } + } +} diff --git a/OpenAuth.App/BaseApp/RoleManager/Request/QueryRoleListReq.cs b/OpenAuth.App/BaseApp/RoleManager/Request/QueryRoleListReq.cs new file mode 100644 index 0000000..75e94fc --- /dev/null +++ b/OpenAuth.App/BaseApp/RoleManager/Request/QueryRoleListReq.cs @@ -0,0 +1,6 @@ +namespace OpenAuth.App.Request +{ + public class QueryRoleListReq : PageReq + { + } +} diff --git a/OpenAuth.App/BaseApp/RoleManager/Response/RoleView.cs b/OpenAuth.App/BaseApp/RoleManager/Response/RoleView.cs new file mode 100644 index 0000000..0d1ac7e --- /dev/null +++ b/OpenAuth.App/BaseApp/RoleManager/Response/RoleView.cs @@ -0,0 +1,60 @@ +// *********************************************************************** +// Assembly : OpenAuth.App +// Author : yubaolee +// Created : 11-29-2015 +// +// Last Modified By : yubaolee +// Last Modified On : 11-29-2015 +// *********************************************************************** +// +// Copyright (c) www.cnblogs.com/yubaolee. All rights reserved. +// +// 角色模型视图 +// *********************************************************************** + +using Infrastructure; +using OpenAuth.Repository.Domain; + +namespace OpenAuth.App.Response +{ + public partial class RoleView + { + /// + /// 用户ID + /// + /// + public long Id { get; set; } + + /// + /// 名称 + /// + /// + public string Name { get; set; } + + /// + /// 当前状态 + /// + public int Status { get; set; } + /// + /// 角色类型 + /// + public int Type { get; set; } + + + /// + ///是否属于某用户 + /// + public bool Checked { get; set; } + + public static implicit operator RoleView(SysRole role) + { + return role.MapTo(); + } + + public static implicit operator SysRole(RoleView rolevm) + { + return rolevm.MapTo(); + } + + } +} diff --git a/OpenAuth.App/BaseApp/RoleManager/RoleApp.cs b/OpenAuth.App/BaseApp/RoleManager/RoleApp.cs new file mode 100644 index 0000000..62fd41c --- /dev/null +++ b/OpenAuth.App/BaseApp/RoleManager/RoleApp.cs @@ -0,0 +1,179 @@ +using OpenAuth.App.Interface; +using OpenAuth.App.Response; +using OpenAuth.Repository.Domain; +using Infrastructure; +using OpenAuth.App.Request; +using OpenAuth.Repository; +using SqlSugar; +using OpenAuth.App.Base; +using OpenAuth.App.BaseApp.Base; +using OpenAuth.App.BasicQueryService; + +namespace OpenAuth.App +{ + public class RoleApp : SqlSugarBaseApp + { + UserManager userManager; + public RoleApp( + ISugarUnitOfWork unitWork, + ISimpleClient repository, + IAuth auth, + UserManager userManager + ) : base(unitWork, repository, auth) + { + this.userManager = userManager; + } + + #region 查询 + /// + /// 查询所有角色分页 + /// + public async Task>>> LoadAllPage(QueryRoleListReq request) + { + RefAsync totalCount = 0; + var result = new PageInfo(); + var roles = await base.Repository.AsQueryable() + .WhereIF(!string.IsNullOrEmpty(request.key), r => r.Name.Contains(request.key)) + .OrderByDescending(r => r.CreateTime) + .ToPageListAsync(request.page, request.limit, totalCount); + + return new Response>> + { + Result = new PageInfo> + { + Items = roles, + Total = totalCount + } + }; + } + + /// + /// 登录用户可访问的角色分页 + /// + /// + /// + public async Task>>> LoadPage(QueryRoleListReq request) + { + long userId = long.Parse(_auth.GetUserId()); + + RefAsync totalCount = 0; + var result = new PageInfo(); + var roles = await userManager.UserRoles(userId) + .WhereIF(!string.IsNullOrEmpty(request.key), (u, r) => r.Name.Contains(request.key)) + .OrderByDescending((u, r) => r.CreateTime) + .Select((u, r) => r) + .ToPageListAsync(request.page, request.limit, totalCount); + + return new Response>> + { + Result = new PageInfo> + { + Items = roles, + Total = totalCount + } + }; + } + + public async Task> UserRoles(long userId) + { + return await base.Repository.AsSugarClient().Queryable() + .Where(ur => ur.UserId == userId) + .Select(ur => ur.RoleId).ToListAsync(); + } + #endregion + + + + public SysRole Get(long id) + { + return Repository.GetById(id); + } + + + + /// + /// 添加角色 + /// + public async Task> Add(RoleView obj) + { + SysRole role = obj; + role.CreateTime = DateTime.Now; + role.Id = Yitter.IdGenerator.YitIdHelper.NextId(); + var flag = await Repository.InsertAsync(role); + + return new Response + { + Result = flag, + Message = flag == true ? "success" : "error" + }; + } + + + + /// + /// 删除角色时,需要删除角色对应的权限 + /// + /// + public async Task> Delete(long[] ids) + { + using (var uwo = UnitWork.CreateContext()) + { + await uwo.SysRoleModule.DeleteAsync(a => ids.Contains(a.RoleId)); + await uwo.SysRoleElement.DeleteAsync(a => ids.Contains(a.RoleId)); + await uwo.Role.DeleteAsync(u => ids.Contains(u.Id)); + var flag = uwo.Commit(); + + return new Response + { + Result = flag, + Message = flag == true ? "success" : "error" + }; + } + } + + /// + /// 更新角色属性 + /// + /// + public async Task> Update(RoleView obj) + { + SysRole role = obj; + + bool flag = await base.Repository.UpdateAsync(u => new SysRole + { + Name = role.Name, + Status = role.Status + }, u => u.Id == obj.Id); + + return new Response + { + Result = flag, + Message = flag == true ? "success" : "error" + }; + } + + /// + /// 为角色分配菜单和按钮 + /// + /// + /// + public async Task> AssignModule(ModuleElementWithRole model) + { + using (var uwo = UnitWork.CreateContext()) + { + await uwo.SysRoleModule.DeleteAsync(a => a.RoleId == model.RoleId); + await uwo.SysRoleElement.DeleteAsync(a => a.RoleId == model.RoleId); + await uwo.SysRoleModule.InsertRangeAsync(model.ModuleIds.Select(a => new SysRoleModule { RoleId = model.RoleId, ModuleId = a }).ToList()); + await uwo.SysRoleElement.InsertRangeAsync(model.ElementIds.Select(a => new SysRoleElement { RoleId = model.RoleId, ElementId = a }).ToList()); + + var flag = uwo.Commit(); + return new Response + { + Result = flag, + Message = flag == true ? "success" : "error" + }; + } + } + + } +} \ No newline at end of file diff --git a/OpenAuth.App/BaseApp/SSO/LocalAuth.cs b/OpenAuth.App/BaseApp/SSO/LocalAuth.cs new file mode 100644 index 0000000..1c61c08 --- /dev/null +++ b/OpenAuth.App/BaseApp/SSO/LocalAuth.cs @@ -0,0 +1,242 @@ +using Infrastructure.Cache; +using Microsoft.AspNetCore.Http; +using OpenAuth.App.Interface; +using System; +using Infrastructure; +using Microsoft.Extensions.Options; +using OpenAuth.Repository.Domain; + +namespace OpenAuth.App.SSO +{ + /// + /// 使用本地登录。这个注入IAuth时,只需要OpenAuth.Mvc一个项目即可,无需webapi的支持 + /// + public class LocalAuth : IAuth + { + private IHttpContextAccessor _httpContextAccessor; + private IOptions _appConfiguration; + private SysLogApp _logApp; + + private AuthContextFactory _app; + private LoginParse _loginParse; + private ICacheContext _cacheContext; + + public LocalAuth(IHttpContextAccessor httpContextAccessor + , AuthContextFactory app + , LoginParse loginParse + , ICacheContext cacheContext, IOptions appConfiguration, SysLogApp logApp) + { + _httpContextAccessor = httpContextAccessor; + _app = app; + _loginParse = loginParse; + _cacheContext = cacheContext; + _appConfiguration = appConfiguration; + _logApp = logApp; + } + + /// + /// 如果是Identity,则返回信息为用户账号 + /// + /// + private string GetToken() + { + if (_appConfiguration.Value.IsIdentityAuth) + { + return _httpContextAccessor.HttpContext.User.Identity.Name; + } + string token = _httpContextAccessor.HttpContext.Request.Query[Define.TOKEN_NAME]; + if (!String.IsNullOrEmpty(token)) return token; + + token = _httpContextAccessor.HttpContext.Request.Headers[Define.TOKEN_NAME]; + if (!String.IsNullOrEmpty(token)) return token; + + var cookie = _httpContextAccessor.HttpContext.Request.Cookies[Define.TOKEN_NAME]; + return cookie ?? String.Empty; + } + + public void CoverToken(string account, string name) + { + var user = _cacheContext.Get(GetToken()); + user.Name = name; + user.Account = account; + _cacheContext.Set(user.Token, user, DateTime.Now.AddDays(10)); + } + + public bool CheckLogin(string token = "", string otherInfo = "") + { + if (_appConfiguration.Value.IsIdentityAuth) + { + return (!string.IsNullOrEmpty(_httpContextAccessor.HttpContext.User.Identity.Name)); + } + + if (string.IsNullOrEmpty(token)) + { + token = GetToken(); + } + + if (string.IsNullOrEmpty(token)) + { + return false; + } + + try + { + var result = _cacheContext.Get(token) != null; + return result; + } + catch (Exception ex) + { + throw ex; + } + } + + /// + /// 获取当前登录的用户信息 + /// 通过URL中的Token参数或Cookie中的Token + /// + /// The account. + /// LoginUserVM. + public AuthStrategyContext GetCurrentUser() + { + if (_appConfiguration.Value.IsIdentityAuth) + { + return _app.GetAuthStrategyContext(GetToken()); + } + AuthStrategyContext context = null; + var user = _cacheContext.Get(GetToken()); + if (user != null) + { + context = _app.GetAuthStrategyContext(user.Account); + } + return context; + } + + /// + /// 获取当前登录的用户名 + /// 通过URL中的Token参数或Cookie中的Token + /// + /// The account. + /// System.String. + public string GetUserName(string otherInfo = "") + { + if (_appConfiguration.Value.IsIdentityAuth) + { + return _httpContextAccessor.HttpContext.User.Identity.Name; + } + + var user = _cacheContext.Get(GetToken()); + if (user != null) + { + return user.Account; + } + + return ""; + } + + public string GetUserNickName(string otherInfo = "") + { + if (_appConfiguration.Value.IsIdentityAuth) + { + return _httpContextAccessor.HttpContext.User.Identity.Name; + } + + var user = _cacheContext.Get(GetToken()); + if (user != null) + { + return user.Name; + } + + return ""; + } + + /// + /// 获取 UserId + /// + /// + /// + public string GetUserId(string otherInfo = "") + { + if (_appConfiguration.Value.IsIdentityAuth) + { + return _httpContextAccessor.HttpContext.User.Identity.Name; + } + + var user = _cacheContext.Get(GetToken()); + if (user != null) + { + return user.UserId; + } + + return ""; + } + + /// + /// 登录接口 + /// + /// 应用程序key. + /// 用户名 + /// 密码 + /// System.String. + public Response Login(string appKey, string username, string pwd) + { + //throw new Exception("hahah"); + if (_appConfiguration.Value.IsIdentityAuth) + { + return new Response + { + Code = 500, + Message = "接口启动了OAuth认证,暂时不能使用该方式登录" + }; + } + + var result = _loginParse.Do(new PassportLoginRequest + { + AppKey = appKey, + Account = username, + Password = pwd + }); + + var log = new SysLog + { + Content = $"用户登录,结果:{result.Message}", + Result = result.Code == 200 ? 0 : 1, + CreateId = -1, + CreateName = username, + TypeName = "登录日志" + }; + _logApp.Add(log); + + return result; + } + + /// + /// 注销,如果是Identity登录,需要在controller处理注销逻辑 + /// + public bool Logout() + { + var token = GetToken(); + if (String.IsNullOrEmpty(token)) return true; + + try + { + _cacheContext.Remove(token); + return true; + } + catch + { + return false; + } + + + } + + /// + /// 是否是超级管理员 + /// + /// + public bool IsSystem() + { + return Define.SYSTEM_USERNAME == GetUserName(); + } + } +} \ No newline at end of file diff --git a/OpenAuth.App/BaseApp/SSO/LoginParse.cs b/OpenAuth.App/BaseApp/SSO/LoginParse.cs new file mode 100644 index 0000000..5f632e7 --- /dev/null +++ b/OpenAuth.App/BaseApp/SSO/LoginParse.cs @@ -0,0 +1,104 @@ +using System; +using Infrastructure; +using Infrastructure.Cache; +using Infrastructure.Helpers; +using Infrastructure.Utilities; +using Microsoft.Extensions.Configuration; +using OpenAuth.Repository.Domain; +using SqlSugar; + +namespace OpenAuth.App.SSO +{ + public class LoginParse + { + //这个地方使用IRepository 而不使用UserManagerApp是防止循环依赖 + public ISimpleClient _app; + private ICacheContext _cacheContext; + private IConfiguration configuration; + + + public LoginParse(ICacheContext cacheContext, ISimpleClient userApp, IConfiguration configuration) + { + _cacheContext = cacheContext; + _app = userApp; + this.configuration = configuration; + } + + public Response Do(PassportLoginRequest model) + { + var result = new Response(); + try + { + model.Trim(); + //todo:如果需要判定应用,可以取消该注释 + // var appInfo = _appInfoService.GetByAppKey(model.AppKey); + // if (appInfo == null) + // { + // throw new Exception("应用不存在"); + // } + //获取用户信息 + SysUser userInfo = null; + if (model.Account == Define.SYSTEM_USERNAME) + { + userInfo = new SysUser + { + Id = -1, + Account = Define.SYSTEM_USERNAME, + Name = "超级管理员", + Password = Md5Helper.Encrypt(DESEncrypt.Encrypt(Md5Helper.Hash(configuration.GetSection("AppSetting:SYSTEM_USERPWD").Value), Define.SYSTEM_SECRETKEY).ToLower(), 32).ToLower(), + //Password = Define.SYSTEM_USERPWD, + Secretkey = Define.SYSTEM_SECRETKEY + }; + } + else + { + userInfo = _app.GetFirst(u => u.Account == model.Account); + } + + if (userInfo == null) + { + throw new Exception("用户不存在"); + } + + string dbPwd = Md5Helper.Encrypt(DESEncrypt.Encrypt(model.Password, userInfo.Secretkey).ToLower(), 32).ToLower(); + if (userInfo.Password != dbPwd) + { + throw new Exception("密码错误"); + } + + if (userInfo.Status != 0) + { + throw new Exception("账号状态异常,可能已停用"); + } + + var currentSession = new UserAuthSession + { + UserId = userInfo.Id.ToString(), + Account = model.Account, + Name = userInfo.Name, + Token = Guid.NewGuid().ToString().GetHashCode().ToString("x"), + AppKey = model.AppKey, + CreateTime = DateTime.Now + // , IpAddress = HttpContext.Current.Request.UserHostAddress + }; + + //创建Session + _cacheContext.Set(currentSession.Token, currentSession, DateTime.Now.AddDays(10)); + + result.Code = 200; + result.Result = new LoginResult + { + Token = currentSession.Token + }; + + } + catch (Exception ex) + { + result.Code = 500; + result.Message = ex.Message; + } + + return result; + } + } +} \ No newline at end of file diff --git a/OpenAuth.App/BaseApp/SSO/LoginResult.cs b/OpenAuth.App/BaseApp/SSO/LoginResult.cs new file mode 100644 index 0000000..2d054ed --- /dev/null +++ b/OpenAuth.App/BaseApp/SSO/LoginResult.cs @@ -0,0 +1,17 @@ +using Infrastructure; + +namespace OpenAuth.App.SSO +{ + //public class LoginResult : Response + //{ + + // public string ReturnUrl; + // public string Token; + //} + + public class LoginResult + { + public string ReturnUrl; + public string Token; + } +} \ No newline at end of file diff --git a/OpenAuth.App/BaseApp/SSO/PassportLoginRequest.cs b/OpenAuth.App/BaseApp/SSO/PassportLoginRequest.cs new file mode 100644 index 0000000..a56dabd --- /dev/null +++ b/OpenAuth.App/BaseApp/SSO/PassportLoginRequest.cs @@ -0,0 +1,37 @@ +using System; +using System.ComponentModel.DataAnnotations; + +namespace OpenAuth.App.SSO +{ + + public class PassportLoginRequest + { + /// System + public string Account { get; set; } + + /// e10adc3949ba59abbe56e057f20f883e + public string Password { get; set; } + + /// + /// 应用的AppSecrect,目前没判定可以随便填一个。如果需要判定请根据注释调整LoginParse.Do方法 + /// + /// openauth + public string AppKey { get; set; } + + public void Trim() + { + if (string.IsNullOrEmpty(Account)) + { + throw new Exception("用户名不能为空"); + } + + if (string.IsNullOrEmpty(Password)) + { + throw new Exception("密码不能为空"); + } + Account = Account.Trim(); + Password = Password.Trim(); + if (!string.IsNullOrEmpty(AppKey)) AppKey = AppKey.Trim(); + } + } +} \ No newline at end of file diff --git a/OpenAuth.App/BaseApp/SSO/UserAuthSession.cs b/OpenAuth.App/BaseApp/SSO/UserAuthSession.cs new file mode 100644 index 0000000..10e348a --- /dev/null +++ b/OpenAuth.App/BaseApp/SSO/UserAuthSession.cs @@ -0,0 +1,30 @@ +using System; + +namespace OpenAuth.App.SSO +{ + [Serializable] + public class UserAuthSession + { + public string Token { get; set; } + + public string AppKey { get; set; } + /// + /// 用户主键 + /// + public string UserId { get; set; } + + /// + /// 用户账号 + /// + public string Account { get; set; } + + /// + /// 用户名 + /// + public string Name { get; set; } + + public string IpAddress { get; set; } + + public DateTime CreateTime { get; set; } + } +} \ No newline at end of file diff --git a/OpenAuth.App/BaseApp/SysAppFiles/SysAppFilesApp.cs b/OpenAuth.App/BaseApp/SysAppFiles/SysAppFilesApp.cs new file mode 100644 index 0000000..a0820be --- /dev/null +++ b/OpenAuth.App/BaseApp/SysAppFiles/SysAppFilesApp.cs @@ -0,0 +1,105 @@ +using Infrastructure; +using Newtonsoft.Json.Linq; +using OpenAuth.App.Base; +using OpenAuth.App.Interface; +using OpenAuth.Repository; +using OpenAuth.Repository.Domain; +using SqlSugar; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using OpenAuth.App.BaseApp.Base; + +namespace OpenAuth.App +{ + public class SysAppFilesApp : SqlSugarBaseApp + { + public SysAppFilesApp(ISugarUnitOfWork unitWork, ISimpleClient repository, IAuth auth) : base(unitWork, repository, auth) + { + + } + #region app文件 + + /// + /// 添加App文件 + /// + /// + /// + public Response AddAppFiles(SysAppFiles model) + { + var response = new Response(); + + var user = _auth.GetCurrentUser().User; + + model.Id = Guid.NewGuid().ToString(); + model.createtime = DateTime.Now; + model.createuser = user.Account; + + var flag = base.Repository.Insert(model); + if (flag) + { + response.Result = true; + response.Message = "添加成功"; + } + else + { + response.Result = false; + response.Message = "添加失败"; + } + return response; + } + + /// + /// 获取下载更新的文件路径 + /// + /// + /// + public object GetUpdateFiles(string project) + { + + var model = base.Repository.AsQueryable().Where(c => c.project_name == project).OrderBy(c => c.createtime, OrderByType.Desc).Select(c => new + { + c.Id, + edition = SqlFunc.ToInt32(c.edition), + c.description, + c.filepath, + c.createtime, + c.createuser, + c.must_update, + c.project_name, + }).First(); + + if (model != null) + { + JObject obj = new JObject(); + + string filename = model.filepath.Substring(model.filepath.LastIndexOf("\\") + 1); + + obj.Add("Id", model.Id); + obj.Add("edition", model.edition); + obj.Add("description", model.description); + obj.Add("filepath", model.filepath); + obj.Add("filename", filename); + obj.Add("createtime", model.createtime); + obj.Add("createuser", model.createuser); + obj.Add("must_update", model.must_update); + obj.Add("project_name", model.project_name); + + return obj; + } + else + { + return null; + } + } + + + public SysAppFiles GetByProject(string project) + { + return base.Repository.AsQueryable().Where(c => c.project_name == project).OrderBy(c => c.createtime, SqlSugar.OrderByType.Desc).First(); + } + #endregion + } +} diff --git a/OpenAuth.App/BaseApp/SysDataItem/SysDataItemApp.cs b/OpenAuth.App/BaseApp/SysDataItem/SysDataItemApp.cs new file mode 100644 index 0000000..e4a58cd --- /dev/null +++ b/OpenAuth.App/BaseApp/SysDataItem/SysDataItemApp.cs @@ -0,0 +1,84 @@ +using OpenAuth.App.Base; +using OpenAuth.Repository.Domain; +using OpenAuth.Repository; +using SqlSugar; +using OpenAuth.App.Interface; +using Infrastructure; +using OpenAuth.App.BaseApp.Base; + +namespace OpenAuth.App +{ + public class SysDataItemApp : SqlSugarBaseApp + { + public SysDataItemApp(ISugarUnitOfWork unitWork, ISimpleClient repository, IAuth auth) : base(unitWork, repository, auth) + { + } + + public async Task> Load(string keyword, bool enabledMark = true) + { + var list = await base.Repository.AsQueryable() + .Where(a => a.DeleteMark == 0) + .WhereIF(enabledMark == true, a => a.EnabledMark == 1) + .WhereIF(!string.IsNullOrEmpty(keyword), a => a.ItemName.Contains(keyword) || a.ItemCode.Contains(keyword)) + .ToListAsync(); + + return list; + } + + public async Task GetDataItem(string code) + { + var mdeol = await base.Repository.GetFirstAsync(a => a.DeleteMark == 0 && a.ItemCode == code); + return mdeol; + } + + public async Task> SaveClassifyEntity(SysDataItem entity) + { + var flag = false; + + if (string.IsNullOrEmpty(entity.ItemId)) + { + entity.ItemId = Guid.NewGuid().ToString(); + entity.CreateDate = DateTime.Now; + if (string.IsNullOrEmpty(entity.ParentId)) + { + entity.ParentId = "0"; + } + entity.DeleteMark = 0; + entity.CreateUserId = base._auth.GetUserId(); + entity.CreateUserName = base._auth.GetUserName(); + flag = await base.Repository.InsertAsync(entity); + } + else + { + entity.ModifyDate = DateTime.Now; + entity.ModifyUserId = base._auth.GetUserId(); + entity.ModifyUserName = base._auth.GetUserName(); + flag = await base.Repository.UpdateAsync(entity); + } + + return new Response + { + Result = flag, + Message = flag == true ? "success" : "error" + }; + } + + public async Task> DeleteClassify(string keyValue) + { + using (var uow = base.UnitWork.CreateContext()) + { + await uow.SysDataItem.DeleteByIdAsync(keyValue); + await uow.SysDataItemDetail.DeleteAsync(a => a.ItemId == keyValue); + + var falg = uow.Commit(); + return new Response + { + Result = falg, + Message = (falg == true ? "success" : "error") + }; + } + } + + + } +} diff --git a/OpenAuth.App/BaseApp/SysDataItemDetail/SysDataItemDetailApp.cs b/OpenAuth.App/BaseApp/SysDataItemDetail/SysDataItemDetailApp.cs new file mode 100644 index 0000000..16d790c --- /dev/null +++ b/OpenAuth.App/BaseApp/SysDataItemDetail/SysDataItemDetailApp.cs @@ -0,0 +1,109 @@ +using Infrastructure; +using Infrastructure.Extensions; +using OpenAuth.App.Base; +using OpenAuth.App.Interface; +using OpenAuth.Repository; +using OpenAuth.Repository.Domain; +using SqlSugar; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using OpenAuth.App.BaseApp.Base; +using System.Web; + +namespace OpenAuth.App +{ + public class SysDataItemDetailApp : SqlSugarBaseApp + { + ISqlSugarClient client; + public SysDataItemDetailApp(ISugarUnitOfWork unitWork, ISimpleClient repository, IAuth auth) : base(unitWork, repository, auth) + { + client = base.Repository.AsSugarClient(); + } + + public async Task> Load(string code, string keyWord) + { + var list = await client.Queryable() + .LeftJoin((a, b) => a.ItemId == b.ItemId) + .Where((a, b) => b.ItemCode == code) + .WhereIF(!string.IsNullOrEmpty(keyWord), (a => a.ItemName.Contains(keyWord) || a.ItemCode.Contains(keyWord))) + .OrderBy(a => a.SortCode) + .Select(a => a) + .ToListAsync(); + + return list; + } + + /// + /// 获取字段数据(有父级编号) + /// + /// 编码 + /// 筛选条件 + /// 父级编号 + /// + public async Task> LoadWithPid(string code, string keyWord,string pid) + { + var list = await client.Queryable() + .LeftJoin((a, b) => a.ItemId == b.ItemId) + .Where((a, b) => b.ItemCode == code) + .WhereIF(!string.IsNullOrEmpty(keyWord), (a => a.ItemName.Contains(keyWord) || a.ItemCode.Contains(keyWord))) + .WhereIF(!string.IsNullOrEmpty(pid),a=>a.ParentId==pid) + .OrderBy(a => a.SortCode) + .Select(a => a) + .ToListAsync(); + + return list; + } + + public async Task> SaveDetailEntity(SysDataItemDetail entity) + { + var flag = false; + + var dataItem = await client.Queryable() + .Where(a => a.DeleteMark == 0 && a.ItemId == entity.ItemId).FirstAsync(); + ; + if (dataItem.IsTree != 1 || string.IsNullOrEmpty(dataItem.ParentId)) + { + entity.ParentId = "0"; + } + + entity.QuickQuery = StringExtension.ConvertPinYin(entity.ItemName).ToUpper(); + entity.SimpleSpelling = StringExtension.PinYin(entity.ItemName); + if (string.IsNullOrEmpty(entity.ItemDetailId)) + { + entity.ItemDetailId = Guid.NewGuid().ToString(); + entity.CreateDate = DateTime.Now; + if (string.IsNullOrEmpty(entity.ParentId)) + { + entity.ParentId = "0"; + } + entity.DeleteMark = 0; + flag = await base.Repository.InsertAsync(entity); + } + else + { + entity.ItemDetailId = entity.ItemDetailId; + entity.ModifyDate = DateTime.Now; + flag = await base.Repository.UpdateAsync(entity); + } + + return new Response + { + Result = flag, + Message = flag == true ? "success" : "error" + }; + } + + public async Task> Delete(string id) + { + var flag = await base.Repository.DeleteByIdAsync(id); + return new Response + { + Result = flag, + Message = flag == true ? "success" : "error" + }; + } + } +} diff --git a/OpenAuth.App/BaseApp/SysDatabaseLink/Response/TreeModel.cs b/OpenAuth.App/BaseApp/SysDatabaseLink/Response/TreeModel.cs new file mode 100644 index 0000000..789893e --- /dev/null +++ b/OpenAuth.App/BaseApp/SysDatabaseLink/Response/TreeModel.cs @@ -0,0 +1,71 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace OpenAuth.App.SysDatabaseLink.Response +{ + public class TreeModel + { + // + // 摘要: + // 节点id + public string id { get; set; } + + // + // 摘要: + // 节点显示数据 + public string text { get; set; } + + // + // 摘要: + // 节点提示 + public string title { get; set; } + + // + // 摘要: + // 节点数值 + public string value { get; set; } + + // + // 摘要: + // 显示图标 + public string icon { get; set; } + + // + // 摘要: + // 是否显示勾选框 + public bool showcheck { get; set; } + + // + // 摘要: + // 是否被勾选0 for unchecked, 1 for partial checked, 2 for checked + public int checkstate { get; set; } + + // + // 摘要: + // 是否有子节点 + public bool hasChildren { get; set; } + + // + // 摘要: + // 是否展开 + public bool isexpand { get; set; } + + // + // 摘要: + // 子节点是否已经加载完成了 + public bool complete { get; set; } + + // + // 摘要: + // 子节点列表数据 + public List ChildNodes { get; set; } + + // + // 摘要: + // 父级节点ID + public string parentId { get; set; } + } +} diff --git a/OpenAuth.App/BaseApp/SysDatabaseLink/SysDatabaseLinkApp.cs b/OpenAuth.App/BaseApp/SysDatabaseLink/SysDatabaseLinkApp.cs new file mode 100644 index 0000000..b837ffd --- /dev/null +++ b/OpenAuth.App/BaseApp/SysDatabaseLink/SysDatabaseLinkApp.cs @@ -0,0 +1,241 @@ +using Infrastructure; +using Infrastructure.Extensions; +using OpenAuth.App.Base; +using OpenAuth.App.Interface; +using OpenAuth.App.SysDatabaseLink.Response; +using OpenAuth.Repository; +using OpenAuth.Repository.Core; +using OpenAuth.Repository.Domain; +using SqlSugar; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using OpenAuth.App.BaseApp.Base; + +namespace OpenAuth.App.SysDatabaseLink +{ + public class SysDatabaseLinkApp : SqlSugarBaseApp + { + public SysDatabaseLinkApp(ISugarUnitOfWork unitWork, ISimpleClient repository, IAuth auth) : base(unitWork, repository, auth) + { + + } + + /// + /// 获取树形数据 + /// + /// + public async Task>> LoadDataBaseLinkTree() + { + var list = await this.Repository.AsQueryable().Where(r => r.DeleteMark == 0).ToListAsync(); + List treelist = new List(); + Dictionary> dic = new Dictionary>(); + + TreeModel mynode = new TreeModel(); + mynode.id = "hcsystemdb"; + mynode.text = "本地数据库"; + mynode.value = "hcsystemdb"; + mynode.complete = true; + mynode.hasChildren = false; + treelist.Add(mynode); + foreach (var item in list) + { + TreeModel node = new TreeModel(); + node.id = item.DBName; + node.text = item.DBAlias; + node.value = item.DBName; + node.complete = true; + node.hasChildren = false; + + if (!dic.ContainsKey(item.ServerAddress)) + { + TreeModel pnode = new TreeModel(); + pnode.id = item.ServerAddress; + pnode.text = item.ServerAddress; + pnode.value = "hcServerAddress"; + pnode.isexpand = true; + pnode.complete = true; + pnode.hasChildren = true; + pnode.ChildNodes = new List(); + treelist.Add(pnode); + dic.Add(item.ServerAddress, pnode.ChildNodes); + } + dic[item.ServerAddress].Add(node); + } + return new Response> + { + Result = treelist + }; + } + + /// + /// 分页获取列表数据 + /// + /// + /// + /// + /// + public async Task>>> LoadDataBaseInfo(string keyword, int pageindex, int pagesize) + { + //定义且实例化分页数据 + RefAsync totalCount = 0; + + //数据查询并返回 + var info = await this.Repository.AsQueryable() + .WhereIF(!string.IsNullOrEmpty(keyword), t => t.DBName.Contains(keyword)) + .OrderBy(r=>r.DBName) + .ToPageListAsync(pageindex, pagesize, totalCount); + + return new Response>> + { + Result = new PageInfo> + { + Items = info, + Total = totalCount + } + }; + } + + /// + /// 获取实体数据 + /// + /// 数据库连接主键 + /// + public async Task> GetDataBaseForm(string id) + { + var info = await this.Repository.GetByIdAsync(id); + return new Response + { + Result = info + }; + } + /// + /// 添加数据库连接(新增、修改) + /// + /// 主键值 + /// 数据库实体 + /// + public async Task> SaveBaseLinkEntity(string keyValue, Repository.Domain.SysDatabaseLink baseLink) + { + /*测试数据库连接串"******";*/ + if (!string.IsNullOrEmpty(keyValue) && baseLink.DBConnection == "******") + { + baseLink.DBConnection = null;// 不更新连接串地址 + } + else + { + var testRes = TestConnection(baseLink.DBConnection, baseLink.DBType); + if (testRes != "success") + { + return new Response + { + Result = false, + Message = "字符串连接失败" + }; + } + } + var user = _auth.GetCurrentUser().User; + using (var uwo = base.UnitWork.CreateContext()) + { + if (!string.IsNullOrEmpty(keyValue)) + { + + baseLink.DatabaseLinkId = keyValue; + baseLink.ModifyDate = DateTime.Now; + baseLink.ModifyUserId = user.Id.ToString(); + baseLink.ModifyUsername = user.Name; + await uwo.SysDatabaseLink.UpdateAsync(baseLink); + } + else + { + baseLink.DatabaseLinkId = Guid.NewGuid().ToString(); + baseLink.Createdate = DateTime.Now; + baseLink.DeleteMark = 0; + baseLink.EnabledMark = 1; + baseLink.CreateUserId = user.Id.ToString(); + baseLink.CreateUsername = user.Name; + await uwo.SysDatabaseLink.InsertAsync(baseLink); + } + + var flag = uwo.Commit(); + return new Response + { + Result = flag, + Message = flag == true ? "操作成功" : "操作失败" + }; + } + } + + /// + /// 删除数据库连接 + /// + /// 主键 + /// + public async Task> DeleteBaseLink(string id) + { + using (var uwo = base.UnitWork.CreateContext()) + { + await uwo.SysDatabaseLink.DeleteByIdAsync(id); + var flag = uwo.Commit(); + return new Response + { + Result = flag, + Message = flag == true ? "操作成功" : "操作失败" + }; + } + } + + #region 扩展方法 + /// + /// 测试数据数据库是否能连接成功 + /// + /// 连接串 + /// 数据库类型 + public string TestConnection(string connection, string dbType) + { + try + { + this.TestLinkClient(connection, dbType).BeginTran(); + this.TestLinkClient(connection, dbType).CommitTran(); + return "success"; + } + catch (Exception ex) + { + return ex.Message; + } + } + + /// + /// 测试连接串是否正确 + /// + /// 连接串 + /// 数据库类型 + /// 主键 + /// + public async Task> TestDataBaseLink(string connection, string dbType, string id) + { + if (!string.IsNullOrEmpty(id) && connection == "******") + { + Repository.Domain.SysDatabaseLink entity = await this.Repository.GetByIdAsync(id); + if (entity != null) + { + connection = entity.DBConnection; + } + } + string res = TestConnection(connection, dbType); + var flag = false; + if (res == "success") + { + flag = true; + } + return new Response + { + Result = flag, + Message = flag == true ? "连接成功" : "连接失败" + }; + } + #endregion + } +} diff --git a/OpenAuth.App/BaseApp/SysLogs/Request/QuerySysLogListReq.cs b/OpenAuth.App/BaseApp/SysLogs/Request/QuerySysLogListReq.cs new file mode 100644 index 0000000..a3fa012 --- /dev/null +++ b/OpenAuth.App/BaseApp/SysLogs/Request/QuerySysLogListReq.cs @@ -0,0 +1,7 @@ +namespace OpenAuth.App.Request +{ + public class QuerySysLogListReq : PageReq + { + //todo:添加自己的请求字段 + } +} \ No newline at end of file diff --git a/OpenAuth.App/BaseApp/SysLogs/SysLogApp.cs b/OpenAuth.App/BaseApp/SysLogs/SysLogApp.cs new file mode 100644 index 0000000..5fa8592 --- /dev/null +++ b/OpenAuth.App/BaseApp/SysLogs/SysLogApp.cs @@ -0,0 +1,61 @@ +using System.Reflection; +using System.Threading.Tasks; +using OpenAuth.App.Base; +using OpenAuth.App.BaseApp.Base; +using OpenAuth.App.Request; +using OpenAuth.App.Response; +using OpenAuth.Repository; +using OpenAuth.Repository.Domain; +using SqlSugar; + +namespace OpenAuth.App +{ + public class SysLogApp : SqlSugarBaseApp + { + public SysLogApp(ISugarUnitOfWork unitWork, ISimpleClient repository) : base(unitWork, repository, null) + { + } + + /// + /// 加载列表 + /// + public async Task Load(QuerySysLogListReq request) + { + int totalCount = 0; + var result = new TableData(); + var objs = base.Repository.AsQueryable() + .WhereIF(!string.IsNullOrEmpty(request.key), u => u.Content.Contains(request.key)); + + result.data = await objs.OrderByDescending(u => u.CreateTime).ToPageListAsync(request.page, request.limit, totalCount); + result.count = totalCount; + + return result; + } + + public void Add(SysLog obj) + { + //程序类型取入口应用的名称,可以根据自己需要调整 + obj.Application = Assembly.GetEntryAssembly().FullName.Split(',')[0]; + obj.Id = Yitter.IdGenerator.YitIdHelper.NextId(); + Repository.Insert(obj); + } + + public void Update(SysLog obj) + { + base.Repository.Update(u => new SysLog + { + //todo:要修改的字段赋值 + }, u => u.Id == obj.Id); + } + + public SysLog Get(string id) + { + return Repository.GetById(id); + } + + public void Delete(string[] ids) + { + Repository.DeleteByIds(ids); + } + } +} \ No newline at end of file diff --git a/OpenAuth.App/BaseApp/SysMessage/Request/QuerySysMessageListReq.cs b/OpenAuth.App/BaseApp/SysMessage/Request/QuerySysMessageListReq.cs new file mode 100644 index 0000000..895c93f --- /dev/null +++ b/OpenAuth.App/BaseApp/SysMessage/Request/QuerySysMessageListReq.cs @@ -0,0 +1,10 @@ +namespace OpenAuth.App.Request +{ + public class QuerySysMessageListReq : PageReq + { + /// + /// 消息状态 0:未读;1:已读; 999:全部 + /// + public int Status { get; set; } + } +} \ No newline at end of file diff --git a/OpenAuth.App/BaseApp/SysMessage/SysMessageApp.cs b/OpenAuth.App/BaseApp/SysMessage/SysMessageApp.cs new file mode 100644 index 0000000..f3edc3f --- /dev/null +++ b/OpenAuth.App/BaseApp/SysMessage/SysMessageApp.cs @@ -0,0 +1,130 @@ +using Infrastructure; +using Microsoft.Extensions.Logging; +using OpenAuth.App.Base; +using OpenAuth.App.BaseApp.Base; +using OpenAuth.App.Interface; +using OpenAuth.App.Request; +using OpenAuth.App.Response; +using OpenAuth.Repository; +using OpenAuth.Repository.Domain; +using SqlSugar; + +namespace OpenAuth.App +{ + public class SysMessageApp : SqlSugarBaseApp + { + private readonly ILogger _logger; + + public SysMessageApp( + ISugarUnitOfWork unitWork, + ISimpleClient repository, + IAuth auth, + ILogger logger) : base(unitWork, repository, auth) + { + _logger = logger; + } + + /// + /// 加载列表 + /// + public async Task Load(QuerySysMessageListReq request) + { + var loginContext = _auth.GetCurrentUser(); + if (loginContext == null) + { + throw new CommonException("登录已过期", Define.INVALID_TOKEN); + } + + int totalCount = 0; + var result = new TableData(); + var objs = base.Repository.AsQueryable().Where(u => u.ToId == loginContext.User.Id && u.ToStatus != -1) + .WhereIF(!string.IsNullOrEmpty(request.key), u => u.Title.Contains(request.key)) + .WhereIF(request.Status != 999, u => u.ToStatus == request.Status); + + result.data = await objs.OrderByDescending(u => u.CreateTime).ToPageListAsync(request.page, request.limit, totalCount); + result.count = totalCount; + + return result; + } + + public void Add(SysMessage obj) + { + obj.Id = Yitter.IdGenerator.YitIdHelper.NextId(); + Repository.Insert(obj); + } + + /// + /// 发送指定消息给用户 + /// + /// + /// + public void SendMsgTo(long userId, string message) + { + SysUser user = null; + if (userId == -1) + { + user = new SysUser + { + Name = Define.SYSTEM_USERNAME, + Id = userId + }; + } + else + { + user = (Repository.ChangeRepository>()).GetFirst(u => u.Id == userId); + } + if (user == null) + { + _logger.LogError($"未能找到用户{userId},不能给该用户发送消息"); + return; + } + Repository.Insert(new SysMessage + { + ToId = user.Id, + ToName = user.Name, + TypeName = "系统消息", + TypeId = "SYS_MSG", + FromId = Guid.Empty.ToString(), + FromName = "系统管理员", + Content = message, + CreateTime = DateTime.Now + }); + } + + /// + /// 消息变为已读 + /// + /// + public void Read(IdRequest req) + { + Repository.Update(u => new SysMessage + { + ToStatus = 1 + }, u => u.Id == req.Id); + } + /// + /// 消息采用逻辑删除 + /// + /// + public void Del(long[] ids) + { + Repository.Update(u => new SysMessage + { + ToStatus = -1 //逻辑删除 + }, u => ids.Contains(u.Id)); + + } + + public void Delete(string[] ids) + { + Repository.DeleteByIds(ids); + } + + public SysMessage Get(string id) + { + return Repository.GetById(id); + } + + + } +} \ No newline at end of file diff --git a/OpenAuth.App/BaseApp/SysPosGroup/Request/PosGroupForm.cs b/OpenAuth.App/BaseApp/SysPosGroup/Request/PosGroupForm.cs new file mode 100644 index 0000000..b357dbe --- /dev/null +++ b/OpenAuth.App/BaseApp/SysPosGroup/Request/PosGroupForm.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace OpenAuth.App +{ + public class PosGroupForm + { + public string Name { get; set; } + } +} diff --git a/OpenAuth.App/BaseApp/SysPosGroup/SysPosGroupApp.cs b/OpenAuth.App/BaseApp/SysPosGroup/SysPosGroupApp.cs new file mode 100644 index 0000000..ff2f208 --- /dev/null +++ b/OpenAuth.App/BaseApp/SysPosGroup/SysPosGroupApp.cs @@ -0,0 +1,54 @@ +using OpenAuth.App.Base; +using OpenAuth.Repository.Domain; +using OpenAuth.Repository; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using SqlSugar; +using OpenAuth.App.Interface; +using Infrastructure; +using OpenAuth.App; +using OpenAuth.App.BaseApp.Base; +using OpenAuth.App.Request; + +namespace OpenAuth.App +{ + public class SysPosGroupApp : SqlSugarBaseApp + { + public SysPosGroupApp(ISugarUnitOfWork unitWork, ISimpleClient repository, IAuth auth) : base(unitWork, repository, auth) + { + } + + + public async Task>> Load(string name) + { + var groups = await base.Repository.AsQueryable() + .WhereIF(!string.IsNullOrEmpty(name), g => g.Name.Contains(name)) + .ToListAsync(); + + return new Response> + { + Result = groups + }; + } + + public async Task> Add(PosGroupForm pos) + { + var userId = base._auth.GetCurrentUser().User.Id; + + var model = pos.MapTo(); + model.Id = Yitter.IdGenerator.YitIdHelper.NextId(); + model.CreateId = userId; + model.CreateTime = DateTime.Now; + + var flag = await base.Repository.InsertAsync(model); + return new Response + { + Result = flag, + Message = flag == true ? "success" : "error" + }; + } + } +} diff --git a/OpenAuth.App/BaseApp/SysPosition/Request/QueryPositonListReq.cs b/OpenAuth.App/BaseApp/SysPosition/Request/QueryPositonListReq.cs new file mode 100644 index 0000000..4a42172 --- /dev/null +++ b/OpenAuth.App/BaseApp/SysPosition/Request/QueryPositonListReq.cs @@ -0,0 +1,14 @@ +using OpenAuth.App.Request; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace OpenAuth.App +{ + public class QueryPositonListReq : PageReq + { + public long GroupId { get; set; } + } +} diff --git a/OpenAuth.App/BaseApp/SysPosition/SysPositionApp.cs b/OpenAuth.App/BaseApp/SysPosition/SysPositionApp.cs new file mode 100644 index 0000000..aabd0e0 --- /dev/null +++ b/OpenAuth.App/BaseApp/SysPosition/SysPositionApp.cs @@ -0,0 +1,137 @@ +using OpenAuth.App.Base; +using OpenAuth.Repository.Domain; +using OpenAuth.Repository; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using SqlSugar; +using OpenAuth.App.Interface; +using Infrastructure; +using OpenAuth.App.BaseApp.Base; +using OpenAuth.Repository.Core; +using Oracle.ManagedDataAccess.Types; + +namespace OpenAuth.App +{ + public class SysPositionApp : SqlSugarBaseApp + { + public SysPositionApp(ISugarUnitOfWork unitWork, ISimpleClient repository, IAuth auth) : base(unitWork, repository, auth) + { + } + + public async Task> Get(long id) + { + var pos = await base.Repository.GetByIdAsync(id); + return new Response + { + Result = pos, + Message = "success" + }; + } + + public async Task>>> Load(QueryPositonListReq req) + { + RefAsync totalCount = 0; + var list = await base.Repository.AsQueryable() + .WhereIF(!string.IsNullOrEmpty(req.key), p => p.Name.Contains(req.key)) + .WhereIF(req.GroupId != 0, p => p.GroupId == req.GroupId) + .OrderBy(p => p.Id) + .ToPageListAsync(req.page, req.limit, totalCount); + + return new Response>> + { + Result = new PageInfo> + { + Items = list, + Total = totalCount + } + }; + } + + public async Task>> PositionsTree(long groupId) + { + var result = new Response>(); + + if (groupId != 0) + { + object[] posIds = (await base.Repository.AsQueryable() + .Where(a => a.GroupId == groupId) + .Select(it => it.Id).ToListAsync()).Cast().ToArray(); + + result.Result = await base.Repository.AsQueryable() + .Select() + .ToTreeAsync(a => a.Children, a => a.ParentId, 0, posIds); + } + else + { + result.Result = await base.Repository.AsQueryable() + .Select() + .ToTreeAsync(a => a.Children, a => a.ParentId, 0, a => a.Id); + } + + return result; + } + + public async Task> Add(SysPosition pos) + { + pos.Id = Yitter.IdGenerator.YitIdHelper.NextId(); + var flag = await base.Repository.InsertAsync(pos); + return new Response + { + Result = flag, + Message = flag == true ? "success" : "error" + }; + } + + public async Task> Update(SysPosition pos) + { + var flag = await base.Repository.UpdateAsync(pos); + return new Response + { + Result = flag, + Message = flag == true ? "success" : "error" + }; + } + + #region 根据部门查询职级 + public async Task>> LoadPositionByOrg(long orgid) + { + //var list = new List(); + //var org = base.Repository.ChangeRepository>().AsQueryable() + // .Where(r => r.Id == orgid).First(); + //if (org != null) + //{ + // list = await base.Repository + // .AsQueryable() + // .Where(r => r.GroupId == org.PosGroupId) + // .OrderBy(r => r.Name) + // .Select() + // .ToTreeAsync(a => a.Children, a => a.ParentId, 0, a => a.Id); + //} + //return new Response> + //{ + // Result = list + //}; + + var poses = await base.Repository.AsQueryable() + .LeftJoin((p1, p2) => p1.ParentId == p2.Id && p2.OrgId == orgid) + .Where(p1 => p1.OrgId == orgid) + .Select((p1, p2) => new TreeItemLong + { + Id = p1.Id, + ParentId = p2.Id == null ? 0 : p1.ParentId, + Name = p1.Name + }).ToTreeAsync(a => a.Children, a => a.ParentId, 0, a => a.Id); + + return new Response> + { + Result = poses + }; + + + } + #endregion + } +} diff --git a/OpenAuth.App/BaseApp/UserManager/Request/AssignUserOrgs.cs b/OpenAuth.App/BaseApp/UserManager/Request/AssignUserOrgs.cs new file mode 100644 index 0000000..d329f66 --- /dev/null +++ b/OpenAuth.App/BaseApp/UserManager/Request/AssignUserOrgs.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace OpenAuth.App.Request +{ + public class AssignUserOrgs + { + public long UserId { get; set; } + public List OrgPoses { get; set; } + } +} diff --git a/OpenAuth.App/BaseApp/UserManager/Request/AssignUserRoles.cs b/OpenAuth.App/BaseApp/UserManager/Request/AssignUserRoles.cs new file mode 100644 index 0000000..078de5c --- /dev/null +++ b/OpenAuth.App/BaseApp/UserManager/Request/AssignUserRoles.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace OpenAuth.App.Request +{ + public class AssignUserRoles + { + public long UserId { get; set; } + + public List RoleIds { get; set; } + } +} diff --git a/OpenAuth.App/BaseApp/UserManager/Request/ChangePasswordReq.cs b/OpenAuth.App/BaseApp/UserManager/Request/ChangePasswordReq.cs new file mode 100644 index 0000000..892a5e9 --- /dev/null +++ b/OpenAuth.App/BaseApp/UserManager/Request/ChangePasswordReq.cs @@ -0,0 +1,8 @@ +namespace OpenAuth.App.Request +{ + public class ChangePasswordReq + { + public string Account { get; set; } + public string Password { get; set; } + } +} diff --git a/OpenAuth.App/BaseApp/UserManager/Request/ChangeProfileReq.cs b/OpenAuth.App/BaseApp/UserManager/Request/ChangeProfileReq.cs new file mode 100644 index 0000000..065f8c1 --- /dev/null +++ b/OpenAuth.App/BaseApp/UserManager/Request/ChangeProfileReq.cs @@ -0,0 +1,16 @@ +namespace OpenAuth.App.Request +{ + public class ChangeProfileReq + { + public string Account { get; set; } + + /// + /// 用户姓名 + /// + public string Name { get; set; } + /// + /// 性别 + /// + public int Sex { get; set; } + } +} diff --git a/OpenAuth.App/BaseApp/UserManager/Request/QueryUserListByOrgReq.cs b/OpenAuth.App/BaseApp/UserManager/Request/QueryUserListByOrgReq.cs new file mode 100644 index 0000000..4ced4d5 --- /dev/null +++ b/OpenAuth.App/BaseApp/UserManager/Request/QueryUserListByOrgReq.cs @@ -0,0 +1,7 @@ +namespace OpenAuth.App.Request +{ + public class QueryUserListByOrgReq : PageReq + { + public long orgId { get; set; } + } +} diff --git a/OpenAuth.App/BaseApp/UserManager/Request/QueryUserListByRoleReq.cs b/OpenAuth.App/BaseApp/UserManager/Request/QueryUserListByRoleReq.cs new file mode 100644 index 0000000..0cfabc0 --- /dev/null +++ b/OpenAuth.App/BaseApp/UserManager/Request/QueryUserListByRoleReq.cs @@ -0,0 +1,7 @@ +namespace OpenAuth.App.Request +{ + public class QueryUserListByRoleReq : PageReq + { + public long roleId { get; set; } + } +} diff --git a/OpenAuth.App/BaseApp/UserManager/Request/QueryUserListReq.cs b/OpenAuth.App/BaseApp/UserManager/Request/QueryUserListReq.cs new file mode 100644 index 0000000..88ea0f0 --- /dev/null +++ b/OpenAuth.App/BaseApp/UserManager/Request/QueryUserListReq.cs @@ -0,0 +1,7 @@ +namespace OpenAuth.App.Request +{ + public class QueryUserListReq : PageReq + { + public long orgId { get; set; } + } +} diff --git a/OpenAuth.App/BaseApp/UserManager/Request/UpdateUserReq.cs b/OpenAuth.App/BaseApp/UserManager/Request/UpdateUserReq.cs new file mode 100644 index 0000000..98fc4ec --- /dev/null +++ b/OpenAuth.App/BaseApp/UserManager/Request/UpdateUserReq.cs @@ -0,0 +1,67 @@ +using System.ComponentModel.DataAnnotations; +using Infrastructure; +using OpenAuth.Repository.Domain; + +namespace OpenAuth.App.Request +{ + /// + /// 添加或修改用户信息的请求 + /// + public class UpdateUserReq + { + /// + /// 用户ID + /// + /// + public long Id { get; set; } + + + /// + /// + /// + [Required(ErrorMessage = "账号肯定不能为空啊~~")] + public string Account { get; set; } + + /// + /// + /// + public string Password { get; set; } + + + /// + /// 用户姓名 + /// + /// + [Required(ErrorMessage="姓名不能为空")] + public string Name { get; set; } + + + /// + /// + /// + public int Sex { get; set; } + + + /// + /// 当前状态 + /// + /// + public int Status { get; set; } + + + public static implicit operator UpdateUserReq(SysUser user) + { + return user.MapTo(); + } + + public static implicit operator SysUser(UpdateUserReq view) + { + return view.MapTo(); + } + + public UpdateUserReq() + { + + } + } +} diff --git a/OpenAuth.App/BaseApp/UserManager/Response/UserView.cs b/OpenAuth.App/BaseApp/UserManager/Response/UserView.cs new file mode 100644 index 0000000..dd832e6 --- /dev/null +++ b/OpenAuth.App/BaseApp/UserManager/Response/UserView.cs @@ -0,0 +1,85 @@ +using System; +using Infrastructure; +using OpenAuth.Repository.Domain; + +namespace OpenAuth.App.Response +{ + public class UserView + { + /// + /// 用户ID + /// + /// + public long Id { get; set; } + + + /// + /// + /// + public string Account { get; set; } + + + /// + /// 组织名称 + /// + /// + public string Name { get; set; } + + + /// + /// + /// + public int Sex { get; set; } + + + /// + /// 当前状态 + /// + /// + public int Status { get; set; } + + + /// + /// 组织类型 + /// + /// + public int Type { get; set; } + + + + /// + /// 创建时间 + /// + /// + public DateTime CreateTime { get; set; } + + + /// + /// 创建人名字 + /// + /// The create user. + public long CreateUser { get; set; } + + /// + /// 所属组织名称,多个可用,分隔 + /// + /// The organizations. + public string Organizations { get; set; } + + public string OrganizationIds { get; set; } + /// + /// 用户最大部门等级 + /// + public string OrgMaxLevel { get; set; } + + public static implicit operator UserView(SysUser user) + { + return user.MapTo(); + } + + public static implicit operator SysUser(UserView view) + { + return view.MapTo(); + } + } +} diff --git a/OpenAuth.App/BaseApp/UserManager/UserManagerApp.cs b/OpenAuth.App/BaseApp/UserManager/UserManagerApp.cs new file mode 100644 index 0000000..2bcbf99 --- /dev/null +++ b/OpenAuth.App/BaseApp/UserManager/UserManagerApp.cs @@ -0,0 +1,669 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using DocumentFormat.OpenXml.Spreadsheet; +using DocumentFormat.OpenXml.Wordprocessing; +using Infrastructure; +using Infrastructure.Extensions; +using Infrastructure.Helpers; +using Infrastructure.Utilities; +using Microsoft.AspNetCore.Http; +using NetModular.DocX.Core; +using NPOI.HSSF.UserModel; +using NPOI.SS.Formula.Functions; +using NPOI.SS.UserModel; +using NPOI.XSSF.UserModel; +using OpenAuth.App.Base; +using OpenAuth.App.BaseApp.Base; +using OpenAuth.App.Config; +using OpenAuth.App.Interface; +using OpenAuth.App.Request; +using OpenAuth.App.Response; +using OpenAuth.Repository; +using OpenAuth.Repository.Domain; +using SixLabors.ImageSharp; +using SqlSugar; +using Yitter.IdGenerator; +using static ICSharpCode.SharpZipLib.Zip.ExtendedUnixData; + +namespace OpenAuth.App +{ + public class UserManagerApp : SqlSugarBaseApp + { + private ISqlSugarClient client; + + #region 构造函数 + public UserManagerApp( + ISugarUnitOfWork unitWork, + ISimpleClient repository, + IAuth auth, + ISqlSugarClient sqlSugarClient + ) : base(unitWork, repository, auth) + { + this.client = sqlSugarClient; + } + #endregion + + #region 用户查询 + + public async Task>>> LoadAll(QueryUserListReq request) + { + RefAsync totalNumber = 0; + //过滤orgid传参 + List dpt = new List(); + if (request.orgId != 0) + { + dpt = client.Queryable().ToChildList(it => it.ParentId, request.orgId)?.Select(it => it.Id).ToList(); + } + var aa = client.Queryable().Where(r => dpt.Contains(r.OrgId)).ToList(); + var table = await base.Repository.AsQueryable() + .WhereIF(!string.IsNullOrEmpty(request.key), u => u.Account.Contains(request.key) || u.Name.Contains(request.key)) + .LeftJoin((u, o) => u.Id == o.UserId) + .WhereIF(request.orgId != 0, (u, o) => dpt.Contains(o.OrgId)) + .OrderByDescending((u, o) => u.CreateTime) + .Distinct() + .Select((u, o) => new + { + id = u.Id, + account = u.Account, + name = u.Name, + createtime = u.CreateTime, + department = SqlFunc.Subqueryable().Where(a => a.UserId == u.Id).LeftJoin((a, o) => a.OrgId == o.Id).SelectStringJoin((a, o) => o.Name, ","), + role = SqlFunc.Subqueryable().Where(a => a.UserId == u.Id).LeftJoin((a, o) => a.RoleId == o.Id).SelectStringJoin((a, o) => o.Name, ","), + }) + .ToPageListAsync(request.page, request.limit, totalNumber); + + return new Response>>() + { + Result = new PageInfo> + { + Items = table, + Total = totalNumber + } + }; + } + public List getAllChildOrgId(long parentId) + { + var query = base.Repository.AsSugarClient().Queryable() + .Where(c => c.ParentId == parentId) + .Select(c => new { c.Id, c.ParentId }) + .ToList(); + + var childIds = query.Select(c => c.Id).ToList(); + var grandChildIds = query.SelectMany(c => getAllChildOrgId(c.Id)).ToList(); + + childIds.AddRange(grandChildIds); + return childIds; + } + + + public async Task>>> Load(QueryUserListReq request) + { + + var user = _auth.GetCurrentUser().User; + + string sqlstr = "select \"min\"(\"Level\") from sys_userorg where \"UserId\"='" + user.Id + "' "; + var level = client.Ado.GetInt(sqlstr); + if (level == 0 || user.Id == -1) + { + + + RefAsync totalNumber = 0; + + var table = await base.Repository.AsQueryable() + .WhereIF(!string.IsNullOrEmpty(request.key), u => u.Account.Contains(request.key) || u.Name.Contains(request.key)) + .WhereIF(request.orgId != 0, + u => SqlFunc.Subqueryable().Where(uo => uo.OrgId == request.orgId && uo.UserId == u.Id).Any()) + .OrderByDescending(u => u.CreateTime) + .Select() + .ToPageListAsync(request.page, request.limit, totalNumber); + + return new Response>>() + { + Result = new PageInfo> + { + Items = table, + Total = totalNumber + } + }; + + + } + else + { + string sqlstr1 = "select \"OrgId\" from sys_userorg where \"Level\"=" + level + " and \"UserId\"='" + user.Id + "' "; + List orgIds1 = client.Ado.SqlQuery(sqlstr1); + int count11 = orgIds1.Count; + for (int i = 0; i < count11; i++) + + { + List longs = getAllChildOrgId(orgIds1[i]); + orgIds1.AddRange(longs); + } + string keyword = ""; + if (!string.IsNullOrEmpty(request.key)) + { + keyword = keyword + "and a.\"Name\" like '%" + request.key + "%'"; + } + string orgsql = ""; + if (request.orgId != 0) + { + orgsql = " and \"OrgId\" ='" + request.orgId + "'"; + } + string orgIds = ""; + for (int j = 0; j < orgIds1.Count; j++) + { + if (j == orgIds1.Count - 1) + { + orgIds = orgIds + orgIds1[j]; + } + else + { + orgIds = orgIds + orgIds1[j] + ","; + } + + } + + string sql = "WITH t as (\r\nselect \"UserId\" from ( select * from sys_userorg where \"UserId\" not in (select \"UserId\" from sys_userorg where \"Level\"<(select \"min\"(\"Level\") from sys_userorg " + + // " where \"UserId\"='"+ user.Id+ "' ))) a where a.\"OrgId\" in (select \"OrgId\" from sys_userorg where \"UserId\"='"+ user.Id+ "'"+ orgsql + " ) GROUP BY \"UserId\")"; + " where \"UserId\"='" + user.Id + "' ))) a where a.\"OrgId\" =ANY(array[" + orgIds + "]) GROUP BY \"UserId\")"; + string sql1 = "((SELECT a.* FROM t LEFT JOIN sys_user a on t.\"UserId\"=a.\"Id\" where 1=1 " + keyword + " ) UNION (SELECT * FROM sys_user a where a.\"CreateId\"='" + user.Id + "' " + keyword + ")) limit " + request.limit + "OFFSET " + (request.page - 1) * request.limit; + string sql2 = "(SELECT COUNT(a.*) FROM t LEFT JOIN sys_user a on t.\"UserId\"=a.\"Id\" where 1=1 " + keyword + " ) "; + string sql3 = "SELECT COUNT(a.*) FROM sys_user a where a.\"CreateId\"='" + user.Id + "' " + keyword; + + + var table = client.Ado.SqlQuery(sql + sql1); + int count1 = client.Ado.GetInt(sql + sql2); + int count2 = client.Ado.GetInt(sql + sql3); + + int count = count1 + count2; + /* var orgIds = base._auth.GetCurrentUser().Orgs.Select(o => o.Id); + + RefAsync totalNumber = 0; + + var table = await base.Repository.AsQueryable() + .WhereIF(!string.IsNullOrEmpty(request.key), u => u.Account.Contains(request.key) || u.Name.Contains(request.key)) + .WhereIF(request.orgId != 0, + u => SqlFunc.Subqueryable().Where(uo => uo.OrgId == request.orgId && uo.UserId == u.Id).Any()) + .Where(u => SqlFunc.Subqueryable().Where(uo => orgIds.Contains(uo.OrgId) && uo.UserId == u.Id).Any()) + .OrderByDescending(u => u.CreateTime) + .Select() + .ToPageListAsync(request.page, request.limit, totalNumber);*/ + + return new Response>>() + { + Result = new PageInfo> + { + Items = table, + Total = count + } + }; + } + } + + public async Task>>> LoadUsers(QueryUserListReq request) + { + //获取当前用户 + var user = _auth.GetCurrentUser().User; + RefAsync totalNumber = 0; + //该用户下包含所有部门 + List deplist = new List(); + if (user != null) + { + var departs = client.Queryable().Where(r => r.UserId == user.Id).ToList(); + foreach (var item in departs) + { + var list = client.Queryable().ToChildList(it => it.ParentId, item.OrgId)?.Select(it => it.Id).ToList(); + deplist = deplist.Union(list).ToList(); + } + } + + //过滤orgid传参 + List dpt = new List(); + if (request.orgId != 0) + { + dpt = client.Queryable().ToChildList(it => it.ParentId, request.orgId)?.Select(it => it.Id).ToList(); + } + + //获取列表数据 + var table = await base.Repository.AsQueryable() + .WhereIF(!string.IsNullOrEmpty(request.key), u => u.Account.Contains(request.key) || u.Name.Contains(request.key))//过滤key + .LeftJoin((u, o) => u.Id == o.UserId) + .Where((u, o) => deplist.Contains(o.OrgId) || u.CreateId == user.Id) //部门及创建人 + .WhereIF(request.orgId != 0, (u, o) => dpt.Contains(o.OrgId)) //部门及左侧选择部门 + .Distinct() + .OrderByDescending((u, o) => u.CreateTime) + .Select((u, o) => new + { + id = u.Id, + account = u.Account, + name = u.Name, + create = u.CreateTime, + department = SqlFunc.Subqueryable().Where(a => a.UserId == u.Id).LeftJoin((a, o) => a.OrgId == o.Id).SelectStringJoin((a, o) => o.Name, ","), + role = SqlFunc.Subqueryable().Where(a => a.UserId == u.Id).LeftJoin((a, o) => a.RoleId == o.Id).SelectStringJoin((a, o) => o.Name, ","), + }) + .ToPageListAsync(request.page, request.limit, totalNumber); + + return new Response>>() + { + Result = new PageInfo> + { + Items = table, + Total = totalNumber + } + }; + } + + /// + /// 获取指定角色包含的用户列表 + /// + /// + /// + public async Task>>> LoadByRole(QueryUserListByRoleReq request) + { + RefAsync totalCount = 0; + + var users = await client.Queryable() + .LeftJoin((ur, u) => ur.UserId == u.Id) + .Where(ur => ur.RoleId == request.roleId) + .Select((ur, u) => new UserView + { + Id = u.Id, + Account = u.Account, + Name = u.Name + }).ToPageListAsync(request.page, request.limit, totalCount); + + return new Response>> + { + Code = 200, + Message = "success", + Result = new PageInfo> + { + Items = users, + Total = totalCount + } + }; + + } + + + /// + /// 获取指定机构包含的用户列表 + /// + /// + /// + public async Task LoadByOrg(QueryUserListByOrgReq request) + { + var db = base.Repository.AsSugarClient(); + RefAsync totalCount = 0; + + var users = await db.CopyNew().Queryable() + .LeftJoin((uo, u) => uo.UserId == u.Id) + .Where((uo, u) => uo.OrgId == request.orgId) + .ToPageListAsync(request.page, request.limit, totalCount); + + return new TableData + { + count = (int)totalCount, + data = users + }; + } + + /// + /// 根据 account 判断帐号是否存在 + /// + /// + /// + public bool IsExistUser(string account) + { + return base.Repository.IsAny(a => a.Account == account); + } + + public SysUser Get(long id) + { + return Repository.GetById(id); + } + + public SysUser GetByAccount(string account) + { + return Repository.GetFirst(u => u.Account == account); + } + #endregion + + #region 用户增删改 + + #region 添加/修改 + public Response AddOrUpdate(UpdateUserReq request) + { + request.ValidationEntity(u => new { u.Account, u.Name }); + + SysUser requser = request; + requser.CreateId = _auth.GetCurrentUser().User.Id; + + using (var uow = base.UnitWork.CreateContext()) + { + if (request.Id == 0) + { + if (uow.User.IsAny(u => u.Account == request.Account)) + { + throw new Exception("用户账号已存在"); + } + + requser.Secretkey = Md5Helper.Encrypt(CommonHelper.CreateNo(), 16).ToLower(); + if (string.IsNullOrEmpty(requser.Password)) + { + requser.Password = Md5Helper.Encrypt(DESEncrypt.Encrypt(Md5Helper.Hash(requser.Account), requser.Secretkey).ToLower(), 32).ToLower(); //如果客户端没提供密码,默认密码同账号 + } + else + { + requser.Password = Md5Helper.Encrypt(DESEncrypt.Encrypt(requser.Password, requser.Secretkey).ToLower(), 32).ToLower(); + } + requser.CreateTime = DateTime.Now; + requser.Id = Yitter.IdGenerator.YitIdHelper.NextId(); + uow.User.Insert(requser); + request.Id = requser.Id; //要把保存后的ID存入view + } + else + { + uow.User.Update(u => new SysUser + { + Account = requser.Account, + BizCode = requser.BizCode, + Name = requser.Name, + Sex = requser.Sex, + Status = requser.Status + }, u => u.Id == request.Id); + if (!string.IsNullOrEmpty(requser.Password)) //密码为空的时候,不做修改 + { + var userInfo = uow.User.GetById(requser.Id); + + requser.Password = Md5Helper.Encrypt(DESEncrypt.Encrypt(requser.Password, userInfo.Secretkey).ToLower(), 32).ToLower(); + uow.User.Update(u => new SysUser + { + Password = requser.Password + }, u => u.Id == request.Id); + } + } + + var flag = uow.Commit(); + + return new Response + { + Result = flag, + Message = flag == true ? "success" : "error" + }; + } + } + #endregion + + #region 删除 + /// + /// 删除用户,包含用户与组织关系、用户与角色关系 + /// + /// + public Response Delete(long[] ids) + { + using (var uow = base.UnitWork.CreateContext()) + { + uow.SysUserOrg.Delete(a => ids.Contains(a.UserId)); + uow.SysUserRole.Delete(a => ids.Contains(a.UserId)); + uow.User.Delete(u => ids.Contains(u.Id)); + var flag = uow.Commit(); + return new Response + { + Result = flag, + Message = flag == true ? "success" : "error" + }; + } + } + #endregion + + #region 分配角色 + public async Task> UserRoles(AssignUserRoles userRoles) + { + using (var uow = base.UnitWork.CreateContext()) + { + //删除用户相关角色 + await uow.SysUserRole.DeleteByIdAsync(userRoles.UserId); + //重新添加本次角色 + var models = userRoles.RoleIds.Select(r => new SysUserRole { UserId = userRoles.UserId, RoleId = r }).ToList(); + await uow.SysUserRole.InsertRangeAsync(models); + + var flag = uow.Commit(); + + return new Response + { + Result = flag, + Message = flag == true ? "success" : "error" + }; + } + } + #endregion + + #region 分配部门 + public async Task> UserOrgs(AssignUserOrgs userOrgs) + { + using (var uow = base.UnitWork.CreateContext()) + { + //删除用户相关部门 + await uow.SysUserOrg.DeleteByIdAsync(userOrgs.UserId); + //重新添加本次部门 + var models = userOrgs.OrgPoses.Select(o => new SysUserOrg { UserId = userOrgs.UserId, OrgId = o.OrgId, PositionId = o.PosId, Level = o.Level }).ToList(); + await uow.SysUserOrg.InsertRangeAsync(models); + + var flag = uow.Commit(); + + return new Response + { + Result = flag, + Message = flag == true ? "success" : "error" + }; + } + } + #endregion + #region 修改密码 + /// + /// 修改密码 + /// + /// + public void ChangePassword(ChangePasswordReq request) + { + var user = base.Repository.GetFirst(a => a.Account == request.Account); + + if (user != null) + { + var pwd = Md5Helper.Encrypt(DESEncrypt.Encrypt(request.Password, user.Secretkey).ToLower(), 32).ToLower(); + + Repository.Update(user => new SysUser + { + Password = pwd + }, u => u.Account == request.Account); + } + } + #endregion + + #region 修改用户资料 + /// + /// 修改用户资料 + /// + /// + public void ChangeProfile(ChangeProfileReq request) + { + if (request.Account == Define.SYSTEM_USERNAME) + { + throw new Exception("不能修改超级管理员信息"); + } + + Repository.Update(user => new SysUser + { + Name = request.Name, + Sex = request.Sex + }, u => u.Account == request.Account); + } + #endregion + + #region 更新数据库实体类 + /// + /// 更新数据库实体类 + /// + /// + public string AddEntity() + { + using (var uow = base.UnitWork.CreateContext()) + { + foreach (var item in uow.Db.DbMaintenance.GetTableInfoList().Where(r => r.Name.ToLower().StartsWith("drone_caseinfo_sthx"))) + { + //string entityName = item.Name.Substring(0, 1).ToUpper() + item.Name.Substring(1, 7).ToLower() + item.Name.Substring(9, 1).ToUpper() + item.Name.Substring(10).ToLower();/*实体名大写*/ + string entityName = "DroneCaseInfoSTHX"; + uow.Db.MappingTables.Add(entityName, item.Name); + //foreach (var col in db.DbMaintenance.GetColumnInfosByTableName(item.Name)) + //{ + // db.MappingColumns.Add(col.DbColumnName.ToUpper() /*类的属性大写*/, col.DbColumnName, entityName); + //} + } + uow.Db.DbFirst.Where(r => r.ToLower().StartsWith("drone_caseinfo_sthx")).IsCreateAttribute().CreateClassFile("E:\\天空地\\codeLinyi\\Project1\\OpenAuth.Repository\\Domain", "OpenAuth.Repository.Domain"); + uow.Commit(); + } + return "更新实体成功"; + } + #endregion + + + + #endregion + + + /// + /// 上传用户信息 + /// + /// + /// + public Response ImportUserInfo(IFormFileCollection formFiles) + { + IFormFile file = formFiles[0]; + //存储文件到服务器 + if (file != null) + { + if (file.FileName.IndexOf(".xls") > 0 || file.FileName.IndexOf(".xlsx") > 0) + { + //数据库导入 + IWorkbook workbook = null; + if (file.FileName.IndexOf(".xlsx") > 0) + { + using (var stream = file.OpenReadStream()) + { + workbook = new XSSFWorkbook(stream);//excel的版本2007 + } + } + else if (file.FileName.IndexOf(".xls") > 0) + { + using (var stream = file.OpenReadStream()) + { + workbook = new HSSFWorkbook(stream);//excel的版本2003 + } + } + + //数据处理 + using (var uow = base.UnitWork.CreateContext()) + { + //获取sheet + ISheet sheet; + sheet = workbook.GetSheetAt(0); + //处理sheet数据 + string res = ""; + if (sheet != null) + { + IRow firstRow = sheet.GetRow(0); + //获取有效数据行数 + int lastRow = sheet.LastRowNum; + int rowCount = 0; + //具体excel数据解析 + for (int i = 1; i <= lastRow; ++i) + { + IRow row = sheet.GetRow(i); + if (row == null || string.IsNullOrEmpty(row.GetCell(0).ToString())) continue; + + SysUser user = new SysUser(); + user.Id = YitIdHelper.NextId(); + user.CreateTime = DateTime.Now; + user.Name = row.GetCell(4).ToString(); + user.CreateId = _auth.GetCurrentUser().User.Id; + user.Account=row.GetCell(5).ToString(); + user.Password = Md5Helper.Encrypt(user.Account, 32).ToLower(); + + if (uow.User.IsAny(u => u.Account == user.Account)) + { + res += "账号" + user.Account + "已存在,"; + } + else + { + user.Secretkey = Md5Helper.Encrypt(CommonHelper.CreateNo(), 16).ToLower(); + if (string.IsNullOrEmpty(user.Password)) + { + user.Password = Md5Helper.Encrypt(DESEncrypt.Encrypt(Md5Helper.Hash(user.Account), user.Secretkey).ToLower(), 32).ToLower(); //如果客户端没提供密码,默认密码同账号 + } + else + { + + user.Password = Md5Helper.Encrypt(DESEncrypt.Encrypt(user.Password, user.Secretkey).ToLower(), 32).ToLower(); + } + uow.User.Insert(user); + + if (!string.IsNullOrEmpty(row.GetCell(7).ToString())) + { + //为用户分配角色 + SysUserRole userrole = new SysUserRole(); + userrole.UserId = user.Id; + userrole.RoleId = Convert.ToInt64(row.GetCell(7).ToString()); + //删除用户相关角色 + uow.SysUserRole.DeleteById(user.Id); + //添加 + uow.SysUserRole.Insert(userrole); + } + + if (!string.IsNullOrEmpty(row.GetCell(2).ToString())) + { + //为用户分配部门 + SysUserOrg userorg = new SysUserOrg(); + userorg.UserId = user.Id; + userorg.OrgId = Convert.ToInt64(row.GetCell(2).ToString()); + userorg.PositionId = 0; + userorg.Level = Convert.ToInt16(row.GetCell(3).ToString()); + //删除用户相关部门 + uow.SysUserOrg.DeleteById(user.Id); + //重新添加本次部门 + uow.SysUserOrg.Insert(userorg); + } + } + } + } + var flag = uow.Commit(); + + return new Response + { + Result = flag, + Message = flag == true ? "更新成功 "+res : "更新失败" + }; + } + } + else + { + return new Response + { + Result = false, + Message = "上传文件类型错误,请上传Excel文件" + }; + } + } + else + { + return new Response + { + Result = false, + Message = "文件为空" + }; + } + } + } +} \ No newline at end of file diff --git a/OpenAuth.App/BaseApp/WFDelegate/Request/WFDelegateDto.cs b/OpenAuth.App/BaseApp/WFDelegate/Request/WFDelegateDto.cs new file mode 100644 index 0000000..6aced72 --- /dev/null +++ b/OpenAuth.App/BaseApp/WFDelegate/Request/WFDelegateDto.cs @@ -0,0 +1,22 @@ +using OpenAuth.Repository.Domain; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace OpenAuth.App.Request +{ + public class WFDelegateDto + { + /// + /// 委托信息 + /// + public WFDelegateRule entity { get; set; } + + /// + /// 模板信息 + /// + public List list { get; set; } + } +} diff --git a/OpenAuth.App/BaseApp/WFDelegate/WFDelegateApp.cs b/OpenAuth.App/BaseApp/WFDelegate/WFDelegateApp.cs new file mode 100644 index 0000000..f689450 --- /dev/null +++ b/OpenAuth.App/BaseApp/WFDelegate/WFDelegateApp.cs @@ -0,0 +1,203 @@ +using Infrastructure; +using OpenAuth.App.Base; +using OpenAuth.App.Interface; +using OpenAuth.App.Request; +using OpenAuth.Repository; +using OpenAuth.Repository.Domain; +using SqlSugar; +using StackExchange.Redis; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using OpenAuth.App.BaseApp.Base; + +namespace OpenAuth.App +{ + public class WFDelegateApp : SqlSugarBaseApp + { + ISqlSugarClient client; + public WFDelegateApp(ISugarUnitOfWork unitWork, ISimpleClient repository, IAuth auth) : base(unitWork, repository, auth) + { + client = base.Repository.AsSugarClient(); + } + + + #region 获取数据 + /// + /// 获取分页列表 + /// + /// 分页参数 + /// 查询参数 + /// + public async Task>> GetPageList(PageReq pageReq, WFDelegateRule queryParams) + { + RefAsync totalCount = 0; + var userId = _auth.GetUserId(); + var exp = Expressionable.Create() + .AndIF(!string.IsNullOrEmpty(queryParams.ToUserName), t => t.ToUserName.Contains(queryParams.ToUserName)) + .AndIF(queryParams.EnabledMark != null, t => t.EnabledMark == queryParams.EnabledMark) + .AndIF(queryParams.Type != null, t => t.Type == queryParams.Type) + .And(t => t.CreateUserId == userId).ToExpression(); + + var list = await client.Queryable().Where(exp) + .ToPageListAsync(pageReq.page, pageReq.limit, totalCount); + return new PageInfo> + { + Items = list, + Total = totalCount + }; + + } + /// + /// 根据委托人获取委托记录 + /// + /// 0 审核委托 1发起委托 + /// + public Task> GetList(int type = 0) { return null; } + /// + /// 获取关联的模板数据 + /// + /// + public async Task> GetRelationList(string keyValue) + { + return await client.Queryable().Where(a => a.DelegateRuleId == keyValue).ToListAsync(); + } + /// + /// 获取委托信息 + /// + /// + public Task Get(string keyValue) { return null; } + /// + /// 获取我的委托人(发起委托) + /// + /// 流程模板编码 + /// + public async Task> GetMyUserList(string code) + { + var list = new List(); + var userInfo = base._auth.GetCurrentUser().User; + var listTmp = await GetMyUserList(userInfo, code); + var dic = new Dictionary(); + foreach (var item in listTmp) + { + if (!dic.ContainsKey(item.CreateUserId)) + { + dic.Add(item.CreateUserId, true); + list.Add(item.CreateUserId); + } + } + if (list.Count > 0) + { + return await client.Queryable().Where(t => list.Contains(t.Id.ToString())).ToListAsync(); + } + else + { + return new List(); + } + } + + public async Task GetEntity(string keyValue) + { + return await base.Repository.GetByIdAsync(keyValue); + } + #endregion + + #region 提交数据 + /// + /// 删除实体 + /// + /// 主键 + public async Task> DeleteEntity(string keyValue) + { + using (var uwo = UnitWork.CreateContext()) + { + await uwo.WFDelegateRule.DeleteByIdAsync(keyValue); + await uwo.WFDelegateRelation.DeleteAsync(t => t.DelegateRuleId == keyValue); + + var flag = uwo.Commit(); + return new Response + { + Result = flag, + Message = flag == true ? "success" : "error" + }; + } + } + /// + /// 保存实体 + /// + /// 主键 + /// 实体数据 + /// 关联模板主键 + public async Task> SaveEntity(string keyValue, WFDelegateRule wfDelegateRuleEntity, string[] schemeInfoList) + { + using (var uwo = UnitWork.CreateContext()) + { + if (string.IsNullOrEmpty(keyValue)) + { + wfDelegateRuleEntity.Id = Guid.NewGuid().ToString(); + wfDelegateRuleEntity.CreateDate = DateTime.Now; + wfDelegateRuleEntity.EnabledMark = 1; + wfDelegateRuleEntity.CreateUserId = _auth.GetUserId(); + wfDelegateRuleEntity.CreateUserName = _auth.GetUserName(); + + await uwo.WFDelegateRule.InsertAsync(wfDelegateRuleEntity); + } + else + { + wfDelegateRuleEntity.Id = keyValue; + await uwo.WFDelegateRule.UpdateAsync(wfDelegateRuleEntity); + await uwo.WFDelegateRelation.DeleteAsync(t => t.DelegateRuleId == keyValue); + } + + foreach (string schemeInfoId in schemeInfoList) + { + WFDelegateRelation wfDelegateRuleRelationEntity = new WFDelegateRelation(); + wfDelegateRuleRelationEntity.Id = Guid.NewGuid().ToString(); + wfDelegateRuleRelationEntity.DelegateRuleId = wfDelegateRuleEntity.Id; + wfDelegateRuleRelationEntity.SchemeInfoId = schemeInfoId; + await uwo.WFDelegateRelation.InsertAsync(wfDelegateRuleRelationEntity); + } + + var flag = uwo.Commit(); + return new Response + { + Result = flag, + Message = flag == true ? "success" : "error" + }; + } + } + /// + /// 更新委托规则状态信息 + /// + /// 主键 + /// + public async Task> UpdateState(string keyValue, int state) + { + var flag = await base.Repository.UpdateSetColumnsTrueAsync(a => new WFDelegateRule { EnabledMark = state }, a => a.Id == keyValue); + return new Response + { + Result = flag, + Message = flag == true ? "success" : "error" + }; + } + #endregion + + + public async Task> GetMyUserList(SysUser userInfo, string code) + { + string userId = userInfo.Id.ToString(); + DateTime datetime = DateTime.Now; + + var queryable = client.Queryable() + .LeftJoin((rule, r) => rule.Id == r.DelegateRuleId); + var exp = Expressionable.Create() + .And((rule, r) => rule.ToUserId == userId && rule.Type == 1 && r.SchemeInfoId == code) + .And((rule, r) => rule.BeginDate <= datetime && rule.EndDate >= @datetime) + .ToExpression(); + return await queryable.Where(exp).Select((rule, r) => rule).ToListAsync(); + + } + } +} diff --git a/OpenAuth.App/BaseApp/WFProcess/Config/WFDelegate.cs b/OpenAuth.App/BaseApp/WFProcess/Config/WFDelegate.cs new file mode 100644 index 0000000..2e56b1c --- /dev/null +++ b/OpenAuth.App/BaseApp/WFProcess/Config/WFDelegate.cs @@ -0,0 +1,54 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace OpenAuth.App.Config +{ + /// + /// 获取等待任务 + /// + /// 流程进程ID + /// 流程单元ID + /// + public delegate Task> GetAwaitTaskListMethod(string processId, string unitId); + /// + /// 获取设置人员 + /// + /// 人员配置信息 + /// 流程发起人 + /// 流程进程实例ID + /// 开始节点 + /// + public delegate Task> GetUserListMethod(List auditorList, WFUserInfo createUser, string processId, WFUnit startNode); + /// + /// 获取系统管理员 + /// + /// + public delegate Task> GetSystemUserListMethod(); + + /// + /// 获取单元上一次的审核人 + /// + /// 流程进程ID + /// 流程单元ID + /// + public delegate Task> GetRreTaskUserListMethod(string processId, string unitId); + + /// + /// 判断会签是否通过 + /// + /// 流程进程ID + /// 流程单元ID + /// + public delegate Task IsCountersignAgreeMethod(string processId, string unitId); + + /// + /// 获取上一个流入节点(不是驳回流入的) + /// + /// 流程进程ID + /// 流程单元ID + /// + public delegate Task GetPrevUnitIdMethod(string processId, string unitId); +} diff --git a/OpenAuth.App/BaseApp/WFProcess/Config/WFEngineConfig.cs b/OpenAuth.App/BaseApp/WFProcess/Config/WFEngineConfig.cs new file mode 100644 index 0000000..0f9cc9c --- /dev/null +++ b/OpenAuth.App/BaseApp/WFProcess/Config/WFEngineConfig.cs @@ -0,0 +1,42 @@ +using OpenAuth.App.Config; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace OpenAuth.App.Config +{ + public class WFEngineConfig + { + + /// + /// 运行参数 + /// + public WFParams Params { get; set; } + /// + /// 获取等待任务 + /// + public GetAwaitTaskListMethod GetAwaitTaskList { get; set; } + /// + /// 获取审核人 + /// + public GetUserListMethod GetUserList { get; set; } + /// + /// 获取单元上一次的审核人 + /// + public GetRreTaskUserListMethod GetPrevTaskUserList { get; set; } + /// + /// 获取系统管理员 + /// + public GetSystemUserListMethod GetSystemUserList { get; set; } + /// + /// 判断会签是否通过 + /// + public IsCountersignAgreeMethod IsCountersignAgree { get; set; } + /// + /// 获取上一个流入节点(不是驳回流入的) + /// + public GetPrevUnitIdMethod GetPrevUnitId { get; set; } + } +} diff --git a/OpenAuth.App/BaseApp/WFProcess/Config/WFParams.cs b/OpenAuth.App/BaseApp/WFProcess/Config/WFParams.cs new file mode 100644 index 0000000..9a6651e --- /dev/null +++ b/OpenAuth.App/BaseApp/WFProcess/Config/WFParams.cs @@ -0,0 +1,83 @@ +using OpenAuth.App.Config; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace OpenAuth.App.Config +{ + public class WFParams + { + /// + /// 是否已经有流程实例 + /// + public bool HasInstance { get; set; } + /// + /// 是否是子流程 + /// + public bool IsChild { get; set; } + /// + /// 父流程的发起子流程的节点Id + /// + public string ParentNodeId { get; set; } + /// + /// 父级流程任务主键 + /// + public string ParentTaskId { get; set; } + /// + /// 父级流程实例主键 + /// + public string ParentProcessId { get; set; } + /// + /// 流程模板 + /// + public string Scheme { get; set; } + /// + /// 流程模板名称 + /// + public string SchemeName { get; set; } + /// + /// 流程模板编码 + /// + public string SchemeCode { get; set; } + /// + /// 流程模板主键 + /// + public string SchemeId { get; set; } + /// + /// 流程实例Id + /// + public string ProcessId { get; set; } + /// + /// 流程标题 + /// + public string Title { get; set; } + /// + /// 审核人信息 + /// + public string Auditers { get; set; } + /// + /// 流程发起用户 + /// + public WFUserInfo CreateUser { get; set; } + /// + /// 当前执行用户 + /// + public WFUserInfo CurrentUser { get; set; } + /// + /// 流程状态 0 默认运行状态 1 重新发起 2 运行结束 + /// + public int State { get; set; } + /// + /// 流程是否开始审核 1 是 0 不是 + /// + public int IsStart { get; set; } + /// + /// 下一个节点审核人 + /// + public Dictionary NextUsers { get; set; } + + + } +} diff --git a/OpenAuth.App/BaseApp/WFProcess/Config/WFUserInfo.cs b/OpenAuth.App/BaseApp/WFProcess/Config/WFUserInfo.cs new file mode 100644 index 0000000..a8804bf --- /dev/null +++ b/OpenAuth.App/BaseApp/WFProcess/Config/WFUserInfo.cs @@ -0,0 +1,66 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace OpenAuth.App.Config +{ + public class WFUserInfo + { + /// + /// ID + /// + public string Id { get; set; } + /// + /// 账号 + /// + public string Account { get; set; } + /// + /// 名称 + /// + public string Name { get; set; } + /// + /// 公司 + /// + public string CompanyId { get; set; } + /// + /// 公司名称 + /// + public string CompanyName { get; set; } + /// + /// 部门 + /// + public string DepartmentId { get; set; } + /// + /// 部门名称 + /// + public string DepartmentName { get; set; } + /// + /// 审核顺序 + /// + public int Sort { get; set; } + /// + /// 是否需要审核 + /// + public bool IsAwait { get; set; } + /// + /// 是否没有审核人 + /// + public bool NotHasUser { get; set; } + /// + /// 上一次审批人是否同意(这边的定义为除了驳回的) + /// + public bool IsAgree { get; set; } + + /// + /// 是否第一次审核 + /// + public bool IsNew { get; set; } + + /// + /// 任务状态 1.激活 2.待激活 3.完成 4.关闭 5.加签状态 6.转移给其他人 + /// + public int State { get; set; } + } +} diff --git a/OpenAuth.App/BaseApp/WFProcess/IWFEngine.cs b/OpenAuth.App/BaseApp/WFProcess/IWFEngine.cs new file mode 100644 index 0000000..c376b6c --- /dev/null +++ b/OpenAuth.App/BaseApp/WFProcess/IWFEngine.cs @@ -0,0 +1,59 @@ +using OpenAuth.App.Config; +using OpenAuth.Repository.Domain; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace OpenAuth.App +{ + public interface IWFEngine + { + /// + /// 开始节点 + /// + WFUnit StartNode { get; } + + /// + /// 流程运行参数 + /// + WFEngineConfig Config { get; } + + + /// + /// 流程发起用户 + /// + WFUserInfo CreateUser { get; } + + + /// + /// 流程配置信息 + /// + WorkFlow.WFScheme WFScheme { get; } + + /// + /// 获取流程单元信息 + /// + /// id + /// 流程单元信息 + WFUnit GetNode(string id); + + /// + /// 获取下一节点集合 + /// + /// 开始节点 + /// 执行动作编码 + /// + List GetNextUnits(string startId, string code = ""); + + /// + /// 获取任务 + /// + /// 开始节点 + /// 执行动作编码 + /// 下一个指定节点 + /// + Task> GetTask(string startId, string code = "", string toUnitId = ""); + } +} diff --git a/OpenAuth.App/BaseApp/WFProcess/IWorkFlowMethod.cs b/OpenAuth.App/BaseApp/WFProcess/IWorkFlowMethod.cs new file mode 100644 index 0000000..1ae6dce --- /dev/null +++ b/OpenAuth.App/BaseApp/WFProcess/IWorkFlowMethod.cs @@ -0,0 +1,18 @@ +using ce.autofac.extension; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace OpenAuth.App.WorkFlow +{ + public interface IWorkFlowMethod : IBLL + { + /// + /// 流程绑定方法需要继承的接口 + /// + /// + Task Execute(WfMethodParameter parameter); + } +} diff --git a/OpenAuth.App/BaseApp/WFProcess/NodeTypeEnum.cs b/OpenAuth.App/BaseApp/WFProcess/NodeTypeEnum.cs new file mode 100644 index 0000000..2a891ce --- /dev/null +++ b/OpenAuth.App/BaseApp/WFProcess/NodeTypeEnum.cs @@ -0,0 +1,29 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace OpenAuth.App +{ + public enum NodeTypeEnum + { + [Description("bpmn:StartEvent")] + startEvent, + [Description("bpmn:EndEvent")] + endEvent, + [Description("bpmn:Task")] + userTask, + [Description("bpmn:ParallelGateway")] + gatewayAnd, + [Description("bpmn:ExclusiveGateway")] + gatewayXor, + [Description("bpmn:InclusiveGateway")] + gatewayInclusive, + [Description("bpmn:SubProcess")] + subprocess, + [Description("bpmn:SequenceFlow")] + myline + } +} diff --git a/OpenAuth.App/BaseApp/WFProcess/Request/WFProcessDto.cs b/OpenAuth.App/BaseApp/WFProcess/Request/WFProcessDto.cs new file mode 100644 index 0000000..897ab84 --- /dev/null +++ b/OpenAuth.App/BaseApp/WFProcess/Request/WFProcessDto.cs @@ -0,0 +1,81 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace OpenAuth.App.Request +{ + public class WFProcessDto + { + /// + /// 流程进程实例 + /// + public string ProcessId { get; set; } + /// + /// 流程模板编码 + /// + public string SchemeCode { get; set; } + /// + /// 流程标题 + /// + public string Title { get; set; } + /// + /// 流程实例补充信息(一般开发用) + /// + public string InstanceInfo { get; set; } + /// + /// 流程发起人ID + /// + public string UserId { get; set; } + + /// + /// 流程转移审核人 + /// + public string ToUserId { get; set; } + /// + /// 下一节点审核人 + /// + public Dictionary NextUsers { get; set; } + /// + /// 审批意见 + /// + public string Des { get; set; } + /// + /// 审核操作码 + /// + public string Code { get; set; } + /// + /// 审核操作名称 + /// + public string Name { get; set; } + /// + /// 盖章ID + /// + public string StampImg { get; set; } + /// + /// 盖章密码 + /// + public string StampPassWord { get; set; } + /// + /// 下一个审核节点 + /// + public string NextId { get; set; } + /// + /// 是否是子流程进程1是0不是 + /// + public int IsChild { get; set; } + /// + /// 父流程的发起子流程的节点Id + /// + public string ParentNodeId { get; set; } + /// + /// 流程进程父进程任务主键 + /// + public string ParentTaskId { get; set; } + /// + /// 流程进程父进程主键 + /// + public string ParentProcessId { get; set; } + } +} diff --git a/OpenAuth.App/BaseApp/WFProcess/Request/WFProcessPointDto.cs b/OpenAuth.App/BaseApp/WFProcess/Request/WFProcessPointDto.cs new file mode 100644 index 0000000..30aedea --- /dev/null +++ b/OpenAuth.App/BaseApp/WFProcess/Request/WFProcessPointDto.cs @@ -0,0 +1,25 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace OpenAuth.App.Request +{ + public class WFProcessPointDto + { + public List List { get; set; } + } + + public class WFProcessPoint + { + /// + /// 流程节点 + /// + public string UnitId { get; set; } + /// + /// 指派的审核人 + /// + public string UserIds { get; set; } + } +} diff --git a/OpenAuth.App/BaseApp/WFProcess/Request/WFProcessSearchDto.cs b/OpenAuth.App/BaseApp/WFProcess/Request/WFProcessSearchDto.cs new file mode 100644 index 0000000..35c1bae --- /dev/null +++ b/OpenAuth.App/BaseApp/WFProcess/Request/WFProcessSearchDto.cs @@ -0,0 +1,71 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace OpenAuth.App.Request +{ + public class WFProcessSearchDto + { + /// + /// 查询关键字 + /// + public string Keyword { get; set; } + + /// + /// 开始日期 + /// + public DateTime? StartDate { get; set; } + + /// + /// 结束流程 + /// + public DateTime? EndDate { get; set; } + + /// + /// 流程模板编码 + /// + public string Code { get; set; } + + /// + /// 类型 1 完成 2 未完成 3 作废 + /// + public int Type { get; set; } + + /// + /// 案件编号 + /// + public string CaseNo { get; set; } + + /// + /// 案件类型 + /// + public string TypeId { get; set; } + + /// + /// 案件名称 + /// + public string CaseName { get; set; } + + /// + /// 是否退回 + /// + public int? IsDrawback { get; set; } + + /// + /// 是否违法 + /// + public int? IsIllegal { get; set; } + + /// + /// 是否核销 + /// + public int? IsVerification { get; set; } + + /// + /// 案件描述 + /// + public string CaseDescription { get; set; } + } +} \ No newline at end of file diff --git a/OpenAuth.App/BaseApp/WFProcess/Response/BPMNWFTaskDto.cs b/OpenAuth.App/BaseApp/WFProcess/Response/BPMNWFTaskDto.cs new file mode 100644 index 0000000..4541e16 --- /dev/null +++ b/OpenAuth.App/BaseApp/WFProcess/Response/BPMNWFTaskDto.cs @@ -0,0 +1,48 @@ +using OpenAuth.Repository.Domain; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace OpenAuth.App.Response +{ + public class BPMNWFTaskDto + { + public string FlowContent { get; set; } + public FlowUnit FlowViewer { get; set; } + + public WFTask Task { get; set; } + /// + /// 流程进程 + /// + public WFProcess Process { get; set; } + /// + /// 流程模板信息 + /// + public WFScheme Scheme { get; set; } + /// + /// 审核日志 + /// + public IEnumerable Logs { get; set; } + /// + /// 任务列表 + /// + public IEnumerable Tasks { get; set; } + } + + + public class FlowUnit + { + //完成节点id集合 + public List FinishedTaskSet { get; set; } + // 完成线条节点id集合 + public List FinishedSequenceFlowSet { get; set; } + // 当前节点id集合 + public List UnfinishedTaskSet { get; set; } + // 拒绝节点id集合(暂无示例) + public List RejectedTaskSet { get; set; } + } + + +} diff --git a/OpenAuth.App/BaseApp/WFProcess/WFAuditor.cs b/OpenAuth.App/BaseApp/WFProcess/WFAuditor.cs new file mode 100644 index 0000000..f153b3b --- /dev/null +++ b/OpenAuth.App/BaseApp/WFProcess/WFAuditor.cs @@ -0,0 +1,47 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace OpenAuth.App +{ + public class WFAuditor + { + /// + /// 设置Id 1.上一级 2.上二级 3.上三级 4.上四级 5.上五级 6.下一级 7.下二级 8.下三级 9.下四级 10.下五级 + /// + public string Id { get; set; } + /// + /// 审核人员类型 1.岗位 2.角色 3.用户 4.上下级 5.节点执行人 6.数据库表字段 7.执行sql + /// + public string Type { get; set; } + /// + /// 名称 + /// + public string Name { get; set; } + /// + /// 条件 1.同一个部门 2.同一个公司 3.发起人上级 4.发起人下级 + /// + public string Condition { get; set; } + + /// + /// 数据库编码 + /// + public string DbCode { get; set; } + /// + /// 对应表 + /// + public string Table { get; set; } + /// + /// 关联字段 + /// + public string Rfield { get; set; } + /// + /// 审核人字段 + /// + public string AuditorField { get; set; } + + public string Sql { get; set; } + } +} diff --git a/OpenAuth.App/BaseApp/WFProcess/WFCondition.cs b/OpenAuth.App/BaseApp/WFProcess/WFCondition.cs new file mode 100644 index 0000000..842a0b7 --- /dev/null +++ b/OpenAuth.App/BaseApp/WFProcess/WFCondition.cs @@ -0,0 +1,55 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace OpenAuth.App +{ + public class WFCondition + { + /// + /// 流程条件编码 + /// + public string Code { get; set; } + /// + /// 条件名称 + /// + public string Name { get; set; } + /// + /// 类型 1比较字段 2 sql语句 + /// + public string Type { get; set; } + /// + /// 数据库 + /// + public string DbCode { get; set; } + /// + /// 数据库表 + /// + public string Table { get; set; } + /// + /// 流程关联字段 + /// + public string Rfield { get; set; } + /// + /// 比较字段 + /// + public string Cfield { get; set; } + /// + /// 比较类型 1等于 2不等于 3大于 4大于等于 5小于 6小于等于 7包含 8不包含 9包含于 10不包含于 + /// + public string CompareType { get; set; } + /// + /// 比较值 + /// + public string Value { get; set; } + /// + /// sql语句 + /// + public string Sql { get; set; } + + + + } +} diff --git a/OpenAuth.App/BaseApp/WFProcess/WFEngine.cs b/OpenAuth.App/BaseApp/WFProcess/WFEngine.cs new file mode 100644 index 0000000..002b44c --- /dev/null +++ b/OpenAuth.App/BaseApp/WFProcess/WFEngine.cs @@ -0,0 +1,1106 @@ +using Infrastructure; +using Infrastructure.Extensions; +using Newtonsoft.Json; +using OpenAuth.App.Config; +using OpenAuth.Repository.Domain; +using SqlSugar; +using StackExchange.Redis; +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace OpenAuth.App +{ + public class WFEngine : IWFEngine + { + ISqlSugarClient client; + //private RepositoryFactory dbFactory { get; set; } + /// + /// 流程配置信息 + /// + private WFEngineConfig _config { get; set; } + /// + /// 流程模板 + /// + private WorkFlow.WFScheme _wfScheme { get; set; } + /// + /// 流程单元对应字典 + /// + private Dictionary _dicUnit { get; set; } + /// + /// 开始节点 + /// + private WFUnit _startNode { get; set; } + /// + /// 线条集合 + /// + private List _lines { get; set; } + #region 构造函数 + /// + /// 构造方法 + /// + /// 流程参数 + public WFEngine(WFEngineConfig config, ISqlSugarClient sqlSugarClient) + { + // 初始化模板数据 + _config = config; + _wfScheme = ToObject(_config.Params.Scheme); + //_wfScheme = Newtonsoft.Json.JsonConvert.DeserializeObject(_config.Params.Scheme); + //_wfScheme = ExtensionsJson.ToObject(_config.Params.Scheme); + + _dicUnit = new Dictionary(); + _lines = new List(); + + foreach (var unit in _wfScheme.WfData) + { + if (!_dicUnit.ContainsKey(unit.Id)) + { + _dicUnit.Add(unit.Id, unit); + } + else + { + continue; + } + + if (unit.Type == NodeTypeEnum.startEvent.GetDescription()) + { + _startNode = unit; + } + else if (unit.Type == NodeTypeEnum.myline.GetDescription()) + { + _lines.Add(unit); + } + } + client = sqlSugarClient; + //dbFactory = new RepositoryFactory(); + } + #endregion + + public T ToObject(string Json) + { + return (T)((Json == null) ? ((object)default(T)) : ((object)JsonConvert.DeserializeObject(Json))); + } + /// + /// 流程运行参数 + /// + public WFEngineConfig Config => _config; + /// + /// 开始节点 + /// + public WFUnit StartNode => _startNode; + + /// + /// 流程发起用户 + /// + public WFUserInfo CreateUser => _config.Params.CreateUser; + /// + /// 流程发起用户 + /// + public WFUserInfo CurrentUser => _config.Params.CurrentUser; + + /// + /// 流程配置信息 + /// + public WorkFlow.WFScheme WFScheme => _wfScheme; + + /// + /// 获取流程单元信息 + /// + /// id + /// 流程单元信息 + public WFUnit GetNode(string id) + { + if (_dicUnit.ContainsKey(id)) + { + return _dicUnit[id]; + } + else + { + return null; + } + } + + /// + /// 获取下一节点集合 + /// + /// 开始节点 + /// 执行动作编码 + /// + public List GetNextUnits(string startId, string code = "") + { + List nextUnits = new List(); + + // 找到与当前节点相连的线条 + foreach (var line in _lines) + { + if (line.From == startId) + { + // 获取连接到开始节点的线条 + bool isOk = false; + if (string.IsNullOrEmpty(line.LineConditions)) + { + isOk = true; + } + else if (!string.IsNullOrEmpty(code)) + { + var codeList = line.LineConditions.Split(','); + foreach (string _code in codeList) + { + if (_code == code) + { + isOk = true; + break; + } + } + } + else + { //没有执行码就是全部执行 + isOk = true; + } + + + // 获取到流入节点 + if (isOk) + { + if (_dicUnit.ContainsKey(line.To) && nextUnits.Find(t => t.Id == line.To) == null) + { + nextUnits.Add(_dicUnit[line.To]); + } + } + } + } + return nextUnits; + } + /// + /// 获取下一节点集合 + /// + /// 开始节点 + /// 条件编码 + /// + public List GetNextUnits(string startId, List codeList) + { + List nextUnits = new List(); + + // 找到与当前节点相连的线条 + foreach (var line in _lines) + { + if (line.From == startId) + { + // 获取连接到开始节点的线条 + bool isOk = false; + if (codeList.Count > 0 && !string.IsNullOrEmpty(line.LineConditions)) + { + var codeList2 = line.LineConditions.Split(','); + foreach (string _code in codeList2) + { + if (codeList.FindIndex(t => t == _code) != -1) + { + isOk = true; + break; + } + } + } + + // 获取到流入节点 + if (isOk) + { + if (_dicUnit.ContainsKey(line.To) && nextUnits.Find(t => t.Id == line.To) == null) + { + nextUnits.Add(_dicUnit[line.To]); + } + } + } + } + return nextUnits; + } + + /// + /// 获取下一节点集合(流转条件为空的情况) + /// + /// 开始节点 + /// + public List GetNextUnitsNoSet(string startId) + { + List nextUnits = new List(); + + // 找到与当前节点相连的线条 + foreach (var line in _lines) + { + if (line.From == startId) + { + // 获取到流入节点 + if (string.IsNullOrEmpty(line.LineConditions)) + { + if (_dicUnit.ContainsKey(line.To) && nextUnits.Find(t => t.Id == line.To) == null) + { + nextUnits.Add(_dicUnit[line.To]); + } + } + } + } + return nextUnits; + } + /// + /// 获取上一单元ID列表 + /// + /// 当前节点Id + /// + public List GetPreUnitIds(string myUnitId) + { + List list = new List(); + // 找到与当前节点相连的线条 + foreach (var line in _lines) + { + if (line.To == myUnitId && list.Find(t => t == line.From) == null) + { + list.Add(line.From); + } + } + return list; + } + /// + /// 获取任务 + /// + /// 开始节点 + /// 执行动作编码 + /// 下一个指定节点 + /// + public async Task> GetTask(string startId, string code = "", string toUnitId = "") + { + bool isIsReject = code == "disagree"; + var nextUnits = GetNextUnits(startId, code); + + if (!string.IsNullOrEmpty(toUnitId)) + { + nextUnits = nextUnits.FindAll(t => t.Type == "bpmn:ScriptTask");// 只保留脚本节点 + nextUnits.Add(GetNode(toUnitId)); + } + + // 如果无法获取下一个审核任务且是驳回操作,就返回到之前流转过来的任务(除去通过驳回流转过来的)(需要过滤掉脚本节点) + if (nextUnits.FindAll(t => t.Type != "bpmn:ScriptTask").Count == 0 && isIsReject) + { + // 获取当前节点的 + var prevUnitId = await Config.GetPrevUnitId(Config.Params.ProcessId, startId); + if (!string.IsNullOrEmpty(prevUnitId)) + { + nextUnits.Add(GetNode(prevUnitId)); + } + } + + var list = await GetTask(nextUnits, isIsReject, startId); + foreach (var item in list) + { + item.PrevUnitId = startId; + } + return list; + } + + + + /// + /// 获取任务 + /// + /// 下一个节点集合 + /// 是否驳回 + /// 开始节点 + /// + public async Task> GetTask(List nextUnits, bool isReject, string startId) + { + WFUnit startUnit = _dicUnit[startId]; + + if (startUnit.Type == NodeTypeEnum.startEvent.GetDescription() && string.IsNullOrEmpty(startUnit.Name)) + { + startUnit.Name = "开始节点"; + } + + List taskList = new List(); + foreach (var unit in nextUnits) + { + switch (unit.Type) + { + //case "startEvent":// 开始节点 + case "bpmn:StartEvent":// 开始节点 + taskList.Add(GetStartTask(unit, isReject, startId, startUnit.Name)); + break; + //case "gatewayAnd": + case "bpmn:ParallelGateway": + // 并行网关会等待所有分支汇入才往下执行,所有出口分支都会被执行 + // 判断是否所有分支都被执行了 + // 1.或去此节点所有上节点 + // 2.生成一个等待任务 + // 3.获取所有该节点当前等待任务 + // 4.判断是否完成获取下一节点 + var preIdList = GetPreUnitIds(unit.Id); + if (preIdList.Count <= 1) + { + taskList.AddRange(await GetTask(unit.Id)); + } + else + { + var awaitTaskList = await Config.GetAwaitTaskList(Config.Params.ProcessId, unit.Id); + var awaitTaskList2 = awaitTaskList.FindAll(t => t.PrevUnitId != startId); // 排除当前等待任务的所有等待任务 + if (awaitTaskList2.Count + 1 >= preIdList.Count) + { + // 如果所有支路都被执行了就生成一个取消所有等待任务的命令 + taskList.Add(GetDeleteAwaitTask(unit, isReject, startId, startUnit.Name)); + taskList.AddRange(await GetTask(unit.Id)); + } + else + { + taskList.Add(GetAwaitTask(unit, isReject, startId, startUnit.Name)); + } + } + break; + //case "gatewayXor": + case "bpmn:ExclusiveGateway": + // 排他网关不会等待所有分支汇入才往下执行,只要有分支汇入就会往下执行,出口分支只会执行一条(条件为true,如果多条出口分支条件为true也执行一条) + // 1.获取当前节点的true条件,如果成立条件为0,就执行人默认没有条件的线条 + // 2.获取下一个节点 + var conditionCodes = await GetConditionIds(unit.Conditions); + var nextUnits2 = GetNextUnits(unit.Id, conditionCodes); + if (nextUnits2.Count == 0) + { + nextUnits2 = GetNextUnitsNoSet(unit.Id); + } + if (nextUnits2.Count == 0) + { + throw new Exception(string.Format("无法获取下一节点【{0}】", unit.Id)); + } + else + { + var nextUnits3 = new List(); + nextUnits3.Add(nextUnits2[0]); + taskList.AddRange(await GetTask(nextUnits3, isReject, unit.Id)); + } + break; + //case "gatewayInclusive": + case "bpmn:InclusiveGateway": + // 包容网关会等待所有分支汇入才往下执行,出口分支能执行多条(条件为true) + bool isNext = false; + var preIdList2 = GetPreUnitIds(unit.Id); + if (preIdList2.Count <= 1) + { + isNext = true; + } + else + { + + var awaitTaskList = await Config.GetAwaitTaskList(Config.Params.ProcessId, unit.Id); + var awaitTaskList2 = awaitTaskList.FindAll(t => t.PrevUnitId != startId); // 排除当前等待任务的所有等待任务 + if (awaitTaskList2.Count + 1 >= preIdList2.Count) + { + // 如果所有支路都被执行了就生成一个取消所有等待任务的命令 + taskList.Add(GetDeleteAwaitTask(unit, isReject, startId, startUnit.Name)); + isNext = true; + } + else + { + taskList.Add(GetAwaitTask(unit, isReject, startId, startUnit.Name)); + } + } + + if (isNext) + { + var conditionCodes2 = await GetConditionIds(unit.Conditions); + var nextUnits3 = GetNextUnits(unit.Id, conditionCodes2); + nextUnits3.AddRange(GetNextUnitsNoSet(unit.Id)); + taskList.AddRange(await GetTask(nextUnits3, isReject, unit.Id)); + } + break; + //case "userTask":// 审批节点 + case "bpmn:Task":// 审批节点 + // 1.获取节点上一次的请求人 + var preAuditUsers = await Config.GetPrevTaskUserList(Config.Params.ProcessId, unit.Id); + if (preAuditUsers.FindIndex(t => t.State == 1) != -1) + { + // 如果此节点被激活不需要审核 + break; + } + + + var auditUsers = new List(); + if (preAuditUsers.Count == 0) + { + // 表示此节点未被处理过 + // 获取审核人 + if (Config.Params.NextUsers != null && Config.Params.NextUsers.ContainsKey(unit.Id)) + { + var auditUserList = new List(); + var strAuditUser = Config.Params.NextUsers[unit.Id]; + var strAuditUserList = strAuditUser.Split(","); + foreach (var id in strAuditUserList) + { + auditUserList.Add(new WFAuditor() + { + Type = "3", + Id = id + }); + } + + auditUsers = await Config.GetUserList(auditUserList, CreateUser, Config.Params.ProcessId, _startNode); + } + else + { + auditUsers = await Config.GetUserList(unit.AuditUsers, CreateUser, Config.Params.ProcessId, _startNode); + } + + // 添加传阅任务,节点第一次执行的时候触发 + var lookUsers = await Config.GetUserList(unit.LookUsers, CreateUser, Config.Params.ProcessId, _startNode); + taskList.AddRange(GetLookUserTaskList(unit, lookUsers, isReject, startId, startUnit.Name, Guid.NewGuid().ToString())); + } + else + { + // 添加一条任务用于更新之前任务状态(将之前的任务设置成不是最近的一次任务) + taskList.Add(GetUpdateTask(unit)); + } + + // 表示找不到审核人 + if (preAuditUsers.Count == 0 && auditUsers.Count == 0) + { + switch (unit.NoAuditor) + { + case "1": + taskList.AddRange(GetUserTaskList(unit, await Config.GetSystemUserList(), isReject, startId, startUnit.Name, Guid.NewGuid().ToString())); + break; + case "2": + // 流转到下一个节点 + taskList.Add(GetSkipTask(unit, isReject, startId, startUnit.Name)); + taskList.AddRange(await GetTask(unit.Id)); + break; + case "3": + throw new Exception(string.Format("【{0}{1}】找不到审核人,无法提交", unit.Name, unit.Id)); + } + } + + // 1.需要先判断是否是会签 + if (unit.IsCountersign) + { + // 之前有人处理过 + if (preAuditUsers.Count > 0) + { + // 驳回处理,重新开启审核 + if (isReject) + { + taskList.AddRange(GetCountersignTask(unit, preAuditUsers, isReject, startId, startUnit.Name, Guid.NewGuid().ToString(), unit.CountersignType)); + } + else + // 1.判断之前的会签策略是否通过 + if (await Config.IsCountersignAgree(Config.Params.ProcessId, unit.Id)) + { + // 判断自动同意规则,会签状态下只有3有效 + if (unit.AutoAgree.IndexOf("3") != -1) + { + taskList.AddRange(await GetTask(unit.Id));// 自动审核,流转到下一节点 + } + else + { + taskList.AddRange(GetCountersignTask(unit, preAuditUsers, isReject, startId, startUnit.Name, Guid.NewGuid().ToString(), unit.CountersignType)); + } + } + else + { + taskList.AddRange(GetCountersignTask(unit, preAuditUsers, isReject, startId, startUnit.Name, Guid.NewGuid().ToString(), unit.CountersignType, unit.CountersignAgian)); + } + } + // 第一次处理 + else + { + taskList.AddRange(GetCountersignTask(unit, auditUsers, isReject, startId, startUnit.Name, Guid.NewGuid().ToString(), unit.CountersignType)); + } + } + else + { + // 之前有人处理过 + if (preAuditUsers.Count > 0) + { + if (IsAtuoAgree(preAuditUsers, unit.AutoAgree.Split(','), isReject)) + { + taskList.AddRange(await GetTask(unit.Id, "agree")); + } + else + { + // 判断是否有完成任务的人 + if (preAuditUsers.FindIndex(t => t.State == 3) != -1) + { + taskList.AddRange(GetUserTaskList(unit, preAuditUsers.FindAll(t => t.State == 3), isReject, startId, startUnit.Name, Guid.NewGuid().ToString())); + } + else + { + taskList.AddRange(GetUserTaskList(unit, preAuditUsers, isReject, startId, startUnit.Name, Guid.NewGuid().ToString())); + } + } + } + else + { + if (IsAtuoAgree(auditUsers, unit.AutoAgree.Split(','), isReject)) + { + taskList.Add(GetAutoSkipTask(unit, isReject, startId, startUnit.Name));// 作为日志记录 + taskList.AddRange(await GetTask(unit.Id, "agree")); + } + else + { + taskList.AddRange(GetUserTaskList(unit, auditUsers, isReject, startId, startUnit.Name, Guid.NewGuid().ToString())); + } + } + } + break; + case "bpmn:ScriptTask":// 脚本节点 + taskList.Add(GetScriptTaskList(unit, isReject, startId, startUnit.Name, Guid.NewGuid().ToString())); + taskList.AddRange(await GetTask(unit.Id)); + break; + //case "subprocess":// 暂时不支持 + case "bpmn:SubProcess": + taskList.Add(GetSubprocessTask(unit, startId, startUnit.Name)); + break; + //case "endEvent":// 结束节点 + case "bpmn:EndEvent":// 结束节点 + taskList.Add(GetEndTask(unit, startId, startUnit.Name)); + break; + } + } + + return taskList; + } + + + #region 私有方法 + /// + /// 获取开始任务 + /// + /// + /// + /// + /// + /// + private WorkFlow.WFTask GetStartTask(WFUnit unit, bool IsReject, string preUnitId, string preUnitName) + { + WorkFlow.WFTask task = new WorkFlow.WFTask() + { + UnitId = unit.Id, + Name = "重新创建", + Token = Guid.NewGuid().ToString(), + Type = 4, + PrevUnitId = preUnitId, + PrevUnitName = preUnitName, + IsReject = IsReject, + MessageType = unit.MessageType, + User = _config.Params.CreateUser + }; + return task; + } + + private WorkFlow.WFTask GetEndTask(WFUnit unit, string preUnitId, string preUnitName) + { + WorkFlow.WFTask task = new WorkFlow.WFTask() + { + UnitId = unit.Id, + Name = "流程结束", + Token = Guid.NewGuid().ToString(), + Type = 100, + PrevUnitId = preUnitId, + PrevUnitName = preUnitName, + }; + return task; + } + /// + /// 获取一个等待任务 + /// + /// + /// + /// + /// + /// + private WorkFlow.WFTask GetAwaitTask(WFUnit unit, bool IsReject, string preUnitId, string preUnitName) + { + WorkFlow.WFTask task = new WorkFlow.WFTask() + { + UnitId = unit.Id, + Name = "等待其它支路完成", + Type = 21, + PrevUnitId = preUnitId, + PrevUnitName = preUnitName, + IsReject = IsReject + }; + + return task; + } + + /// + /// 获取一个更新任务 + /// + /// + /// + private WorkFlow.WFTask GetUpdateTask(WFUnit unit) + { + WorkFlow.WFTask task = new WorkFlow.WFTask() + { + UnitId = unit.Id, + Type = 26, + }; + + return task; + } + + /// + /// 获取一个找不到审核人直接跳过任务 + /// + /// + /// + /// + /// + /// + private WorkFlow.WFTask GetSkipTask(WFUnit unit, bool IsReject, string preUnitId, string preUnitName) + { + WorkFlow.WFTask task = new WorkFlow.WFTask() + { + UnitId = unit.Id, + Name = "当前任务找不到审核人直接跳过", + Type = 23, + PrevUnitId = preUnitId, + PrevUnitName = preUnitName, + IsReject = IsReject + }; + + return task; + } + + /// + /// 获取一个找不到审核人直接跳过任务 + /// + /// + /// + /// + /// + /// + private WorkFlow.WFTask GetAutoSkipTask(WFUnit unit, bool IsReject, string preUnitId, string preUnitName) + { + WorkFlow.WFTask task = new WorkFlow.WFTask() + { + UnitId = unit.Id, + Name = "自动审核规则跳过", + Type = 24, + PrevUnitId = preUnitId, + PrevUnitName = preUnitName, + IsReject = IsReject + }; + + return task; + } + + + /// + /// 获取一个取消等待任务 + /// + /// + /// + /// + /// + /// + private WorkFlow.WFTask GetDeleteAwaitTask(WFUnit unit, bool IsReject, string preUnitId, string preUnitName) + { + WorkFlow.WFTask task = new WorkFlow.WFTask() + { + UnitId = unit.Id, + Name = "取消等待任务", + Type = 22, + PrevUnitId = preUnitId, + PrevUnitName = preUnitName, + IsReject = IsReject + }; + + return task; + } + /// + /// 获取审批任务 + /// + /// + /// + /// + /// + /// + /// + /// + private List GetUserTaskList(WFUnit unit, List userList, bool isReject, string preUnitId, string preUnitName, string token) + { + List list = new List(); + int type = 1; + if (unit!=null&&unit.IsSingleTask) + { + type = 7; + } + foreach (var user in userList) + { + WorkFlow.WFTask task = new WorkFlow.WFTask() + { + UnitId = unit.Id, + Name = unit.Name, + Type = type, + PrevUnitId = preUnitId, + PrevUnitName = preUnitName, + IsReject = isReject, + Token = token, + MessageType = unit.MessageType, + + // 超时设置 + IsOvertimeMessage = unit.IsOvertimeMessage, + OvertimeMessageStart = unit.OvertimeMessageStart, + OvertimeMessageInterval = unit.OvertimeMessageInterval, + OvertimeGo = unit.OvertimeGo, + OvertimeMessageType = unit.OvertimeMessageType, + + IsBatchAudit = unit.IsBatchAudit, + // 处理人 + User = user + + }; + if (string.IsNullOrEmpty(task.Name)) + { + task.Name = "审核处理"; + } + + list.Add(task); + } + + + return list; + } + + + /// + /// 获取传阅任务 + /// + /// + /// + /// + /// + /// + /// + /// + private List GetLookUserTaskList(WFUnit unit, List userList, bool isReject, string preUnitId, string preUnitName, string token) + { + List list = new List(); + foreach (var user in userList) + { + WorkFlow.WFTask task = new WorkFlow.WFTask() + { + UnitId = unit.Id, + Name = unit.Name, + Type = 2, + PrevUnitId = preUnitId, + PrevUnitName = preUnitName, + IsReject = isReject, + Token = token, + MessageType = unit.MessageType, + + // 处理人 + User = user + + }; + if (string.IsNullOrEmpty(task.Name)) + { + task.Name = "查看"; + } + + list.Add(task); + } + + + return list; + } + + /// + /// 获取会签审批任务 + /// + /// + /// + /// + /// + /// + /// + /// 审核方式 1.并行 2.串行 + /// 再次审核 1.已同意不需要审核 2.已同意需要审核 + /// + private List GetCountersignTask(WFUnit unit, List userList, bool isReject, string preUnitId, string preUnitName, string token, string countersignType, string countersignAgian = "2") + { + List list = new List(); + int num = 1; + foreach (var user in userList) + { + if (countersignAgian == "1" && user.IsAgree) + { + continue; + } + + user.IsAwait = countersignType == "1" ? false : true; + user.Sort = num; + if (num == 1) + { + user.IsAwait = false; + } + WorkFlow.WFTask task = new WorkFlow.WFTask() + { + UnitId = unit.Id, + Name = unit.Name, + Type = 5, + PrevUnitId = preUnitId, + PrevUnitName = preUnitName, + IsReject = isReject, + Token = token, + MessageType = unit.MessageType, + + // 超时设置 + IsOvertimeMessage = unit.IsOvertimeMessage, + OvertimeMessageStart = unit.OvertimeMessageStart, + OvertimeMessageInterval = unit.OvertimeMessageInterval, + OvertimeGo = unit.OvertimeGo, + OvertimeMessageType = unit.OvertimeMessageType, + + IsBatchAudit = unit.IsBatchAudit, + // 处理人 + User = user, + + + }; + if (string.IsNullOrEmpty(task.Name)) + { + task.Name = "审核处理"; + } + + list.Add(task); + + num++; + } + + + return list; + } + + /// + /// 获取脚本执行任务 + /// + /// + /// + /// + /// + /// + /// + private WorkFlow.WFTask GetScriptTaskList(WFUnit unit, bool isReject, string preUnitId, string preUnitName, string token) + { + WorkFlow.WFTask task = new WorkFlow.WFTask() + { + UnitId = unit.Id, + Name = unit.Name, + Type = 10, + PrevUnitId = preUnitId, + PrevUnitName = preUnitName, + IsReject = isReject, + Token = token, + + ExecuteType = unit.ExecuteType, + SqlDb = unit.SqlDb, + SqlStr = unit.SqlStr, + SqlStrRevoke = unit.SqlStrRevoke, + ApiUrl = unit.ApiUrl, + ApiUrlRevoke = unit.ApiUrlRevoke, + Ioc = unit.Ioc, + IocRevoke = unit.IocRevoke, + + + }; + if (string.IsNullOrEmpty(task.Name)) + { + task.Name = "脚本执行"; + } + + + return task; + } + + + /// + /// 获取成立的条件ID + /// + /// 条件列表 + /// + private async Task> GetConditionIds(List conditions) + { + List list = new List(); + foreach (var condition in conditions) + { + //获取数据id + var process = await client.Queryable().FirstAsync(a => a.Id == _config.Params.ProcessId); + WFInstanceInfo instanceInfo = null; + if (process != null) + { + // instanceInfo = Newtonsoft.Json.JsonConvert.DeserializeObject(process.InstanceInfo); + instanceInfo = process.InstanceInfo; + } + + if (condition.Type == "1")// 字段比较 + { + string sql = string.Format("select \"{0}\" from \"{1}\" where \"{2}\" = @processId ", condition.Cfield, condition.Table, condition.Rfield, condition.Rfield); + //var dt = await dbFactory.BaseRepository(condition.DbCode).FindTable(sql, new { processId = _config.Params.ProcessId }); + var pars = client.Ado.GetParameters(new { processId = instanceInfo.pkeyValue }); + var dt = await client.Ado.GetDataTableAsync(sql, pars); + if (dt.Rows.Count > 0) + { + var value = dt.Rows[0][condition.Cfield.ToLower()].ToString(); + + switch (condition.CompareType) + { + case "1": + if (value == condition.Value) + { + list.Add(condition.Code); + } + break; + case "2": + if (value != condition.Value) + { + list.Add(condition.Code); + } + break; + case "3": + if (Convert.ToDecimal(value) > Convert.ToDecimal(condition.Value)) + { + list.Add(condition.Code); + } + break; + case "4": + if (Convert.ToDecimal(value) >= Convert.ToDecimal(condition.Value)) + { + list.Add(condition.Code); + } + break; + case "5": + if (Convert.ToDecimal(value) < Convert.ToDecimal(condition.Value)) + { + list.Add(condition.Code); + } + break; + case "6": + if (Convert.ToDecimal(value) <= Convert.ToDecimal(condition.Value)) + { + list.Add(condition.Code); + } + break; + case "7": + if (value.IndexOf(condition.Value) != -1) + { + list.Add(condition.Code); + } + break; + case "8": + if (value.IndexOf(condition.Value) == -1) + { + list.Add(condition.Code); + } + break; + case "9": + if (condition.Value.IndexOf(value) != -1) + { + list.Add(condition.Code); + } + break; + case "10": + if (condition.Value.IndexOf(value) == -1) + { + list.Add(condition.Code); + } + break; + } + } + + } + else + {// sql语句 + //var dt = await dbFactory.BaseRepository(condition.DbCode).FindTable(condition.Sql, new { processId = _config.Params.ProcessId, userId = CreateUser.Id, userAccount = CreateUser.Account, companyId = CreateUser.CompanyId, departmentId = CreateUser.DepartmentId }); + //var pars = client.Ado.GetParameters(new { processId = _config.Params.ProcessId, userId = CreateUser.Id, userAccount = CreateUser.Account, companyId = CreateUser.CompanyId, departmentId = CreateUser.DepartmentId }); + var pars = client.Ado.GetParameters(new { processId = instanceInfo.pkeyValue, userId = CreateUser.Id, userAccount = CreateUser.Account, companyId = CreateUser.CompanyId, departmentId = CreateUser.DepartmentId }); + var dt = await client.Ado.GetDataTableAsync(condition.Sql, pars); + + if (dt.Rows.Count > 0) + { + list.Add(condition.Code); + } + } + } + + return list; + } + /// + /// 判断是否自动同意 + /// + /// 审核处理人 + /// 自动同意策略1.处理人就是提交人 2.处理人和上一步的处理人相同 3.处理人审批过 + /// 是否是驳回 + /// + private bool IsAtuoAgree(List auditUsers, string[] atuoAgrees, bool isReject) + { + if (isReject) + { + return false; + } + + bool res = false; + if (auditUsers.Count == 0 || atuoAgrees.Length == 0) + { + return res; + } + + foreach (var item in atuoAgrees) + { + switch (item) + { + case "1": + if (auditUsers.FindIndex(t => t.Id == CreateUser.Id) != -1) + { + res = true; + } + break; + case "2": + if (auditUsers.FindIndex(t => t.Id == CurrentUser.Id) != -1) + { + res = true; + } + break; + case "3": + if (auditUsers.FindIndex(t => t.IsAgree) != -1 && !isReject) + { + res = true; + } + break; + } + } + return res; + } + + + /// + /// 获取子流程任务 + /// + /// + /// + /// + /// + private WorkFlow.WFTask GetSubprocessTask(WFUnit unit, string preUnitId, string preUnitName) + { + WorkFlow.WFTask task = new WorkFlow.WFTask() + { + Type = 3, + UnitId = unit.Id, + Name = "子流程", + Token = Guid.NewGuid().ToString(), + PrevUnitId = preUnitId, + PrevUnitName = preUnitName, + ChildSchemeInfoId = unit.WfschemeId, + User = _config.Params.CreateUser + }; + return task; + } + #endregion + } +} diff --git a/OpenAuth.App/BaseApp/WFProcess/WFProcessApp.cs b/OpenAuth.App/BaseApp/WFProcess/WFProcessApp.cs new file mode 100644 index 0000000..e0e2253 --- /dev/null +++ b/OpenAuth.App/BaseApp/WFProcess/WFProcessApp.cs @@ -0,0 +1,3161 @@ +using ce.autofac.extension; +using Infrastructure; +using Infrastructure.Extensions; +using Infrastructure.Helpers; +using Microsoft.Extensions.Configuration; +using OpenAuth.App.Base; +using OpenAuth.App.BasicQueryService; +using OpenAuth.App.Config; +using OpenAuth.App.Interface; +using OpenAuth.App.Request; +using OpenAuth.Repository; +using OpenAuth.Repository.Core; +using OpenAuth.Repository.Domain; +using SqlSugar; +using System.Data; +using System.Linq; +using Newtonsoft.Json; +using OpenAuth.App.BaseApp.Jobs; +using System.Text; +using DocumentFormat.OpenXml.Spreadsheet; +using Infrastructure.Utilities; +using OpenAuth.App.BaseApp.Base; +using System.Diagnostics; +using NPOI.Util; + +namespace OpenAuth.App +{ + public partial class WFProcessApp : SqlSugarBaseApp + { + ISqlSugarClient client; + UserManager userManager; + PositionManager positionManager; + IConfiguration _configuration; + OpenJobApp _job; + public WFProcessApp(SqlSugar.ISugarUnitOfWork unitWork, + SqlSugar.ISimpleClient repository, + IAuth auth, + UserManager userManager, + OpenJobApp job, + PositionManager positionManager, + IConfiguration _configuration + ) : base(unitWork, repository, auth) + { + client = base.Repository.AsSugarClient(); + this.userManager = userManager; + this.positionManager = positionManager; + this._configuration = _configuration; + _job = job; + } + + #region 获取数据 + /// + /// 获取流程列表 + /// + /// 分页参数 + /// 查询参数 + /// + public async Task>> GetPageList(PageReq pageReq, WFProcessSearchDto searchParams) + { + RefAsync totalCount = 0; + + var exp = Expressionable.Create() + .AndIF(!string.IsNullOrEmpty(searchParams.Keyword), t => t.Title.Contains(searchParams.Keyword) || t.SchemeName.Contains(searchParams.Keyword)) + .AndIF(!string.IsNullOrEmpty(searchParams.Code), t => t.SchemeCode == searchParams.Code) + .AndIF(searchParams.StartDate != null && searchParams.EndDate != null, t => t.CreateDate >= searchParams.StartDate && t.CreateDate <= searchParams.EndDate); + + if (searchParams.Type == 3) + { + exp = exp.And(t => t.EnabledMark == 3); + } + else + { + exp = exp.And(t => t.EnabledMark == 1); + + if (searchParams.Type == 1) + { + exp = exp.And(t => t.IsFinished == 1); + } + else + { + exp = exp.And(t => t.IsFinished == 0); + } + } + + var list = await client.Queryable() + .Where(exp.ToExpression()) + .OrderByDescending(t => t.CreateDate) + .ToPageListAsync(pageReq.page, pageReq.limit, totalCount); + + return new PageInfo> + { + Items = list, + Total = totalCount + }; + } + /// + /// 获取我的流程 + /// + /// 分页参数 + /// 查询参数 + /// 1正常2草稿3作废 + /// + public async Task>> GetMyPageList(PageReq pageReq, WFProcessSearchDto searchParams, int type) + { + RefAsync totalCount = 0; + + var userId = _auth.GetUserId(); + var expression = Expressionable.Create(); + expression = expression.And(t => t.CreateUserId == userId || t.UserId == userId); + + if (type == 2) + { + expression = expression.And(t => t.EnabledMark == 2); + } + else + { + expression = expression.And(t => t.EnabledMark != 2); + } + + if (!string.IsNullOrEmpty(searchParams.Keyword)) + { + expression = expression.And(t => t.Title.Contains(searchParams.Keyword) || t.SchemeName.Contains(searchParams.Keyword)); + } + if (!string.IsNullOrEmpty(searchParams.Code)) + { + expression = expression.And(t => t.SchemeCode == searchParams.Code); + } + if (searchParams.StartDate != null && searchParams.EndDate != null) + { + expression = expression.And(t => t.CreateDate >= searchParams.StartDate && t.CreateDate <= searchParams.EndDate); + } + + var list = await client.Queryable() + .Where(expression.ToExpression()) + .OrderByDescending(t => t.CreateDate) + .ToPageListAsync(pageReq.page, pageReq.limit, totalCount); + + return new PageInfo> + { + Items = list, + Total = totalCount + }; + } + /// + /// 获取流程进程实体 + /// + /// 主键 + /// + public async Task GetEntity(string keyValue) + { + return await base.Repository.GetByIdAsync(keyValue); + } + #endregion + + #region 保存更新删除 + /// + /// 删除流程进程实体 + /// + /// 流程进程主键 + public async Task DeleteEntity(string processId) + { + return await base.Repository.DeleteByIdAsync(processId); + } + #endregion + + #region 流程API + /// + /// 保存草稿 + /// + /// 流程进程主键 + /// 流程模板编码 + /// 创建人 + /// 父级流程进程主键 + /// 父级流程节点Id + /// 父级任务id + /// 是否为子流程 + /// 流程标题 + /// + public async Task> SaveDraft(string processId, string schemeCode, string userId, string instanceInfo, string parentProcessId, string parentNodeId, string parentTaskId, int isChild, string title = "") + { + var flag = false; + + var currentUser = _auth.GetCurrentUser().User; + + // 判断当前流程进程是否有保存过 + var processEntity = await GetEntity(processId); + if (processEntity == null) + { + // 创建草稿,已经存在不做处理 + var schemeInfo = await client.Queryable().FirstAsync(a => a.Code == schemeCode); + WFProcess wfProcessEntity = new WFProcess() + { + Id = processId, + SchemeCode = schemeCode, + SchemeId = schemeInfo.SchemeId, + SchemeName = schemeInfo.Name, + Title = title, + InstanceInfo = JsonConvert.DeserializeObject(instanceInfo), + EnabledMark = 2, + IsAgain = 0, + IsFinished = 0, + IsChild = isChild, + IsStart = 0, + ParentNodeId = parentNodeId, + ParentProcessId = parentProcessId, + ParentTaskId = parentTaskId, + CreateUserId = currentUser.Id.ToString(), + CreateUserName = currentUser.Name, + CreateUserAccount = currentUser.Account, + UserId = currentUser.Id.ToString() + }; + + if (string.IsNullOrEmpty(title)) + { + wfProcessEntity.Title = currentUser.Name + "-" + schemeInfo.Name; + } + + wfProcessEntity.CreateDate = DateTime.Now; + wfProcessEntity.UserId = currentUser.Id.ToString(); + flag = await base.Repository.InsertAsync(wfProcessEntity); + } + else if (!string.IsNullOrEmpty(title) && processEntity.Title != title) + { + processEntity.Title = title; + flag = await base.Repository.UpdateAsync(processEntity); + } + else + { + flag = true; + } + + return new Response + { + Result = flag, + Message = flag == true ? "success" : "error" + }; + } + /// + /// 保存草稿-新版用户查询 + /// + /// 流程进程主键 + /// 流程模板编码 + /// 创建人 + /// 父级流程进程主键 + /// 父级流程节点Id + /// 父级任务id + /// 是否为子流程 + /// 流程标题 + /// + public async Task> SaveDraftWithUser(string processId, string schemeCode, string userName, string instanceInfo, string parentProcessId, string parentNodeId, string parentTaskId, int isChild, string title = "") + { + var flag = false; + var currentUser = await client.Queryable().FirstAsync(r => r.Name == userName) + ?? new SysUser + { + Id = -1, + Account = Define.SYSTEM_USERNAME, + Name = "超级管理员", + }; + + // 判断当前流程进程是否有保存过 + var processEntity = await GetEntity(processId); + if (processEntity == null) + { + // 创建草稿,已经存在不做处理 + var schemeInfo = await client.Queryable().FirstAsync(a => a.Code == schemeCode); + WFProcess wfProcessEntity = new WFProcess() + { + Id = processId, + SchemeCode = schemeCode, + SchemeId = schemeInfo.SchemeId, + SchemeName = schemeInfo.Name, + Title = title, + InstanceInfo = JsonConvert.DeserializeObject(instanceInfo), + EnabledMark = 2, + IsAgain = 0, + IsFinished = 0, + IsChild = isChild, + IsStart = 0, + ParentNodeId = parentNodeId, + ParentProcessId = parentProcessId, + ParentTaskId = parentTaskId, + CreateUserId = currentUser?.Id.ToString(), + CreateUserName = currentUser?.Name, + CreateUserAccount = currentUser?.Account, + UserId = currentUser?.Id.ToString() + }; + + if (string.IsNullOrEmpty(title)) + { + wfProcessEntity.Title = currentUser.Name + "-" + schemeInfo.Name; + } + + wfProcessEntity.CreateDate = DateTime.Now; + wfProcessEntity.UserId = currentUser.Id.ToString(); + flag = await base.Repository.InsertAsync(wfProcessEntity); + } + else if (!string.IsNullOrEmpty(title) && processEntity.Title != title) + { + processEntity.Title = title; + flag = await base.Repository.UpdateAsync(processEntity); + } + + return new Response + { + Result = flag, + Message = flag == true ? "success" : "error" + }; + } + + /// + /// 删除草稿 + /// + /// 流程进程主键 + public async Task> DeleteDraft(string processId) + { + var flag = false; + var response = new Response(); + + var iWFEngine = await Bootstraper(string.Empty, processId, null); + + var scriptResult = await ExecuteScript(processId, "deleteDraft", iWFEngine.CreateUser, + iWFEngine.WFScheme.DeleteDraftType, + iWFEngine.WFScheme.DeleteDraftDbCode, + iWFEngine.WFScheme.DeleteDraftDbSQL, + iWFEngine.WFScheme.DeleteDraftUrl, + iWFEngine.WFScheme.DeleteDraftIOCName); + + if (scriptResult) + { + flag = await DeleteEntity(processId); + if (flag) + { + response.Result = true; + response.Message = "success"; + } + else + { + response.Result = false; + response.Message = "脚本执行成功,但流程信息删除失败"; + } + } + else + { + response.Result = false; + response.Message = "error"; + } + return response; + } + /// + /// 创建流程 + /// + /// 流程进程主键 + /// 流程模板编码 + /// 创建人 + /// 下一个节点审核人 + /// 流程标题 + /// + public async Task> Create(string processId, string schemeCode, string userId, Dictionary nextUsers, string InstanceInfo, string title = "") + { + var flag = false; + var userInfo = await GetUserInfo(userId); + var iWFEngine = await Bootstraper(schemeCode, processId, userInfo, nextUsers); + // 下一部需要执行的任务 + var taskList = await iWFEngine.GetTask(iWFEngine.StartNode.Id); + //获取当前流程信息 + var process = await GetEntity(processId); + // 处理任务并更新数据 + List myTaskList; + + using (var uwo = UnitWork.CreateContext()) + { + + var processEntity = GetWFProcessEntity(iWFEngine.Config.Params, title); + processEntity.InstanceInfo = JsonConvert.DeserializeObject(InstanceInfo); + if (string.IsNullOrEmpty(processEntity.Title)) + { + processEntity.Title = userInfo.Name + "-" + processEntity.SchemeName; + } + processEntity.CreateDate = DateTime.Now; + + if (taskList.FindIndex(t => t.Type == 100) != -1) + { + processEntity.IsFinished = 1; + if (process != null && process.IsChild == 1) + { + WFTask ts = new WFTask(); + await ChildrenEndTask(process.ParentProcessId, process.ParentNodeId, "", ts, ""); + } + } + + if (process != null && process.IsChild == 1) + { + var taskEntity = await client.Queryable().Where(a => a.Id == process.ParentTaskId).FirstAsync(); + if (taskEntity != null) + { + taskEntity.State = 3; + if (taskEntity.Type == 7) + { + var currentUser = _auth.GetCurrentUser().User; + taskEntity.UserId = currentUser.Id.ToString(); + taskEntity.UserName = currentUser.Name; + } + await uwo.WFTask.UpdateAsync(taskEntity); + } + } + + if (!string.IsNullOrEmpty(schemeCode)) + { + await uwo.WFProcess.InsertAsync(processEntity); + } + else + { + await uwo.WFProcess.UpdateAsync(processEntity); + } + + myTaskList = await ExecuteWFTaskEntity(taskList, iWFEngine, processId, "create", "create"); + foreach (var item in myTaskList) + { + if (item.Type == 7) + { + await uwo.WFTaskUser.InsertRangeAsync(item.WFTaskUser); + } + item.ProcessTitle = processEntity.Title; + await Add(item); + } + + WFTaskLog wfTaskLogEntity = new WFTaskLog() + { + OperationCode = "create", + OperationName = "提交流程", + TaskType = 0, + IsLast = 1, + Des = "提交流程", + ProcessId = processId, + UnitId = iWFEngine.StartNode.Id, + UnitName = string.IsNullOrEmpty(iWFEngine.StartNode.Name) ? "开始节点" : iWFEngine.StartNode.Name + }; + await AddLog(wfTaskLogEntity); + + flag = uwo.Commit(); + } + + var respone = new Response(); + + if (flag) + { + var scriptResults = new List(); + // 脚本执行 + var scriptTaskList = myTaskList.FindAll(t => t.Type == 10); + foreach (var item in scriptTaskList) + { + var result = await ExecuteScript(item, "create", processId, "", "",iWFEngine.CreateUser, iWFEngine, null,""); + scriptResults.Add(result); + } + + if (scriptResults.Contains(false)) + { + respone.Result = false; + respone.Message = "流程信息存储成功,但脚本执行失败"; + } + else + { + respone.Result = true; + respone.Message = "success"; + } + } + else + { + respone.Result = false; + respone.Message = "error"; + } + return respone; + } + /// + /// 创建流程 + /// + /// 流程进程主键 + /// 流程模板编码 + /// 创建人 + /// 下一个节点审核人 + /// 流程标题 + /// + public async Task> CreateWithUser(string processId, string schemeCode, string userName, Dictionary nextUsers, string InstanceInfo, string title = "") + { + var flag = false; + //var userInfo = await client.Queryable().FirstAsync(r => r.Name == userName); + var userInfo = await client.Queryable().FirstAsync(r => r.Name == userName) + ?? new SysUser + { + Id = -1, + Account = Define.SYSTEM_USERNAME, + Name = "超级管理员", + }; + var iWFEngine = await BootstraperWithUser(schemeCode, processId, userInfo, nextUsers); + // 下一部需要执行的任务 + var taskList = await iWFEngine.GetTask(iWFEngine.StartNode.Id); + //获取当前流程信息 + var process = await GetEntity(processId); + // 处理任务并更新数据 + List myTaskList; + + using (var uwo = UnitWork.CreateContext()) + { + + var processEntity = GetWFProcessEntity(iWFEngine.Config.Params, title); + processEntity.InstanceInfo = JsonConvert.DeserializeObject(InstanceInfo); + if (string.IsNullOrEmpty(processEntity.Title)) + { + processEntity.Title = userInfo.Name + "-" + processEntity.SchemeName; + } + processEntity.CreateDate = DateTime.Now; + + if (taskList.FindIndex(t => t.Type == 100) != -1) + { + processEntity.IsFinished = 1; + if (process != null && process.IsChild == 1) + { + WFTask ts = new WFTask(); + await ChildrenEndTask(process.ParentProcessId, process.ParentNodeId, "", ts, ""); + } + } + + if (process != null && process.IsChild == 1) + { + var taskEntity = await client.Queryable().Where(a => a.Id == process.ParentTaskId).FirstAsync(); + if (taskEntity != null) + { + taskEntity.State = 3; + if (taskEntity.Type == 7) + { + var currentUser = _auth.GetCurrentUser().User; + taskEntity.UserId = currentUser.Id.ToString(); + taskEntity.UserName = currentUser.Name; + } + await uwo.WFTask.UpdateAsync(taskEntity); + } + } + + if (!string.IsNullOrEmpty(schemeCode)) + { + await uwo.WFProcess.InsertAsync(processEntity); + } + else + { + await uwo.WFProcess.UpdateAsync(processEntity); + } + + myTaskList = await ExecuteWFTaskEntity(taskList, iWFEngine, processId, "create", "create"); + foreach (var item in myTaskList) + { + if (item.Type == 7) + { + await uwo.WFTaskUser.InsertRangeAsync(item.WFTaskUser); + } + item.ProcessTitle = processEntity.Title; + await Add(item); + } + + WFTaskLog wfTaskLogEntity = new WFTaskLog() + { + OperationCode = "create", + OperationName = "提交流程", + TaskType = 0, + IsLast = 1, + Des = "提交流程", + ProcessId = processId, + UnitId = iWFEngine.StartNode.Id, + UnitName = string.IsNullOrEmpty(iWFEngine.StartNode.Name) ? "开始节点" : iWFEngine.StartNode.Name + }; + await AddLog(wfTaskLogEntity); + + flag = uwo.Commit(); + } + + var respone = new Response(); + + if (flag) + { + var scriptResults = new List(); + // 脚本执行 + var scriptTaskList = myTaskList.FindAll(t => t.Type == 10); + foreach (var item in scriptTaskList) + { + var result = await ExecuteScript(item, "create", processId, "", "",iWFEngine.CreateUser, iWFEngine, null, ""); + scriptResults.Add(result); + } + + if (scriptResults.Contains(false)) + { + respone.Result = false; + respone.Message = "流程信息存储成功,但脚本执行失败"; + } + else + { + respone.Result = true; + respone.Message = "success"; + } + } + else + { + respone.Result = false; + respone.Message = "error"; + } + return respone; + } + + /// + /// 重新创建流程 + /// + /// 流程实例主键 + /// 备注 + /// + public async Task> CreateAgain(string processId, string des) + { + var flag = false; + + var iWFEngine = await Bootstraper("", processId, null, null); + + var unList = (List)await GetUnFinishTaskList(processId); + var taskEntity = unList.Find(t => t.State == 1 && t.UnitId == iWFEngine.StartNode.Id); + + if (taskEntity == null) + { + throw (new Exception("找不到重新提交流程任务!")); + } + + // 下一部需要执行的任务 + var taskList = await iWFEngine.GetTask(iWFEngine.StartNode.Id); + + // 处理任务并更新数据 + + List myTaskList; + + using (var uwo = UnitWork.CreateContext()) + { + await uwo.WFProcess.UpdateSetColumnsTrueAsync(a => new WFProcess() { IsCancel = 1, IsAgain = 0 }, a => a.Id == processId); + await uwo.WFTask.UpdateSetColumnsTrueAsync(a => new WFTask() { State = 3 }, a => a.Id == taskEntity.Id); + // 更新上一个流转过来的任务,提示他无法被撤销 + await CloseTaskLogCancel(taskEntity.ProcessId, taskEntity.PrevTaskId); + + myTaskList = await ExecuteWFTaskEntity(taskList, iWFEngine, processId, taskEntity.Token, taskEntity.Id); + foreach (var item in myTaskList) + { + if (item.Type == 7) + { + await uwo.WFTaskUser.InsertRangeAsync(item.WFTaskUser); + } + await Add(item); + } + + await CreateTaskLog(taskEntity, "", "重新提交", des, ""); + + //await _imMsgIBLL.VirtualDeleteByContentId(taskEntity.Token); // 更新消息 + + flag = uwo.Commit(); + } + + + var respone = new Response(); + + if (flag) + { + var scriptResults = new List(); + // 脚本执行 + var scriptTaskList = myTaskList.FindAll(t => t.Type == 10); + foreach (var item in scriptTaskList) + { + var result = await ExecuteScript(item, "createAgain", processId, "", "",iWFEngine.CreateUser, iWFEngine, null, ""); + scriptResults.Add(result); + } + + if (scriptResults.Contains(false)) + { + respone.Result = false; + respone.Message = "流程信息存储成功,但脚本执行失败"; + } + else + { + respone.Result = true; + respone.Message = "success"; + } + } + else + { + respone.Result = false; + respone.Message = "error"; + } + return respone; + } + + /// + /// 撤销流程(只有在该流程未被处理的情况下) + /// + /// 流程进程主键 + public async Task RevokeFlow(string processId) + { + var iWFEngine = await Bootstraper(string.Empty, processId, null, null); + + if (iWFEngine.Config.Params.IsStart != 1) + { + // 获取脚本任务 + var scriptList = (List)await GetList(processId, 10, "create"); + foreach (var item in scriptList) + { + await ExecuteRevokeScript(item.UnitId, item.UnitName, processId, iWFEngine.CreateUser, iWFEngine); + } + + using (var uwo = UnitWork.CreateContext()) + { + await uwo.WFTask.DeleteAsync(a => a.ProcessId == processId); + await uwo.WFTaskLog.DeleteAsync(a => a.ProcessId == processId); + await uwo.WFProcess.UpdateSetColumnsTrueAsync(a => new WFProcess { EnabledMark = 2 }, a => a.Id == processId); + + uwo.Commit(); + } + + await ExecuteScript(processId, "RevokeFlow", iWFEngine.CreateUser, + iWFEngine.WFScheme.UndoType, + iWFEngine.WFScheme.UndoDbCode, + iWFEngine.WFScheme.UndoDbSQL, + iWFEngine.WFScheme.UndoUrl, + iWFEngine.WFScheme.UndoIOCName); + + return true; + + } + + return false; + } + + /// + /// 催办流程 + /// + /// 流程进程主键 + public async Task> UrgeFlow(string processId) + { + // var userInfo = await this.CurrentUser(); + var iWFEngine = await Bootstraper(string.Empty, processId, null, null); + // 获取未完成的任务 + var taskList = await GetUnFinishTaskList(processId); + + using (var uwo = UnitWork.CreateContext()) + { + + foreach (var item in taskList) + { + if (item.Type == 1 || item.Type == 3 || item.Type == 5 || item.Type == 6 || item.Type == 7) + { + item.IsUrge = 1; + item.UrgeTime = DateTime.Now; + + + // 发送消息 + var msg = $"【审核】【催办】{item.ProcessTitle},{item.UnitName}"; + var userList = new List(); + // todo 单体任务处理 + userList.Add(item.UserId); + var node = iWFEngine.GetNode(item.UnitId); + //await _imMsgIBLL.SendMsg("IMWF", userList, msg, node.MessageType, item.F_Token); + + await uwo.WFTask.UpdateAsync(item); + } + } + + //await wfProcessSerive.Update(new WFProcessEntity() { F_IsUrge = 1, F_Id = processId }); + + WFTaskLog wfTaskLogEntity = new WFTaskLog() + { + Des = "催办审核", + TaskType = 98, + ProcessId = processId, + UnitId = iWFEngine.StartNode.Id, + UnitName = string.IsNullOrEmpty(iWFEngine.StartNode.Name) ? "开始节点" : iWFEngine.StartNode.Name + }; + + wfTaskLogEntity.CreateDate = DateTime.Now; + wfTaskLogEntity.UserId = _auth.GetUserId(); + wfTaskLogEntity.UserName = _auth.GetUserNickName(); + wfTaskLogEntity.Id = Guid.NewGuid().ToString(); + await uwo.WFTaskLog.InsertAsync(wfTaskLogEntity); + + var flag = uwo.Commit(); + return new Response + { + Result = flag, + Message = flag == true ? "success" : "error" + }; + } + } + + + /// + /// 审批流程 + /// + /// 流程任务主键 + /// 流程审批操作码agree 同意 disagree 不同意 lrtimeout 超时 + /// 流程审批操名称 + /// 审批意见 + /// 下一节点指定审核人 + /// 盖章图片 + /// 盖章图片密码 + /// 下一个审核节点 + public async Task AuditFlow(string taskId, string code, string name, string des, Dictionary nextUsers, string stampImg, string stampPassWord, string nextId) + { + var taskEntiy = await client.Queryable().Where(a => a.Id == taskId).FirstAsync(); + if (taskEntiy == null) + { + throw (new Exception("找不到对应流程任务!")); + } + else if (taskEntiy.State != 1) + { + if (taskEntiy.State == 3 || taskEntiy.State == 4) + { + throw (new Exception("该任务已完成!")); + } + else + { + throw (new Exception("该任务未被激活!")); + } + } + + //后期改 + stampImg = ""; + //stampImg = await _wFStampIBLL.ToWfImg(stampImg, stampPassWord); + + var iWFEngine = await Bootstraper("", taskEntiy.ProcessId, null, nextUsers); + + // 1.判断任务类型 1 普通任务 5 会签任务 + if (taskEntiy.Type == 1||taskEntiy.Type==7) + { + await AuditNode(iWFEngine, taskEntiy, code, name, des, stampImg, nextId); + } + else if (taskEntiy.Type == 5) + { + await AuditNodeByCountersign(iWFEngine, taskEntiy, code, name, des, stampImg); + } + else + { + throw (new Exception("该任务无法审核!")); + } + + //await _imMsgIBLL.VirtualDeleteByContentId(taskEntiy.F_Token); // 更新消息 + + // 发送消息 + var msg = $"【提醒】{iWFEngine.Config.Params.Title},{taskEntiy.UnitName}已被处理"; + var userList = new List(); + userList.Add(iWFEngine.CreateUser.Id); + var node = iWFEngine.StartNode; + //await _imMsgIBLL.SendMsg("IMWF", userList, msg, node.MessageType, iWFEngine.Config.Params.ProcessId); + } + + /// + /// 批量审核(只有同意和不同意) + /// + /// 操作码 + /// 任务id串 + public async Task AuditFlows(string code, List taskIdList) + { + foreach (var taskId in taskIdList) + { + string operationName = code == "agree" ? "同意" : "不同意"; + await AuditFlow(taskId, code, operationName, "批量审核", null, "", "", ""); + } + } + + /// + /// 撤销审核 + /// + /// + /// 流程任务主键 + /// + public async Task> RevokeAudit(string processId, string taskId) + { + var iWFEngine = await Bootstraper(string.Empty, processId, null, null); + WFTask taskEntity; + WFProcess processEntity; + if (string.IsNullOrEmpty(taskId)) + { //撤销重新提交任务 + processEntity = await GetEntity(processId); + if (processEntity.IsCancel != 1) + { + throw (new Exception("当前流程无法撤销!")); + } + + var myTaskList = (List)await GetLastTaskList(processId, iWFEngine.StartNode.Id); + taskEntity = myTaskList[0]; + + processEntity.IsAgain = 1; + processEntity.IsCancel = 0; + } + else + { + taskEntity = await client.Queryable().Where(a => a.Id == taskId).FirstAsync(); + + if (taskEntity.State == 1) + { + throw (new Exception("当前任务无法撤销!")); + } + + var taskLogEntity = await GetLogEntity(taskId); + + if (taskLogEntity == null || taskLogEntity.IsCancel != 1) + { + throw (new Exception("当前任务无法撤销!")); + } + processEntity = new WFProcess() + { + Id = processId + }; + processEntity.IsFinished = 0; + processEntity.IsAgain = 0; + } + + using (var uwo = UnitWork.CreateContext()) + { + // 获取脚本任务 + var scriptList = await GetList(processId, 10, taskEntity.Id); + foreach (var item in scriptList) + { + await ExecuteRevokeScript(item.UnitId, item.UnitName, processId, iWFEngine.CreateUser, iWFEngine); + // 删除脚本任务执行日志 + await DeleteLogByTaskId(item.Id); + } + + + // 删除所有生成的任务 + await Delete(processId, taskEntity.Id); + + // 如果是加签审核的,关闭最早的任务 + if (taskEntity.Type == 6) + { + var myTaskEntiy = await GetTaskEntity(taskEntity.FirstId); // 原始审核任务 + myTaskEntiy.State = 5; + await uwo.WFTask.UpdateAsync(myTaskEntiy); + } + + + // 如果是撤销转移任务 + if (taskEntity.State == 6) + { + // 删除转移任务 + await DeleteByFirstId(taskEntity.Id); + } + + + // 删除所有生成的日志 + await DeleteLog(processId, taskEntity.Id); + + // 更新等待任务状态 + await OpenTask(processId, taskEntity.UnitId, 21, taskEntity.Id); + + taskEntity.State = 1; + await uwo.WFTask.UpdateAsync(taskEntity); + + await uwo.Db.Updateable(processEntity) + .IgnoreColumns(ignoreAllNullColumns: true) + .ExecuteCommandAsync(); + + var flag = uwo.Commit(); + return new Response + { + Result = flag, + Message = flag == true ? "success" : "error" + }; + } + + } + + /// + /// 流程加签 + /// + /// 流程任务主键 + /// 加签人员 + /// 加签说明 + public async Task> SignFlow(string taskId, string userId, string des) + { + var taskEntiy = await GetTaskEntity(taskId); + if (taskEntiy == null) + { + throw (new Exception("找不到对应流程任务!")); + } + else if (taskEntiy.State != 1) + { + if (taskEntiy.State == 3 || taskEntiy.State == 4) + { + throw (new Exception("该任务已完成!")); + } + else + { + throw (new Exception("该任务未被激活!")); + } + } + + using (var uwo = UnitWork.CreateContext()) + { + + // 更新任务状态 + taskEntiy.State = 5; + await uwo.WFTask.UpdateAsync(taskEntiy); + + // 更新上一个流转过来的任务,提示他无法被撤销 + await CloseTaskLogCancel(taskEntiy.ProcessId, taskEntiy.PrevTaskId); + + // 填写日志 + await CreateTaskLog(taskEntiy, "sign", "加签", des, ""); + + if (taskEntiy.Type != 6) + { + taskEntiy.FirstId = taskEntiy.Id; + } + + var userInfo = await uwo.User.GetByIdAsync(userId); + + // 添加加签任务 + taskEntiy.PrevTaskId = taskEntiy.Id; + //taskEntiy.UserCompanyId = userInfo.CompanyId; + //taskEntiy.UserDepartmentId = userInfo.DepartmentId; + taskEntiy.UserId = userInfo.Id.ToString(); + taskEntiy.UserName = userInfo.Name; + taskEntiy.Type = 6; + taskEntiy.State = 1; + await uwo.WFTask.InsertAsync(taskEntiy); + + var flag = uwo.Commit(); + return new Response + { + Result = flag, + Message = flag == true ? "success" : "error" + }; + } + } + + /// + /// 流程加签审核 + /// + /// 流程任务主键 + /// 操作码 + /// 流程审批操名称 + /// 审批意见 + /// + public async Task> SignAudit(string taskId, string code, string name, string des) + { + var taskEntiy = await GetTaskEntity(taskId); + if (taskEntiy == null) + { + throw (new Exception("找不到对应流程任务!")); + } + else if (taskEntiy.State != 1) + { + if (taskEntiy.State == 3 || taskEntiy.State == 4) + { + throw (new Exception("该任务已完成!")); + } + else + { + throw (new Exception("该任务未被激活!")); + } + } + + using (var uwo = UnitWork.CreateContext()) + { + var myTaskEntiy = await GetTaskEntity(taskEntiy.FirstId); // 原始审核任务 + myTaskEntiy.IsLast = 0; + await uwo.WFTask.UpdateAsync(myTaskEntiy); + myTaskEntiy.State = 1; + myTaskEntiy.IsLast = 1; + await Add(myTaskEntiy); + + // 更新任务状态 + taskEntiy.State = 3; + if (taskEntiy.Type == 7) + { + var currentUser = _auth.GetCurrentUser().User; + taskEntiy.UserId = currentUser.Id.ToString(); + taskEntiy.UserName = currentUser.Name; + } + await uwo.WFTask.UpdateAsync(taskEntiy); + + // 填写日志 + await CreateTaskLog(taskEntiy, code, name, des, ""); + + // 更新上一个流转过来的任务,提示他无法被撤销 + await CloseTaskLogCancel(taskEntiy.ProcessId, taskEntiy.PrevTaskId); + + var flag = uwo.Commit(); + return new Response + { + Result = flag, + Message = flag == true ? "success" : "error" + }; + } + } + + /// + /// 确认阅读 + /// + /// 流程任务主键 + public async Task> ReadFlow(string taskId) + { + var taskEntiy = await GetTaskEntity(taskId); + if (taskEntiy == null) + { + throw (new Exception("找不到对应流程任务!")); + } + else if (taskEntiy.State == 1) + { + using (var uwo = UnitWork.CreateContext()) + { + taskEntiy.State = 3; + await uwo.WFTask.UpdateAsync(taskEntiy); + + WFTaskLog wfTaskLogEntity = new WFTaskLog() + { + TaskType = taskEntiy.Type, + ProcessId = taskEntiy.ProcessId, + UnitId = taskEntiy.UnitId, + UnitName = taskEntiy.UnitName, + IsAgree = taskEntiy.IsAgree, + OperationCode = "read", + OperationName = "已阅", + Token = taskEntiy.Token, + PrevUnitId = taskEntiy.PrevUnitId, + PrevUnitName = taskEntiy.PrevUnitName, + TaskId = taskEntiy.Id, + IsCancel = 0 + }; + await AddLog(wfTaskLogEntity); + + var flag = uwo.Commit(); + return new Response + { + Result = flag, + Message = flag == true ? "success" : "error" + }; + } + + } + else + { + return new Response + { + Result = false, + Message = "error" + }; + } + } + + /// + /// 转移流程 + /// + /// 任务id + /// 转移人用户id + /// 说明 + /// + public async Task> TransferUser(string taskId, string userId, string des) + { + var taskEntiy = await GetTaskEntity(taskId); + if (taskEntiy == null) + { + throw (new Exception("找不到对应流程任务!")); + } + else if (taskEntiy.State == 1) + { + using (var uwo = UnitWork.CreateContext()) + { + var userInfo = await uwo.User.GetByIdAsync(userId); + + // 更新任务状态 + taskEntiy.State = 6; + await uwo.WFTask.UpdateAsync(taskEntiy); + + // 更新流程 + WFProcess processEntity = new WFProcess() + { + Id = taskEntiy.ProcessId, + IsStart = 1, + IsCancel = 0, + }; + await uwo.WFProcess.UpdateAsync(processEntity); + + // 填写日志 + await CreateTaskLog(taskEntiy, "transfer", "转移", des, ""); + + // 更新上一个流转过来的任务,提示他无法被撤销 + await CloseTaskLogCancel(taskEntiy.ProcessId, taskEntiy.PrevTaskId); + + // 新建任务 + var newTask = taskEntiy; //.ToJson().ToObject(); + newTask.State = 1; + newTask.PrevTaskId = taskEntiy.Id; + //newTask.UserCompanyId = userInfo.F_CompanyId; + //newTask.UserDepartmentId = userInfo.F_DepartmentId; + newTask.UserId = userInfo.Id.ToString(); + newTask.UserName = userInfo.Name; + + await uwo.WFTask.InsertAsync(newTask); + + // 发送消息 + var iWFEngine = await Bootstraper("", taskEntiy.ProcessId, null); + var msg = $"【审核】{taskEntiy.ProcessTitle},{taskEntiy.UnitName}"; + var userList = new List(); + userList.Add(userInfo.Id.ToString()); + var node = iWFEngine.GetNode(taskEntiy.UnitId); + //await _imMsgIBLL.SendMsg("IMWF", userList, msg, node.MessageType, newTask.F_Token); + + var flag = uwo.Commit(); + + return new Response + { + Result = flag, + Message = flag == true ? "success" : "error" + }; + } + } + else + { + return new Response + { + Result = false, + Message = "error" + }; + } + } + + /// + /// 获取下一节点审核人 + /// + /// 流程模板编码 + /// 流程进程主键 + /// 流程节点Id + /// 流程操作代码 + /// 创建人 + /// + public async Task>> GetNextAuditors(string code, string processId, string nodeId, string operationCode, string userId) + { + Dictionary> res = new Dictionary>(); + var userInfo = await client.Queryable().Where(a => a.Id.ToString() == userId).FirstAsync(); + var iWFEngine = await Bootstraper(code, processId, userInfo); + + // 下一部需要执行的任务 + var taskList = await iWFEngine.GetTask(nodeId, operationCode); + + foreach (var item in taskList) + { + if ((item.Type == 1 || item.Type == 3 || item.Type == 5||item.Type==7) && item.User.IsNew) + { + if (!res.ContainsKey(item.UnitId)) + { + res.Add(item.UnitId, new List()); + } + res[item.UnitId].Add(item.User); + } + } + + return res; + } + + /// + /// 作废流程 + /// + /// 流程进程主键 + /// + public async Task> DeleteProcess(string processId) + { + var flag = false; + var iWFEngine = await Bootstraper(string.Empty, processId, null); + + using (var uwo = UnitWork.CreateContext()) + { + // 更新流程状态 + WFProcess processEntity = new WFProcess() + { + Id = processId, + + }; + await uwo.WFProcess.UpdateSetColumnsTrueAsync(a => new WFProcess { EnabledMark = 3 }, a => a.Id == processId); + + await uwo.WFTask.UpdateSetColumnsTrueAsync(a => new WFTask { State = 7 }, a => a.ProcessId == processId && a.State == 1); + flag = uwo.Commit(); + } + + var response = new Response(); + + if (flag) + { + var scriptResult = await ExecuteScript(processId, "delete", iWFEngine.CreateUser, + iWFEngine.WFScheme.DeleteType, + iWFEngine.WFScheme.DeleteDbCode, + iWFEngine.WFScheme.DeleteDbSQL, + iWFEngine.WFScheme.DeleteUrl, + iWFEngine.WFScheme.DeleteIOCName); + + if (scriptResult) + { + response.Result = true; + response.Message = "success"; + } + else + { + response.Result = false; + response.Message = "流程状态更新成功,但脚本执行失败"; + } + } + else + { + response.Result = false; + response.Message = "error"; + } + return response; + } + + /// + /// 流程还原 + /// + /// 流程进程主键 + /// + public async Task> RecoverProcess(string processId) + { + var flag = false; + var iWFEngine = await Bootstraper(string.Empty, processId, null); + + using (var uwo = UnitWork.CreateContext()) + { + // 更新流程状态 + WFProcess processEntity = new WFProcess() + { + Id = processId, + + }; + await uwo.WFProcess.UpdateSetColumnsTrueAsync(a => new WFProcess { EnabledMark = 1 }, a => a.Id == processId); + + await uwo.WFTask.UpdateSetColumnsTrueAsync(a => new WFTask { State = 1 }, a => a.ProcessId == processId && a.State == 7); + flag = uwo.Commit(); + } + + var response = new Response(); + + if (flag) + { + + response.Result = true; + response.Message = "success"; + } + else { + response.Result = false; + response.Message = "error"; + } + + return response; + } + + /// + /// 指派流程审核人 + /// + /// 流程进程主键 + /// 流程指派人信息 + /// + public async Task> PointUser(string processId, List pointList) + { + var list = await GetUnFinishTaskList(processId); + Dictionary> map = new Dictionary>(); + foreach (var item in list) + { + if (!map.ContainsKey(item.UnitId)) + { + map.Add(item.UnitId, new List()); + } + map[item.UnitId].Add(item); + } + + var addList = new List(); + var iWFEngine = await Bootstraper("", processId, null); + using (var uwo = UnitWork.CreateContext()) + { + + foreach (var item in pointList) + { + if (map.ContainsKey(item.UnitId)) + { + await CloseTask(processId, map[item.UnitId][0].Token, ""); + var userIdList = item.UserIds.Split(","); + foreach (var userId in userIdList) + { + var addItem = Newtonsoft.Json.JsonConvert.SerializeObject(map[item.UnitId][0]).ToObject(); + addItem.State = 1; + if (addItem.Type == 6) + { + addItem.Type = 1; + } + + var userEntity = _auth.GetCurrentUser().User; + + addItem.UserId = userId; + addItem.UserName = userEntity.Name; + //addItem.UserCompanyId = userEntity.F_CompanyId; + //addItem.UserDepartmentId = userEntity.F_DepartmentId; + + // 发送消息 + var msg = $"【审核】{addItem.ProcessTitle},{addItem.UnitName}"; + var userList = new List(); + userList.Add(userId); + var node = iWFEngine.GetNode(addItem.UnitId); + //await _imMsgIBLL.SendMsg("IMWF", userList, msg, node.MessageType, addItem.F_Token); + + addList.Add(addItem); + } + } + else + { + throw (new Exception("找不到指派任务节点!")); + } + } + + foreach (var item in addList) + { + await Add(item); + } + + var flag = uwo.Commit(); + + return new Response() + { + Result = flag, + Message = flag == true ? "success" : "error" + }; + } + + } + #endregion + + #region + /// + /// 流程模板初始化 + /// + /// 流程模板 + /// 流程实例 + /// 提交人用户信息 + /// 下一个节点审核人 + /// + private async Task Bootstraper(string code, string processId, SysUser userInfo, Dictionary nextUsers = null) + { + + WFEngineConfig wfEngineConfig = new WFEngineConfig(); + WFParams wfParams = new WFParams(); + wfEngineConfig.Params = wfParams; + + var currentUser = _auth.GetCurrentUser().User; + wfParams.CurrentUser = new WFUserInfo() + { + Id = currentUser.Id.ToString(), + Account = currentUser.Account, + Name = currentUser.Name + //CompanyId = currentUser.F_CompanyId, + //DepartmentId = currentUser.F_DepartmentId + }; + if (!string.IsNullOrEmpty(code)) + { + var schemeInfo = await client.Queryable().FirstAsync(a => a.Code == code); + if (schemeInfo != null) + { + var data = await client.Queryable().FirstAsync(a => a.Id == schemeInfo.SchemeId); + if (data != null) + { + wfParams.Scheme = data.Content; + wfParams.SchemeCode = code; + wfParams.SchemeId = schemeInfo.SchemeId; + wfParams.SchemeName = schemeInfo.Name; + wfParams.ProcessId = processId; + wfParams.HasInstance = false; + + wfParams.CreateUser = new WFUserInfo() + { + Id = userInfo.Id.ToString(), + Account = userInfo.Account, + Name = userInfo.Name + //CompanyId = userInfo.F_CompanyId, + //DepartmentId = userInfo.F_DepartmentId + }; + } + else + { + throw new Exception(string.Format("无法获取对应的流程模板【{0}】", code)); + } + } + else + { + throw new Exception(string.Format("无法获取对应的流程模板【{0}】", code)); + } + } + else if (!string.IsNullOrEmpty(processId)) + { + var processEntity = await GetEntity(processId); + if (processEntity != null) + { + var data = await client.Queryable().FirstAsync(a => a.Id == processEntity.SchemeId); + if (data != null) + { + wfParams.Scheme = data.Content; + wfParams.SchemeCode = processEntity.SchemeCode; + wfParams.SchemeId = processEntity.SchemeId; + wfParams.SchemeName = processEntity.SchemeName; + wfParams.IsChild = processEntity.IsChild == 1; + wfParams.ParentProcessId = processEntity.ParentProcessId; + wfParams.ParentTaskId = processEntity.ParentTaskId; + wfParams.ParentNodeId = processEntity.ParentNodeId; + wfParams.ProcessId = processId; + wfParams.HasInstance = true; + wfParams.IsStart = processEntity.IsStart == 1 ? 1 : 0; + wfParams.Title = processEntity.Title; + + wfParams.CreateUser = new WFUserInfo() + { + Id = processEntity.CreateUserId, + Account = processEntity.CreateUserAccount, + Name = processEntity.CreateUserName, + CompanyId = processEntity.CreateUserCompanyId, + DepartmentId = processEntity.CreateUserDepartmentId + }; + } + else + { + throw new Exception(string.Format("无法获取流程模板【{0}】", processEntity.SchemeId)); + } + } + else if (string.IsNullOrEmpty(wfParams.Scheme)) + { + throw new Exception(string.Format("无法获取实例数据【{0}】", processId)); + } + } + + + wfParams.NextUsers = nextUsers; + + + // 注册委托方法 + wfEngineConfig.GetAwaitTaskList = GetAwaitTaskList; + wfEngineConfig.GetUserList = GetUserList; + wfEngineConfig.GetSystemUserList = GetSystemUserList; + wfEngineConfig.GetPrevTaskUserList = GetPrevTaskUserList; + wfEngineConfig.IsCountersignAgree = IsCountersignAgree; + wfEngineConfig.GetPrevUnitId = GetPrevUnitId; + + + IWFEngine iwfEngine = new WFEngine(wfEngineConfig, client); + + return iwfEngine; + } + + /// + /// 流程模板初始化 + /// + /// 流程模板 + /// 流程实例 + /// 提交人用户信息 + /// 下一个节点审核人 + /// + private async Task BootstraperWithUser(string code, string processId, SysUser userInfo, Dictionary nextUsers = null) + { + + WFEngineConfig wfEngineConfig = new WFEngineConfig(); + WFParams wfParams = new WFParams(); + wfEngineConfig.Params = wfParams; + + var currentUser = userInfo; + wfParams.CurrentUser = new WFUserInfo() + { + Id = currentUser.Id.ToString(), + Account = currentUser.Account, + Name = currentUser.Name + //CompanyId = currentUser.F_CompanyId, + //DepartmentId = currentUser.F_DepartmentId + }; + if (!string.IsNullOrEmpty(code)) + { + var schemeInfo = await client.Queryable().FirstAsync(a => a.Code == code); + if (schemeInfo != null) + { + var data = await client.Queryable().FirstAsync(a => a.Id == schemeInfo.SchemeId); + if (data != null) + { + wfParams.Scheme = data.Content; + wfParams.SchemeCode = code; + wfParams.SchemeId = schemeInfo.SchemeId; + wfParams.SchemeName = schemeInfo.Name; + wfParams.ProcessId = processId; + wfParams.HasInstance = false; + + wfParams.CreateUser = new WFUserInfo() + { + Id = userInfo.Id.ToString(), + Account = userInfo.Account, + Name = userInfo.Name + //CompanyId = userInfo.F_CompanyId, + //DepartmentId = userInfo.F_DepartmentId + }; + } + else + { + throw new Exception(string.Format("无法获取对应的流程模板【{0}】", code)); + } + } + else + { + throw new Exception(string.Format("无法获取对应的流程模板【{0}】", code)); + } + } + else if (!string.IsNullOrEmpty(processId)) + { + var processEntity = await GetEntity(processId); + if (processEntity != null) + { + var data = await client.Queryable().FirstAsync(a => a.Id == processEntity.SchemeId); + if (data != null) + { + wfParams.Scheme = data.Content; + wfParams.SchemeCode = processEntity.SchemeCode; + wfParams.SchemeId = processEntity.SchemeId; + wfParams.SchemeName = processEntity.SchemeName; + wfParams.IsChild = processEntity.IsChild == 1; + wfParams.ParentProcessId = processEntity.ParentProcessId; + wfParams.ParentTaskId = processEntity.ParentTaskId; + wfParams.ParentNodeId = processEntity.ParentNodeId; + wfParams.ProcessId = processId; + wfParams.HasInstance = true; + wfParams.IsStart = processEntity.IsStart == 1 ? 1 : 0; + wfParams.Title = processEntity.Title; + + wfParams.CreateUser = new WFUserInfo() + { + Id = processEntity.CreateUserId, + Account = processEntity.CreateUserAccount, + Name = processEntity.CreateUserName, + CompanyId = processEntity.CreateUserCompanyId, + DepartmentId = processEntity.CreateUserDepartmentId + }; + } + else + { + throw new Exception(string.Format("无法获取流程模板【{0}】", processEntity.SchemeId)); + } + } + else if (string.IsNullOrEmpty(wfParams.Scheme)) + { + throw new Exception(string.Format("无法获取实例数据【{0}】", processId)); + } + } + + + wfParams.NextUsers = nextUsers; + + + // 注册委托方法 + wfEngineConfig.GetAwaitTaskList = GetAwaitTaskList; + wfEngineConfig.GetUserList = GetUserList; + wfEngineConfig.GetSystemUserList = GetSystemUserList; + wfEngineConfig.GetPrevTaskUserList = GetPrevTaskUserList; + wfEngineConfig.IsCountersignAgree = IsCountersignAgree; + wfEngineConfig.GetPrevUnitId = GetPrevUnitId; + + + IWFEngine iwfEngine = new WFEngine(wfEngineConfig, client); + + return iwfEngine; + } + + /// + /// 获取等待任务 + /// + /// 流程进程ID + /// 流程单元ID + /// + private async Task> GetAwaitTaskList(string processId, string unitId) + { + List res = new List(); + var list = await client.Queryable().Where(t => t.ProcessId == processId && t.UnitId == unitId && t.Type == 21 && t.State == 1).ToListAsync(); + + foreach (var item in list) + { + res.Add(GetWFTask(item)); + } + return res; + } + /// + /// 获取设置人员 + /// + /// 人员配置信息 + /// 流程发起人 + /// 流程进程实例ID + /// 开始节点 + /// + private async Task> GetUserList(List auditorList, WFUserInfo createUser, string processId, WFUnit startNode) + { + var res = new List(); + List userIds = new List(); + foreach (var item in auditorList) + { + switch (item.Type) + { + //岗位 + case "1": + var rlist = await userManager.UserIdsByPosition("", item.Id); + foreach (var ritem in rlist) + { + userIds.Add(ritem); + } + break; + //角色 + case "2": + var rlist2 = await userManager.UserIdsByRole(item.Id); + List userIds2 = new List(); + foreach (var ritem2 in rlist2) + { + userIds2.Add(ritem2); + } + var userList2 = await GetListByKeyValues(userIds2.ToArray()); + var userOrgs = await userManager.UserOrgsByUserIds(userIds2); + foreach (var userOrg in userOrgs) + { + var user2 = userList2.FirstOrDefault(a => a.Id.ToString() == userOrg.Key); + switch (item.Condition) + { + case "1": + if ((userOrg.Value as List).Contains(createUser.DepartmentId)) + { + res.Add(GetWFUserInfo(user2)); + } + break; + //case "2": + // if (user2.CompanyId == createUser.CompanyId) + // { + // res.Add(GetWFUserInfo(user2)); + // } + // break; + case "3": + //// 获取当前用户的岗位 + var postList1 = await userManager.PositonsByUser(createUser.Id);// 发起人岗位 + var postList2 = await userManager.PositonsByUser(user2.Id.ToString());// 节点审核人岗位 + //if (await _postIBLL.IsUp(postList1, postList2)) + if (await positionManager.IsUp(postList1, postList2)) + { + res.Add(GetWFUserInfo(user2)); + } + break; + case "4": + // 获取当前用户的岗位 + var postList3 = await userManager.PositonsByUser(createUser.Id);// 发起人岗位 + var postList4 = await userManager.PositonsByUser(user2.Id.ToString());// 节点审核人岗位 + if (await positionManager.IsDown(postList3, postList4)) + { + res.Add(GetWFUserInfo(user2)); + } + break; + default: + res.Add(GetWFUserInfo(user2)); + break; + } + } + break; + //用户 + case "3": + userIds.Add(item.Id); + break; + //上下级 + case "4": + var postIds = await userManager.PositonsByUser(createUser.Id);// 发起人岗位 + int level = Convert.ToInt32(item.Id); + List postList; + if (level < 6) + { + postList = (await positionManager.GetUpIdList(postIds, level)).ToList(); + } + else + { + level = level - 5; + postList = (await positionManager.GetDownIdList(postIds, level)).ToList(); + } + var userRelationList4 = await userManager.UserIdsByPositions(postList); + userIds.AddRange(userRelationList4); + break; + //节点执行人 + case "5": + if (item.Id == startNode.Id) + { + res.Add(createUser); + } + else + { + var taskList = await GetLastFinishTaskList(item.Id, processId); + foreach (var task in taskList) + { + userIds.Add(task.UserId); + } + } + + break; + //数据库表字段 + case "6": + using (var db = this.CodeClient(item.DbCode, _configuration)) + { + DataTable dt = await db.Ado.GetDataTableAsync(string.Format("select {0} from {1} where {2} = @processId ", item.AuditorField, item.Table, item.Rfield), new { processId }); + foreach (DataRow row in dt.Rows) + { + var userIdString = row[0].ToString(); + if (!string.IsNullOrEmpty(userIdString)) + { + var userIdList = userIdString.Split(","); + foreach (var userIdItem in userIdList) + { + userIds.Add(userIdItem); + } + } + } + break; + } + + //数据库表字段 + case "7": + using (var db = this.CodeClient(item.DbCode, _configuration)) + { + //获取数据id + var process = await client.Queryable().FirstAsync(a => a.Id == processId); + WFInstanceInfo instanceInfo = null; + if (process != null) + { + // instanceInfo = Newtonsoft.Json.JsonConvert.DeserializeObject(process.InstanceInfo); + instanceInfo = process.InstanceInfo; + } + DataTable dt = await db.Ado.GetDataTableAsync(string.Format(item.Sql), new { pkeyValue = instanceInfo.pkeyValue }); + foreach (DataRow row in dt.Rows) + { + var userIdString = row[0].ToString(); + if (!string.IsNullOrEmpty(userIdString)) + { + var userIdList = userIdString.Split(","); + foreach (var userIdItem in userIdList) + { + userIds.Add(userIdItem); + } + } + } + break; + } + } + } + var userList = await GetListByKeyValues(userIds.ToArray()); + foreach (var user in userList) + { + res.Add(GetWFUserInfo(user)); + } + return res; + } + /// + /// 获取单元上一次的审核人 + /// + /// 流程进程ID + /// 流程单元ID + /// + private async Task> GetPrevTaskUserList(string processId, string unitId) + { + var res = new List(); + var list = await GetLastTaskList(processId, unitId); + foreach (var item in list) + { + if (!string.IsNullOrEmpty(item.UserId)) + { + res.Add(new WFUserInfo() + { + Id = item.UserId, + Name = item.UserName, + CompanyId = item.UserCompanyId, + DepartmentId = item.UserDepartmentId, + IsAgree = item.IsAgree == 1, + //Sort = item, + State = (int)item.State + }); + } + } + return res; + } + /// + /// 获取系统管理员 + /// + /// + private async Task> GetSystemUserList() + { + var res = new List(); + + var user = new SysUser + { + Id = -1, + Account = Define.SYSTEM_USERNAME, + Name = "超级管理员" + }; + res.Add(GetWFUserInfo(user)); + + return res; + } + /// + /// 判断会签是否通过 + /// + /// 流程进程ID + /// 流程单元ID + /// + private async Task IsCountersignAgree(string processId, string unitId) + { + var list = (List)await GetLogList(processId, unitId); + if (list.Count == 0) + { + return false; + } + return list[0].IsAgree == 1; + } + /// + /// 获取上一个流入节点(不是驳回流入的) + /// + /// + /// + /// + private async Task GetPrevUnitId(string processId, string unitId) + { + var data = await GetLastNotRejectTask(processId, unitId); + if (data != null) + { + return data.PrevUnitId; + } + else + { + return string.Empty; + } + } + /// + /// 获取流程任务数据 + /// + /// + /// + private WorkFlow.WFTask GetWFTask(WFTask wfTaskEntity) + { + var res = new WorkFlow.WFTask() + { + UnitId = wfTaskEntity.UnitId, + Name = wfTaskEntity.UnitName, + PrevUnitId = wfTaskEntity.PrevUnitId, + PrevUnitName = wfTaskEntity.PrevUnitName, + Token = wfTaskEntity.Token, + Type = (int)wfTaskEntity.Type + }; + return res; + } + /// + /// 获取用户 + /// + /// + /// + private WFUserInfo GetWFUserInfo(SysUser userEntity) + { + var res = new WFUserInfo() + { + Id = userEntity.Id.ToString(), + Name = userEntity.Name, + Account = userEntity.Account, + //DepartmentId = userEntity.F_DepartmentId, + //CompanyId = userEntity.F_CompanyId, + IsNew = true + }; + return res; + } + /// + /// 获取流程实例 + /// + /// 流程运行参数 + /// 标题 + /// + private WFProcess GetWFProcessEntity(WFParams myParams, string title) + { + WFProcess wfProcessEntity = new WFProcess() + { + Id = myParams.ProcessId, + SchemeCode = myParams.SchemeCode, + SchemeId = myParams.SchemeId, + SchemeName = myParams.SchemeName, + Title = title, + EnabledMark = 1, + IsAgain = 0, + IsFinished = 0, + IsChild = myParams.IsChild ? 1 : 0, + IsStart = 0, + IsCancel = 1, + ParentNodeId = myParams.ParentNodeId, + ParentProcessId = myParams.ParentProcessId, + ParentTaskId = myParams.ParentTaskId, + CreateUserId = myParams.CreateUser.Id, + CreateUserName = myParams.CreateUser.Name, + CreateUserAccount = myParams.CreateUser.Account, + CreateUserCompanyId = myParams.CreateUser.CompanyId, + CreateUserDepartmentId = myParams.CreateUser.DepartmentId, + UserId = _auth.GetUserId() + }; + + return wfProcessEntity; + + } + + /// + /// 处理任务 + /// + /// 任务列表 + /// + /// 流程进程实例 + /// 任务Token + /// 任务id + /// + private async Task> ExecuteWFTaskEntity(List list, IWFEngine iWFEngine, string processId, string prevToken, string taskId = "") + { + var res = new List(); + + //单任务审核 + var tlist = list.Where(r => r.Type == 7).ToList(); + if (tlist.Count > 0) + { + var group = tlist.GroupBy(r => r.UnitId).ToList(); + foreach (var item in group) + { + var taskuser = new List(); + var users = tlist.Where(r => r.UnitId == item.Key).ToList(); + var userTask = new WFTask + { + Id = Guid.NewGuid().ToString(), + Type = users[0].Type, + ProcessId = processId, + Token = users[0].Token, + UnitId = users[0].UnitId, + UnitName = users[0].Name, + PrevUnitId = users[0].PrevUnitId, + PrevUnitName = users[0].PrevUnitName, + PrevToken = prevToken, + PrevTaskId = taskId, + UserId = users[0].User.Id, + UserName = users[0].User.Name, + UserDepartmentId = users[0].User.DepartmentId, + UserCompanyId = users[0].User.CompanyId, + State = users[0].User.IsAwait ? 2 : 1, + Sort = users[0].User.Sort, + IsReject = users[0].IsReject ? 1 : 0, + ChildProcessId = users[0].ChildSchemeInfoId, + + TimeoutInterval = users[0].OvertimeMessageInterval, + TimeoutStrategy = users[0].OvertimeMessageType, + + IsBatchAudit = users[0].IsBatchAudit ? 1 : 0, + ProcessUserId = iWFEngine.CreateUser.Id, + ProcessUserName = iWFEngine.CreateUser.Name, + ProcessCode = iWFEngine.Config.Params.SchemeCode, + ProcessTitle = iWFEngine.Config.Params.Title, + IsRetract=0 + }; + if (users[0].IsOvertimeMessage) + { + if (users[0].OvertimeGo > 0) + { + userTask.TimeoutAction = DateTime.Now.AddHours(users[0].OvertimeGo); + } + if (users[0].OvertimeMessageStart > 0) + { + userTask.TimeoutNotice = DateTime.Now.AddHours(users[0].OvertimeMessageStart); + } + } + foreach (var ts in users) + { + WFTaskUser tu = new WFTaskUser(); + tu.UserId = ts.User.Id; + tu.TaskId = userTask.Id; + taskuser.Add(tu); + } + userTask.WFTaskUser = taskuser; + res.Add(userTask); + } + } + foreach (var item in list) + { + if (!string.IsNullOrEmpty(item.MessageType)) + { + // 发送消息 + var taskType = ""; + switch (item.Type) + { + case 1: + case 5: + case 7: + taskType = "审核"; + break; + case 2: + taskType = "查阅"; + break; + case 3: + taskType = "提交"; + break; + case 4: + taskType = "重新提交"; + break; + } + if (!string.IsNullOrEmpty(taskType)) + { + var msg = $"【{taskType}】{iWFEngine.Config.Params.Title},{item.Name}"; + var userList = new List(); + userList.Add(item.User.Id); + //await _imMsgIBLL.SendMsg("IMWF", userList, msg, item.MessageType, item.Token); + } + } + + string childSchemeInfoCode = ""; + if (item.Type == 3) + { + var schemeInfo = await this.Repository.ChangeRepository>().GetByIdAsync(item.ChildSchemeInfoId); + if (schemeInfo != null) + { + childSchemeInfoCode = schemeInfo.Code; + } + } + + switch (item.Type) + { + case 1: + case 2: + case 3: + case 4: + case 5: + var userTask = new WFTask + { + Type = item.Type, + ProcessId = processId, + Token = item.Token, + UnitId = item.UnitId, + UnitName = item.Name, + PrevUnitId = item.PrevUnitId, + PrevUnitName = item.PrevUnitName, + PrevToken = prevToken, + PrevTaskId = taskId, + UserId = item.User.Id, + UserName = item.User.Name, + UserDepartmentId = item.User.DepartmentId, + UserCompanyId = item.User.CompanyId, + State = item.User.IsAwait ? 2 : 1, + Sort = item.User.Sort, + IsReject = item.IsReject ? 1 : 0, + ChildProcessId = item.ChildSchemeInfoId, + ChildSchemeInfoCode = childSchemeInfoCode, + + TimeoutInterval = item.OvertimeMessageInterval, + TimeoutStrategy = item.OvertimeMessageType, + + IsBatchAudit = item.IsBatchAudit ? 1 : 0, + ProcessUserId = iWFEngine.CreateUser.Id, + ProcessUserName = iWFEngine.CreateUser.Name, + ProcessCode = iWFEngine.Config.Params.SchemeCode, + ProcessTitle = iWFEngine.Config.Params.Title, + IsRetract=0 + }; + if (item.IsOvertimeMessage) + { + if (item.OvertimeGo > 0) + { + userTask.TimeoutAction = DateTime.Now.AddHours(item.OvertimeGo); + } + if (item.OvertimeMessageStart > 0) + { + userTask.TimeoutNotice = DateTime.Now.AddHours(item.OvertimeMessageStart); + } + } + + res.Add(userTask); + break; + case 10: + // 执行脚本 + res.Add(new WFTask + { + Type = item.Type, + ProcessId = processId, + Token = item.Token, + UnitId = item.UnitId, + UnitName = item.Name, + PrevUnitId = item.PrevUnitId, + PrevUnitName = item.PrevUnitName, + PrevToken = prevToken + }); + break; + case 21: + res.Add(new WFTask + { + Type = item.Type, + ProcessId = processId, + Token = item.Token, + UnitId = item.UnitId, + UnitName = item.Name, + PrevUnitId = item.PrevUnitId, + PrevUnitName = item.PrevUnitName, + PrevToken = prevToken, + State = 1, + }); + break; + case 22: + await CloseTask(processId, item.UnitId, 21, taskId); + break; + case 23: + case 24: + WFTaskLog wfTaskLogEntity = new WFTaskLog() + { + Des = "系统任务", + TaskType = item.Type, + Token = item.Token, + ProcessId = processId, + UnitId = item.UnitId, + UnitName = item.Name, + PrevUnitId = item.PrevUnitId, + PrevUnitName = item.PrevUnitName + }; + await AddLog(wfTaskLogEntity); + break; + case 26: + // 更新任务状态 + await Update(processId, item.UnitId); + break; + } + } + return res; + } + + public void AddOpenJob(WFTask task, DateTime outtime, DateTime overtime) + { + try + { + AddOrUpdateOpenJobReq req = new AddOrUpdateOpenJobReq(); + req.JobName = $"超时通知 {task.Id}"; + req.JobType = 0; + req.JobCallParams = "{\"TaskId\":\"" + task.Id + "\"}"; + req.JobCall = "OpenAuth.App.BaseApp.Jobs.TaskTimeoutJob"; + req.Status = 1; + req.Cron = GetCorn(outtime); + req.Remark = $"{task.ProcessUserId}--{task.UnitName}"; + _job.AddStart(req); + + AddOrUpdateOpenJobReq req1 = new AddOrUpdateOpenJobReq(); + req1.JobName = $"严重超时通知 {task.Id}"; + req1.JobType = 0; + req1.JobCallParams = "{\"TaskId\":\"" + task.Id + "\"}"; + req1.JobCall = "OpenAuth.App.BaseApp.Jobs.TaskOverTimeJob"; + req1.Status = 1; + req1.Cron = GetCorn(overtime); + req1.Remark = $"{task.ProcessUserId}--{task.UnitName}"; + _job.AddStart(req1); + } + catch + { + return; + } + } + + + public string GetCorn(DateTime time) + { + StringBuilder sb = new StringBuilder(); + sb.Append(time.Second.ToString()); + sb.Append(" "); + sb.Append(time.Minute.ToString()); + sb.Append(" "); + sb.Append(time.Hour.ToString()); + sb.Append(" "); + sb.Append(time.Day.ToString()); + sb.Append(" "); + sb.Append(time.Month.ToString()); + sb.Append(" "); + sb.Append("?"); + sb.Append(" "); + sb.Append(time.Year.ToString()); + return sb.ToString(); + } + /// + /// 执行脚本 + /// + /// 脚本任务 + /// + /// + /// + /// + /// + /// + /// + private async Task ExecuteScript(WFTask task, string code, string processId, string prevTaskId,string nextid, WFUserInfo createUser, IWFEngine iWFEngine, WFTask preTask,string des) + { + SysUser userInfo; + if (preTask == null) + { + userInfo = new SysUser() + { + Id = long.Parse(createUser.Id), + Account = createUser.Account, + //CompanyId = createUser.CompanyId, + //F_DepartmentId = createUser.DepartmentId + }; + } + else + { + //userInfo = await this.CurrentUser(preTask.F_UserId); + userInfo = _auth.GetCurrentUser().User; + } + string nextnode = ""; + if (!string.IsNullOrEmpty(nextid)) + { + nextnode = iWFEngine.GetNode(nextid).Name; + } + + var process = await client.Queryable().FirstAsync(a => a.Id == processId); + + WFInstanceInfo instanceInfo = null; + if (process != null) + { + // instanceInfo = Newtonsoft.Json.JsonConvert.DeserializeObject(process.InstanceInfo); + instanceInfo = process.InstanceInfo; + } + + var param = new + { + processId, + userId = createUser.Id, + userAccount = createUser.Account, + companyId = createUser.CompanyId, + departmentId = createUser.DepartmentId, + userName2 = userInfo.Name, + userId2 = userInfo.Id.ToString(), + userAccount2 = userInfo.Account, + //companyId2 = userInfo.F_CompanyId, + //departmentId2 = userInfo.F_DepartmentId, + code, + nodeName=nextnode, + pkeyValue = instanceInfo == null ? "" : instanceInfo.pkeyValue, + des=des + }; + try + { + var node = iWFEngine.GetNode(task.UnitId); + switch (node.ExecuteType) + { + case "1": + if (!string.IsNullOrEmpty(node.SqlDb) && !string.IsNullOrEmpty(node.SqlStr)) + { + await client.Ado.ExecuteCommandAsync(node.SqlStr, param); + } + break; + case "2": + if (!string.IsNullOrEmpty(node.Ioc) && IocManager.Instance.IsRegistered(node.Ioc)) + { + WorkFlow.IWorkFlowMethod iWorkFlowMethod = IocManager.Instance.GetService(node.Ioc); + WorkFlow.WfMethodParameter wfMethodParameter = new WorkFlow.WfMethodParameter() + { + ProcessId = processId, + UnitName = task.UnitName, + TaskId = prevTaskId, + Code = code, + UserId = createUser.Id, + UserAccount = createUser.Account, + CompanyId = createUser.CompanyId, + DepartmentId = createUser.DepartmentId + }; + await iWorkFlowMethod.Execute(wfMethodParameter); + } + break; + case "3": + if (!string.IsNullOrEmpty(node.ApiUrl)) + { + await HttpMethods.Post(node.ApiUrl, Json.ToJson(param)); + } + break; + + } + + /*脚本执行成功*/ + WFTaskLog wfTaskLogEntity = new WFTaskLog() + { + Des = "脚本执行成功", + TaskType = 99, + ProcessId = processId, + TaskId = task.Id, + UnitId = task.UnitId, + UnitName = task.UnitName + }; + await AddLog(wfTaskLogEntity); + return true; + } + catch (Exception ex) + { + WFTaskLog wfTaskLogEntity = new WFTaskLog() + { + Des = string.Format("脚本执行异常:{0}", ex.Message), + TaskType = 99, + ProcessId = processId, + TaskId = task.Id, + UnitId = task.UnitId, + UnitName = task.UnitName + }; + await AddLog(wfTaskLogEntity); + return false; + } + } + + + /// + /// 执行脚本 + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + private async Task ExecuteScript(string processId, string code, WFUserInfo createUser, string executeType, string sqlDb, string sqlStr, string apiUrl, string iocName) + { + var param = new + { + processId, + userId = createUser.Id, + userAccount = createUser.Account, + companyId = createUser.CompanyId, + departmentId = createUser.DepartmentId, + code + }; + + try + { + switch (executeType) + { + case "1": + if (!string.IsNullOrEmpty(sqlDb) && !string.IsNullOrEmpty(sqlStr)) + { + //await wfProcessSerive.BaseRepository(sqlDb).ExecuteSql(sqlStr, param); + await client.Ado.ExecuteCommandAsync(sqlStr, param); + } + break; + case "2": + if (!string.IsNullOrEmpty(iocName) && IocManager.Instance.IsRegistered(iocName)) + { + WorkFlow.IWorkFlowMethod iWorkFlowMethod = IocManager.Instance.GetService(iocName); + WorkFlow.WfMethodParameter wfMethodParameter = new WorkFlow.WfMethodParameter() + { + ProcessId = processId, + Code = code, + UserId = createUser.Id, + UserAccount = createUser.Account, + CompanyId = createUser.CompanyId, + DepartmentId = createUser.DepartmentId + }; + await iWorkFlowMethod.Execute(wfMethodParameter); + } + break; + case "3": + if (!string.IsNullOrEmpty(apiUrl)) + { + await HttpMethods.Post(apiUrl, Json.ToJson(param)); + } + break; + } + return true; + } + catch (Exception ex) + { + return false; + } + } + + + /// + /// 执行脚本(撤销) + /// + /// + /// + /// + /// + /// + /// + private async Task ExecuteRevokeScript(string unitId, string unitName, string processId, WFUserInfo createUser, IWFEngine iWFEngine) + { + var param = new + { + processId, + userId = createUser.Id, + userAccount = createUser.Account, + companyId = createUser.CompanyId, + departmentId = createUser.DepartmentId, + code = "revoke", + }; + try + { + var node = iWFEngine.GetNode(unitId); + switch (node.ExecuteType) + { + case "1": + if (!string.IsNullOrEmpty(node.SqlDb) && !string.IsNullOrEmpty(node.SqlStrRevoke)) + { + //await wfProcessSerive.BaseRepository(node.SqlDb).ExecuteSql(node.SqlStrRevoke, param); + await client.Ado.ExecuteCommandAsync(node.SqlStrRevoke, param); + } + break; + case "2": + if (!string.IsNullOrEmpty(node.IocRevoke) && IocManager.Instance.IsRegistered(node.IocRevoke)) + { + WorkFlow.IWorkFlowMethod iWorkFlowMethod = IocManager.Instance.GetService(node.IocRevoke); + WorkFlow.WfMethodParameter wfMethodParameter = new WorkFlow.WfMethodParameter() + { + ProcessId = processId, + UnitName = unitName, + Code = "revoke", + UserId = createUser.Id, + UserAccount = createUser.Account, + CompanyId = createUser.CompanyId, + DepartmentId = createUser.DepartmentId + }; + await iWorkFlowMethod.Execute(wfMethodParameter); + } + break; + case "3": + if (!string.IsNullOrEmpty(node.ApiUrlRevoke)) + { + await HttpMethods.Post(node.ApiUrlRevoke, Json.ToJson(param)); + } + break; + + } + } + catch (Exception ex) + { + WFTaskLog wfTaskLogEntity = new WFTaskLog() + { + Des = string.Format("脚本执行异常:{0}", ex.Message), + TaskType = 99, + ProcessId = processId, + UnitId = unitId, + UnitName = $"{unitName}-撤销" + }; + await AddLog(wfTaskLogEntity); + + } + + } + + /// + /// 普通审核 + /// + /// + /// + /// + /// + /// 下一个节点 + /// + /// + /// + private async Task AuditNode(IWFEngine iWFEngine, WFTask taskEntity, string code, string name, string des, string stampImg, string nextId = "") + { + // 下一部需要执行的任务 + var taskList = await iWFEngine.GetTask(taskEntity.UnitId, code, nextId); + if (taskList.FindAll(t => t.Type != 10).Count == 0) + { + throw (new Exception("找不到下一个流转节点!")); + } + + List myTaskList; + // 处理任务并更新数据 + using (var uwo = UnitWork.CreateContext()) + { + var process = await uwo.WFProcess.GetByIdAsync(taskEntity.ProcessId); + await uwo.Db.Updateable() + .SetColumns(p => new WFProcess { IsStart = 1, IsCancel = 0 }) + .SetColumnsIF(taskList.FindIndex(t => t.Type == 100) != -1, p => p.IsFinished == 1) + .SetColumnsIF(taskList.FindIndex(t => t.Type == 4) != -1, p => p.IsAgain == 1) + .Where(p => p.Id == taskEntity.ProcessId) + .ExecuteCommandAsync(); + if (taskList.FindIndex(t => t.Type == 100) != -1) + { + if (process.IsChild == 1) + { + await ChildrenEndTask(process.ParentProcessId, process.ParentNodeId, code, taskEntity, nextId); + } + } + + // 更新任务状态 + taskEntity.IsAgree = code == "disagree" ? 0 : 1; + taskEntity.State = 3; + if (taskEntity.Type == 7) + { + var currentUser = _auth.GetCurrentUser().User; + taskEntity.UserId = currentUser.Id.ToString(); + taskEntity.UserName = currentUser.Name; + } + await uwo.WFTask.UpdateAsync(taskEntity); + + // 更新上一个流转过来的任务,提示他无法被撤销 + await CloseTaskLogCancel(taskEntity.ProcessId, taskEntity.PrevTaskId); + + // 关闭同一个节点,其他人的任务 + await CloseTask(taskEntity.ProcessId, taskEntity.Token, taskEntity.Id); + + myTaskList = await ExecuteWFTaskEntity(taskList, iWFEngine, taskEntity.ProcessId, taskEntity.Token, taskEntity.Id); + foreach (var item in myTaskList) + { + if (item.Type == 7) + { + await uwo.WFTaskUser.InsertRangeAsync(item.WFTaskUser); + } + await Add(item); + } + + await CreateTaskLog(taskEntity, code, name, des, stampImg); + + uwo.Commit(); + } + + + // 脚本执行 + var scriptTaskList = myTaskList.FindAll(t => t.Type == 10); + foreach (var item in scriptTaskList) + { + await ExecuteScript(item, code, taskEntity.ProcessId, taskEntity.Id, nextId, iWFEngine.CreateUser, iWFEngine, taskEntity,des); + } + } + + + /// + /// 子流程结束,获取父流程下一任务 + /// + /// 父流程id + /// 父流程的节点id + /// 上一任务code(类似agress,degress) + /// 任务 + /// 下一节点 + /// + private async Task ChildrenEndTask(string processId, string unitId, string code, WFTask taskEntity, string nextId = "") + { + var process = await GetEntity(processId); + var iWFEngine = await Bootstraper("", processId, null, null); + // 下一部需要执行的任务 + var taskList = await iWFEngine.GetTask(unitId, code, nextId); + if (taskList.FindAll(t => t.Type != 10).Count == 0) + { + throw (new Exception("找不到下一个流转节点!")); + } + + // 处理任务并更新数据 + //wfProcessSerive.BeginTrans(); + List myTaskList; + using (var uwo = UnitWork.CreateContext()) + { + try + { + // 更新流程状态 + await uwo.Db.Updateable() + .SetColumns(p => new WFProcess { IsStart = 1, IsCancel = 0 }) + .SetColumnsIF(taskList.FindIndex(t => t.Type == 100) != -1, p => p.IsFinished == 1) + .SetColumnsIF(taskList.FindIndex(t => t.Type == 4) != -1, p => p.IsAgain == 1) + .Where(p => p.Id == processId) + .ExecuteCommandAsync(); + + if (taskList.FindIndex(t => t.Type == 100) != -1) + { + if (process.IsChild == 1) + { + await ChildrenEndTask(process.ParentProcessId, process.ParentNodeId, code, taskEntity, nextId); + } + } + + myTaskList = await ExecuteWFTaskEntity(taskList, iWFEngine, processId, taskEntity.Token, taskEntity.Id); + foreach (var item in myTaskList) + { + if (item.Type == 7) + { + await uwo.WFTaskUser.InsertRangeAsync(item.WFTaskUser); + } + await Add(item); + } + } + catch + { + throw; + } + uwo.Commit(); + } + // 脚本执行 + var scriptTaskList = myTaskList.FindAll(t => t.Type == 10); + foreach (var item in scriptTaskList) + { + await ExecuteScript(item, code, processId, taskEntity.Id, nextId, iWFEngine.CreateUser, iWFEngine, taskEntity,""); + } + } + + + private async Task AuditNodeByCountersign(IWFEngine iWFEngine, WFTask taskEntiy, string code, string name, string des, string stampImg) + { + taskEntiy.IsAgree = code == "disagree" ? 0 : 1; + var node = iWFEngine.GetNode(taskEntiy.UnitId); + var list = (List)await GetLastTaskList(taskEntiy.ProcessId, taskEntiy.UnitId); + var myIndex = list.FindIndex(t => t.Id == taskEntiy.Id); + if (myIndex == -1) + { + throw (new Exception("会签任务记录异常无法审核!")); + } + + if (taskEntiy.IsAgree == 0) + { + if (node.CountersignType == "1") + { + // 并行 + if (node.IsCountersignAll) + { + // 等待 + if (list.FindIndex(t => t.Id != taskEntiy.Id && t.State == 1) != -1) + { + // 表示还有人没有审核 + // 表示还有任务没有完成 + using (var uwo = UnitWork.CreateContext()) + { + WFProcess processEntity = new WFProcess() + { + Id = taskEntiy.ProcessId, + IsStart = 1, + IsCancel = 0, + }; + await uwo.WFProcess.UpdateAsync(processEntity); + taskEntiy.State = 3; + await uwo.WFTask.UpdateAsync(taskEntiy); + await CreateTaskLog(taskEntiy, code, name, des, stampImg); + + uwo.Commit(); + } + } + else + { + var num = await GetTaskUserMaxNum(taskEntiy.ProcessId, taskEntiy.UnitId); + var fnum = list.FindAll(t => t.State == 3 && t.IsAgree == 0).Count + 1; + if ((num - fnum) * 100 / num >= node.CountersignAllType) + { + await AuditNode(iWFEngine, taskEntiy, "agree", "同意", des, stampImg); + } + else + { + await AuditNode(iWFEngine, taskEntiy, code, name, des, stampImg); + } + } + } + else + { + // 不等待,每次都需要计算通过率 + var num = await GetTaskUserMaxNum(taskEntiy.ProcessId, taskEntiy.UnitId); + var fnum = list.FindAll(t => t.State == 3 && t.IsAgree == 0).Count + 1; + if ((num - fnum) * 100 / num >= node.CountersignAllType)// 表示还有通过的希望 + { + using (var uwo = UnitWork.CreateContext()) + { + WFProcess processEntity = new WFProcess() + { + Id = taskEntiy.ProcessId, + IsStart = 1, + IsCancel = 0, + }; + await uwo.WFProcess.UpdateAsync(processEntity); + taskEntiy.State = 3; + await uwo.WFTask.UpdateAsync(taskEntiy); + await CreateTaskLog(taskEntiy, code, name, des, stampImg); + + uwo.Commit(); + } + } + else + { + await AuditNode(iWFEngine, taskEntiy, code, name, des, stampImg); + } + } + } + else + { + // 串行 + await AuditNode(iWFEngine, taskEntiy, code, name, des, stampImg); + } + } + else + { + if (node.CountersignType == "1") + { + // 并行 + + // 如果不等待,不同意直接跳转 + if (node.IsCountersignAll) + { + if (list.FindIndex(t => t.Id != taskEntiy.Id && t.State == 1) != -1) // 表示还有人没有处理完 + { + // 表示还有任务没有完成 + using (var uwo = UnitWork.CreateContext()) + { + WFProcess processEntity = new WFProcess() + { + Id = taskEntiy.ProcessId, + IsStart = 1, + IsCancel = 0, + }; + await uwo.WFProcess.UpdateAsync(processEntity); + taskEntiy.State = 3; + await uwo.WFTask.UpdateAsync(taskEntiy); + await CreateTaskLog(taskEntiy, code, name, des, stampImg); + uwo.Commit(); + } + } + else + { + var num = await GetTaskUserMaxNum(taskEntiy.ProcessId, taskEntiy.UnitId); + var fnum = list.FindAll(t => t.State == 3 && t.IsAgree == 0).Count; + if ((num - fnum) * 100 / num >= node.CountersignAllType) + { + await AuditNode(iWFEngine, taskEntiy, "agree", "同意", des, stampImg); + } + else + { + await AuditNode(iWFEngine, taskEntiy, "disagree", "不同意", des, stampImg); + } + } + } + else + { + var num = await GetTaskUserMaxNum(taskEntiy.ProcessId, taskEntiy.UnitId); + var snum = list.FindAll(t => t.State == 3 && t.IsAgree == 1).Count; + if (snum * 100 / num >= node.CountersignAllType) + { + await AuditNode(iWFEngine, taskEntiy, "agree", "同意", des, stampImg); + } + else + { + using (var uwo = UnitWork.CreateContext()) + { + WFProcess processEntity = new WFProcess() + { + Id = taskEntiy.ProcessId, + IsStart = 1, + IsCancel = 0, + }; + await uwo.WFProcess.UpdateAsync(processEntity); + taskEntiy.State = 3; + await uwo.WFTask.UpdateAsync(taskEntiy); + await CreateTaskLog(taskEntiy, code, name, des, stampImg); + + uwo.Commit(); + } + } + } + } + else + { + // 串行 + if (myIndex == list.Count - 1) // 表示最后一个人审核完成,然后往下执行 + { + await AuditNode(iWFEngine, taskEntiy, code, name, des, stampImg); + } + else + { + // 表示还有任务没有完成 + using (var uwo = UnitWork.CreateContext()) + { + WFProcess processEntity = new WFProcess() + { + Id = taskEntiy.ProcessId, + IsStart = 1, + IsCancel = 0, + }; + await uwo.WFProcess.UpdateAsync(processEntity); + taskEntiy.State = 3; + await uwo.WFTask.UpdateAsync(taskEntiy); + await CreateTaskLog(taskEntiy, code, name, des, stampImg); + + + if (node.CountersignType == "2") + { + // 串行,更新上一个任务的撤销操作,开启下一个任务 + if (myIndex == 0) + { + await CloseTaskLogCancel(taskEntiy.ProcessId, taskEntiy.PrevTaskId); + } + else + { + await CloseTaskLogCancel(list[myIndex - 1].Id); + } + await uwo.WFTask.UpdateAsync(new WFTask { Id = list[myIndex + 1].Id, State = 1 }); + } + var flag = uwo.Commit(); + } + } + } + } + } + + /// + /// 创建日志 + /// + /// + /// + /// + /// + /// + /// + private async Task CreateTaskLog(WFTask taskEntiy, string code, string name, string des, string stampImg) + { + + WFTaskLog wfTaskLogEntity = new WFTaskLog() + { + Des = $"【{name}】{des}", + TaskType = taskEntiy.Type, + ProcessId = taskEntiy.ProcessId, + UnitId = taskEntiy.UnitId, + UnitName = taskEntiy.UnitName, + IsAgree = taskEntiy.IsAgree, + OperationCode = code, + OperationName = name, + Token = taskEntiy.Token, + PrevUnitId = taskEntiy.PrevUnitId, + PrevUnitName = taskEntiy.PrevUnitName, + TaskId = taskEntiy.Id, + StampImg = stampImg, + IsCancel = 1, + IsLast = 1 + + }; + + await UpdateLog(taskEntiy.ProcessId, taskEntiy.UnitId); + await AddLog(wfTaskLogEntity); + } + #endregion + + + public async Task> Supervise(string processId,int supervise = 1) + { + // todo 实现督办 + + var iWFEngine = await Bootstraper(string.Empty, processId, null, null); + // 获取未完成的任务 + var taskList = await GetUnFinishTaskList(processId); + using (var uwo = UnitWork.CreateContext()) + { + + foreach (var item in taskList) + { + if (item.Type == 1 || item.Type == 3 || item.Type == 5 || item.Type == 6 || item.Type == 7) + { + + // 督办时间督办标志 + item.IsSupervise = supervise; + item.SuperviseTime = DateTime.Now; + item.SuperviseId = _auth.GetCurrentUser().User.Id; + // 发送消息 + var msg = $"【审核】【督办】{item.ProcessTitle},{item.UnitName}"; + var userList = new List(); + // todo 单体任务处理 + userList.Add(item.UserId); + var node = iWFEngine.GetNode(item.UnitId); + //await _imMsgIBLL.SendMsg("IMWF", userList, msg, node.MessageType, item.F_Token); + + await uwo.WFTask.UpdateAsync(item); + } + } + + //await wfProcessSerive.Update(new WFProcessEntity() { F_IsUrge = 1, F_Id = processId }); + + var wfTaskLogEntity = new WFTaskLog + { + Des = "督办审核", + TaskType = 98, + ProcessId = processId, + UnitId = iWFEngine.StartNode.Id, + UnitName = string.IsNullOrEmpty(iWFEngine.StartNode.Name) ? "开始节点" : iWFEngine.StartNode.Name, + CreateDate = DateTime.Now, + UserId = _auth.GetUserId(), + UserName = _auth.GetUserNickName(), + Id = Guid.NewGuid().ToString() + }; + + await uwo.WFTaskLog.InsertAsync(wfTaskLogEntity); + + var flag = uwo.Commit(); + return new Response + { + Result = flag, + Message = flag ? "success" : "error" + }; + } + + } + + + + #region 撤回 + /// + /// 撤回流程 + /// + /// 流程任务主键 + /// 流程审批操作码agree 同意 disagree 不同意 lrtimeout 超时 + /// 流程审批操名称 + /// 审批意见 + /// 下一节点指定审核人 + /// 盖章图片 + /// 盖章图片密码 + /// 下一个审核节点 + public async Task RetractFlow(string taskId, string code, string name, string des, Dictionary nextUsers, string stampImg, string stampPassWord, string nextId) + { + var taskEntiy = await client.Queryable().Where(a => a.Id == taskId).FirstAsync(); + if (taskEntiy == null) + { + throw (new Exception("找不到对应流程任务!")); + } + else if (taskEntiy.State != 3&& taskEntiy.State != 4) + { + throw (new Exception("该任务未完成!")); + } + + //后期改 + stampImg = ""; + //stampImg = await _wFStampIBLL.ToWfImg(stampImg, stampPassWord); + + var iWFEngine = await Bootstraper("", taskEntiy.ProcessId, null, nextUsers); + + // 1.判断任务类型 1 普通任务 5 会签任务 + if (taskEntiy.Type == 1 || taskEntiy.Type == 7) + { + await RetractNode(iWFEngine, taskEntiy, code, name, des, stampImg, nextId); + } + else + { + throw (new Exception("该任务无法审核!")); + } + + //await _imMsgIBLL.VirtualDeleteByContentId(taskEntiy.F_Token); // 更新消息 + + // 发送消息 + var msg = $"【提醒】{iWFEngine.Config.Params.Title},{taskEntiy.UnitName}已被撤回"; + var userList = new List(); + userList.Add(iWFEngine.CreateUser.Id); + var node = iWFEngine.StartNode; + //await _imMsgIBLL.SendMsg("IMWF", userList, msg, node.MessageType, iWFEngine.Config.Params.ProcessId); + } + + /// + /// 撤回 + /// + /// + /// + /// + /// + /// 下一个节点 + /// + /// + /// + private async Task RetractNode(IWFEngine iWFEngine, WFTask taskEntity, string code, string name, string des, string stampImg, string nextId = "") + { + // 下一部需要执行的任务 + var taskList = await iWFEngine.GetTask(taskEntity.UnitId, code, nextId); + + List myTaskList; + //修改原有任务的IsLast状态 + await client.Updateable() + .SetColumns(t => new WFTask() { IsLast = 0 }) + .Where(t => t.Id==taskEntity.Id) + .ExecuteCommandAsync(); + + //删除该节点之后生成的所有任务 + var dellist=client.Queryable().Where(r=>r.CreateDate>taskEntity.CreateDate&&r.ProcessId==taskEntity.ProcessId).ToList(); + await client.Deleteable(dellist).ExecuteCommandAsync(); + // 处理任务并更新数据 + using (var uwo = UnitWork.CreateContext()) + { + var process = await uwo.WFProcess.GetByIdAsync(taskEntity.ProcessId); + await uwo.Db.Updateable() + .SetColumns(p => new WFProcess { IsStart = 1, IsCancel = 0 }) + .SetColumns(p=>p.IsFinished==0) + .SetColumnsIF(taskList.FindIndex(t => t.Type == 100) != -1, p => p.IsFinished == 1) + .SetColumnsIF(taskList.FindIndex(t => t.Type == 4) != -1, p => p.IsAgain == 1) + .Where(p => p.Id == taskEntity.ProcessId) + .ExecuteCommandAsync(); + if (taskList.FindIndex(t => t.Type == 100) != -1) + { + if (process.IsChild == 1) + { + await ChildrenEndTask(process.ParentProcessId, process.ParentNodeId, code, taskEntity, nextId); + } + } + + var newTask = new WFTask + { + Id = Guid.NewGuid().ToString(), + Type = taskEntity.Type, + ProcessId = taskEntity.ProcessId, + Token = Guid.NewGuid().ToString(), + UnitId = taskEntity.UnitId, + UnitName = taskEntity.UnitName, + PrevUnitId = taskEntity.PrevUnitId, + PrevUnitName = taskEntity.PrevUnitName, + PrevToken = taskEntity.Token, + PrevTaskId = taskEntity.Id, + UserId = taskEntity.UserId, + UserName = taskEntity.UserName, + UserDepartmentId = taskEntity.UserDepartmentId, + UserCompanyId = taskEntity.UserCompanyId, + State = 1, + Sort = taskEntity.Sort, + IsReject = 0, + ChildProcessId = taskEntity.ChildProcessId, + ChildSchemeInfoCode = taskEntity.ChildSchemeInfoCode, + + TimeoutInterval = taskEntity.TimeoutInterval, + TimeoutStrategy = taskEntity.TimeoutStrategy, + + IsBatchAudit = taskEntity.IsBatchAudit, + ProcessUserId = iWFEngine.CreateUser.Id, + ProcessUserName = iWFEngine.CreateUser.Name, + ProcessCode = iWFEngine.Config.Params.SchemeCode, + ProcessTitle = iWFEngine.Config.Params.Title, + CreateDate = DateTime.Now, + CreateUserId = _auth.GetUserId(), + CreateUserName = _auth.GetUserName(), + IsLast=1, + IsRetract=1 + }; + await uwo.WFTask.InsertAsync(newTask); + + // 更新上一个流转过来的任务,提示他无法被撤销 + await CloseTaskLogCancel(taskEntity.ProcessId, taskEntity.PrevTaskId); + + // 关闭同一个节点,其他人的任务 + await CloseTask(taskEntity.ProcessId, taskEntity.Token, taskEntity.Id); + + myTaskList = await ExecuteWFTaskEntity(taskList, iWFEngine, taskEntity.ProcessId, taskEntity.Token, taskEntity.Id); + foreach (var item in myTaskList) + { + if (item.Type == 7) + { + await uwo.WFTaskUser.InsertRangeAsync(item.WFTaskUser); + } + await Add(item); + } + + await CreateTaskLog(taskEntity, code, name, des, stampImg); + + uwo.Commit(); + } + + + // 脚本执行 + var scriptTaskList = myTaskList.FindAll(t => t.Type == 10); + foreach (var item in scriptTaskList) + { + await ExecuteScript(item, code, taskEntity.ProcessId, taskEntity.Id, nextId, iWFEngine.CreateUser, iWFEngine, taskEntity, des); + } + } + + + #endregion + } +} diff --git a/OpenAuth.App/BaseApp/WFProcess/WFProcessApp1.cs b/OpenAuth.App/BaseApp/WFProcess/WFProcessApp1.cs new file mode 100644 index 0000000..9f9e52a --- /dev/null +++ b/OpenAuth.App/BaseApp/WFProcess/WFProcessApp1.cs @@ -0,0 +1,239 @@ +using Infrastructure; +using Infrastructure.Extensions; +using OpenAuth.Repository.Domain; +using SqlSugar; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace OpenAuth.App +{ + public partial class WFProcessApp + { + #region 任务 + + public async Task> GetListByKeyValues(string[] keyValues) + { + return await client.Queryable().Where(t => keyValues.Contains(t.Id.ToString())).ToListAsync(); + //return await client.Queryable().Where(t => t.DeleteMark == 0 && keyValues.Contains(t.Id.ToString())); + } + + public async Task CloseTask(string processId, string token, string taskId) + { + var expression = Expressionable.Create() + .And(t => t.ProcessId == processId && t.Token == token) + .AndIF(!string.IsNullOrEmpty(taskId), t => t.Id != taskId).ToExpression(); + + + + + await client.Updateable().SetColumns(a => new WFTask() { State = 4 }) + .Where(expression) + .ExecuteCommandAsync(); + } + + public async Task CloseTaskLogCancel(string processId, string taskId) + { + await client.Updateable() + .SetColumns(a => new WFTaskLog { IsCancel = 0 }) + .Where(a => a.ProcessId == processId && a.TaskId == taskId).ExecuteCommandAsync(); + } + + public async Task CloseTaskLogCancel(string taskId) + { + await client.Updateable() + .SetColumns(a => new WFTaskLog { IsCancel = 0 }) + .Where(a => a.TaskId == taskId).ExecuteCommandAsync(); + } + + public async Task GetTaskUserMaxNum(string processId, string unitId) + { + StringBuilder strSql = new StringBuilder(); + strSql.Append(" select conut(1) from wf_task t where ProcessId = @processId AND UnitId =@unitId AND (Type = 1 OR Type = 3 OR Type = 5) {LEARUN_SASSID} group by Token order by conut(1) "); + + var pars = client.Ado.GetParameters(new { processId, unitId }); + var data = await client.Ado.GetDataTableAsync(strSql.ToString(), pars); + + if (data.Rows.Count > 0) + { + return (data.Rows[0][0]).ToInt(); + } + else + { + return 0; + } + } + + public Task> GetLastFinishTaskList(string processId, string unitId) + { + return client.Queryable().Where(t => t.ProcessId == processId && t.UnitId == unitId && (t.Type == 1||t.Type==7) && t.IsLast == 1 && t.State == 3).ToListAsync(); + } + + public async Task> GetLastTaskList(string processId, string unitId) + { + return await client.Queryable() + .Where(t => t.ProcessId == processId && t.UnitId == unitId && t.IsLast == 1 && (t.Type == 1 || t.Type == 3 || t.Type == 4 || t.Type == 5||t.Type==7)) + .OrderBy(t => t.Sort) + .ToListAsync(); + } + + + + public async Task GetLastNotRejectTask(string processId, string unitId) + { + return await client.Queryable() + .Where(t => t.ProcessId == processId + && t.UnitId == unitId + && t.IsReject != 1 + && t.IsRetract!=1 + && (t.Type == 1 || t.Type == 3 || t.Type == 5||t.Type==7)) + .OrderByDescending(t => t.CreateDate) + .FirstAsync(); + } + + //public async Task CloseTask(string processId, string token, string taskId) + //{ + + // await client.Updateable() + // .SetColumns(t => new WFTask() { State = 4 }) + // .Where(t => t.ProcessId == processId && t.Token == token) + // .WhereIF(!string.IsNullOrEmpty(taskId), t => t.Id != taskId) + // .ExecuteCommandAsync(); + //} + + public async Task CloseTask(string processId, string unitId, int type, string taskId) + { + await client.Updateable() + .SetColumns(t => new WFTask() { State = 4, UpdateTaskId = taskId }) + .Where(t => t.ProcessId == processId && t.UnitId == unitId && t.Type == type) + .ExecuteCommandAsync(); + } + + public async Task Update(string processId, string unitId) + { + await client.Updateable() + .SetColumns(t => new WFTask() { IsLast = 0 }) + .Where(t => t.ProcessId == processId && t.UnitId == unitId) + .ExecuteCommandAsync(); + } + + public async Task Add(WFTask wfTaskEntity) + { + wfTaskEntity.CreateDate = DateTime.Now; + wfTaskEntity.CreateUserId = _auth.GetUserId(); + wfTaskEntity.CreateUserName = _auth.GetUserName(); + wfTaskEntity.Id= string.IsNullOrEmpty(wfTaskEntity.Id)?Guid.NewGuid().ToString(): wfTaskEntity.Id; + wfTaskEntity.IsLast = 1; + var flag= await client.Insertable(wfTaskEntity).ExecuteCommandAsync(); + //if (flag!=0&&wfTaskEntity.TimeoutNotice!=null&&wfTaskEntity.TimeoutInterval!=0) + //{ + // DateTime outtime=(DateTime)wfTaskEntity.TimeoutNotice; + // DateTime overtime = outtime.AddHours((int)wfTaskEntity.TimeoutInterval); + // AddOpenJob(wfTaskEntity, outtime, overtime); + //} + } + + public async Task> GetUnFinishTaskList(string processId) + { + return await client.Queryable().Where(t => t.ProcessId == processId && t.State == 1 && (t.Type == 1 || t.Type == 2 || t.Type == 3 || t.Type == 4 || t.Type == 5 || t.Type == 6||t.Type==7)).ToListAsync(); + } + + public async Task> GetList(string processId, int type, string prevTaskId) + { + return await client.Queryable().Where(t => t.ProcessId == processId && t.Type == type && t.PrevTaskId == prevTaskId).ToListAsync(); + } + + public async Task GetTaskEntity(string keyValue) + { + return await client.Queryable() + .Where(a => a.Id == keyValue).FirstAsync(); + } + + public async Task Delete(string processId, string prevTaskId) + { + await client.Deleteable().Where(t => t.ProcessId == processId && t.PrevTaskId == prevTaskId).ExecuteCommandAsync(); + } + + public async Task DeleteByFirstId(string id) + { + await client.Deleteable().Where(t => t.FirstId == id).ExecuteCommandAsync(); + } + + public async Task OpenTask(string processId, string unitId, int type, string taskId) + { + await client.Updateable().SetColumns(a => new WFTask { State = 1, UpdateTaskId = taskId }) + .Where(t => t.ProcessId == processId && t.UnitId == unitId && t.Type == type) + .ExecuteCommandAsync(); + } + #endregion + + #region 日志 + + public async Task AddLog(WFTaskLog wfTaskLogEntity) + { + wfTaskLogEntity.CreateDate = DateTime.Now; + wfTaskLogEntity.UserId = _auth.GetUserId(); + wfTaskLogEntity.UserName = _auth.GetUserNickName(); + wfTaskLogEntity.Id = Guid.NewGuid().ToString(); + await client.Insertable(wfTaskLogEntity).ExecuteCommandAsync(); + } + public async Task> GetLogList(string processId, string unitId) + { + return await client.Queryable() + .Where(t => t.ProcessId == processId && t.UnitId == unitId) + .OrderByDescending(t => t.CreateDate) + .ToListAsync(); + } + + public async Task UpdateLog(string processId, string unitId) + { + var userId = _auth.GetUserId(); + + await this.client.Updateable() + .SetColumns(t => new WFTaskLog { IsLast = 0 }) + .Where(t => t.ProcessId == processId && t.UnitId == unitId && t.IsLast == 1 && t.UserId == userId) + .ExecuteCommandAsync(); + } + + public async Task GetLogEntity(string taskId) + { + return await client.Queryable().Where(t => t.TaskId == taskId).FirstAsync(); + } + + public async Task DeleteLog(string processId, string taskId) + { + await client.Deleteable().Where(t => t.ProcessId == processId && t.TaskId == taskId && t.IsLast == 1).ExecuteCommandAsync(); + } + + public async Task DeleteLogByTaskId(string taskId) + { + await client.Deleteable().Where(t => t.TaskId == taskId).ExecuteCommandAsync(); + } + + public async Task Delete(string processId) + { + await client.Deleteable().Where(t => t.ProcessId == processId).ExecuteCommandAsync(); + } + #endregion + + public async Task GetUserInfo(string userId) + { + if (userId == "-1") + { + return new SysUser + { + Account = Define.SYSTEM_USERNAME, + Name = "超级管理员", + Id = -1 + }; + } + else + { + return await client.Queryable().FirstAsync(a => a.Id.ToString() == userId); + } + + } + } +} diff --git a/OpenAuth.App/BaseApp/WFProcess/WFScheme.cs b/OpenAuth.App/BaseApp/WFProcess/WFScheme.cs new file mode 100644 index 0000000..8644a06 --- /dev/null +++ b/OpenAuth.App/BaseApp/WFProcess/WFScheme.cs @@ -0,0 +1,82 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace OpenAuth.App.WorkFlow +{ + public class WFScheme + { + /// + /// 流程图数据 + /// + public List WfData { get; set; } + /// + /// 撤销执行 动作类型 1执行SQL 2.NET方法 3第三方接口 + /// + public string UndoType { get; set; } + /// + /// 撤销执行 数据库编码 + /// + public string UndoDbCode { get; set; } + /// + /// 撤销执行 SQL语句 + /// + public string UndoDbSQL { get; set; } + /// + /// 撤销执行 .NET注入类名 + /// + public string UndoIOCName { get; set; } + /// + /// 撤销执行 接口地址 + /// + public string UndoUrl { get; set; } + + + /// + /// 作废操作 动作类型 1执行SQL 2.NET方法 3第三方接口 + /// + public string DeleteType { get; set; } + /// + /// 作废操作 数据库编码 + /// + public string DeleteDbCode { get; set; } + /// + /// 作废操作 SQL语句 + /// + public string DeleteDbSQL { get; set; } + /// + /// 作废操作 .NET注入类名 + /// + public string DeleteIOCName { get; set; } + /// + /// 作废操作 接口地址 + /// + public string DeleteUrl { get; set; } + + /// + /// 删除草稿 动作类型 1执行SQL 2.NET方法 3第三方接口 + /// + public string DeleteDraftType { get; set; } + /// + /// 删除草稿 数据库编码 + /// + public string DeleteDraftDbCode { get; set; } + /// + /// 删除草稿 SQL语句 + /// + public string DeleteDraftDbSQL { get; set; } + /// + /// 删除草稿 .NET注入类名 + /// + public string DeleteDraftIOCName { get; set; } + /// + /// 删除草稿 接口地址 + /// + public string DeleteDraftUrl { get; set; } + + + + } +} diff --git a/OpenAuth.App/BaseApp/WFProcess/WFTask.cs b/OpenAuth.App/BaseApp/WFProcess/WFTask.cs new file mode 100644 index 0000000..a4c3e41 --- /dev/null +++ b/OpenAuth.App/BaseApp/WFProcess/WFTask.cs @@ -0,0 +1,113 @@ +using OpenAuth.App.Config; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace OpenAuth.App.WorkFlow +{ + public class WFTask + { + /// + /// 流程单元ID + /// + public string UnitId { get; set; } + /// + /// 任务名称 + /// + public string Name { get; set; } + /// + /// 任务令牌 + /// + public string Token { get; set; } + /// + /// 任务类型 1.审核任务 2传阅任务 3.子流程 4.重新创建 5.会签任务 10.脚本任务 21.等待任务(系统自己完成)22.取消等待任务 23.找不到审核人直接跳过 24.自动审核规则跳过 25.会签任务记录 26 更新任务状态 100 结束任务 + /// + public int Type { get; set; } + /// + /// 上一个流程单元ID + /// + public string PrevUnitId { get; set; } + /// + /// 上一个流程单元名称 + /// + public string PrevUnitName { get; set; } + /// + /// 是否是驳回任务 + /// + public bool IsReject { get; set; } + /// + /// 是否允许批量审核 + /// + public bool IsBatchAudit { get; set; } + + /// + /// 通知策略 1.短信 2.邮箱 3.微信 4.IM + /// + public string MessageType { get; set; } + + /// + /// 是否超时通知 + /// + public bool IsOvertimeMessage { get; set; } + /// + /// 第一次通知 单位(时) + /// + public int OvertimeMessageStart { get; set; } + /// + /// 间隔通知 单位(时) + /// + public int OvertimeMessageInterval { get; set; } + /// + /// 超时流转时间 单位(时) + /// + public int OvertimeGo { get; set; } + /// + /// 超时通知策略 1.短信 2.邮箱 3.微信 4.IM + /// + public string OvertimeMessageType { get; set; } + + /// + /// 脚本执行类型 1SQL 2接口 3IOC + /// + public string ExecuteType { get; set; } + /// + /// 执行SQL数据库编码 + /// + public string SqlDb { get; set; } + /// + /// 执行SQL语句 + /// + public string SqlStr { get; set; } + /// + /// 撤回的时候执行SQL语句 + /// + public string SqlStrRevoke { get; set; } + /// + /// 调用接口 + /// + public string ApiUrl { get; set; } + /// + /// 撤回的时候调用接口 + /// + public string ApiUrlRevoke { get; set; } + /// + /// 注入类名 + /// + public string Ioc { get; set; } + /// + /// 撤回的时候注入类名 + /// + public string IocRevoke { get; set; } + + /// + /// 处理人信息 + /// + public WFUserInfo User { get; set; } + /// + /// 子流程的流程模板id + /// + public string ChildSchemeInfoId { get; set; } + } +} diff --git a/OpenAuth.App/BaseApp/WFProcess/WFUnit.cs b/OpenAuth.App/BaseApp/WFProcess/WFUnit.cs new file mode 100644 index 0000000..5aca1ed --- /dev/null +++ b/OpenAuth.App/BaseApp/WFProcess/WFUnit.cs @@ -0,0 +1,170 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace OpenAuth.App +{ + public class WFUnit + { + #region 节点原始属性 + /// + /// 流程单元类型 + /// 线条 myline + /// 开始节点 startEvent + /// 结束节点 endEvent + /// 并行网关 gatewayAnd 并行网关会等待所有分支汇入才往下执行,所有出口分支都会被执行 + /// 排他网关 gatewayXor 排他网关不会等待所有分支汇入才往下执行,只要有分支汇入就会往下执行,出口分支只会执行一条(条件为true,如果多条出口分支条件为true也执行一条) + /// 包容网关 gatewayInclusive 包容网关会等待所有分支汇入才往下执行,出口分支能执行多条(条件为true) + /// 审核节点 userTask + /// 脚本节点 scriptTask + /// 子流程节点 subprocess + /// + public string Type { get; set; } + /// + /// 流程单元id + /// + public string Id { get; set; } + /// + /// 节点名称 + /// + public string Name { get; set; } + /// + /// 流出节点ID + /// + public string From { get; set; } + /// + /// 流入节点ID + /// + public string To { get; set; } + #endregion + /// + /// 通知策略 1.短信 2.邮箱 3.微信 4.IM + /// + public string MessageType { get; set; } + /// + /// 条件节点执行条件 + /// + public List Conditions { get; set; } + /// + /// 是否允许加签 + /// + public bool IsAddSign { get; set; } + /// + /// 是否允许单任务 + /// + public bool IsSingleTask { get; set; } + /// + /// 是否允许批量审核 + /// + public bool IsBatchAudit { get; set; } + /// + /// 自动同意规则 1.处理人就是提交人 2.处理人和上一步的处理人相同 3.处理人审批过 + /// + public string AutoAgree { get; set; } + /// + /// 无对应处理人 1.超级管理员处理 2.跳过此步骤 3.不能提交 + /// + public string NoAuditor { get; set; } + /// + /// 驳回策略 1.驳回节点固定 2.能驳回到任何执行过节点 + /// + public string RejectType { get; set; } + /// + /// 审核人 + /// + public List AuditUsers { get; set; } + /// + /// 传阅人 + /// + public List LookUsers { get; set; } + /// + /// 是否会签 + /// + public bool IsCountersign { get; set; } + /// + /// 是否等待所有人审核完 + /// + public bool IsCountersignAll { get; set; } + /// + /// 通过百分比 + /// + public int CountersignAllType { get; set; } + /// + /// 审核方式 1.并行 2.串行 + /// + public string CountersignType { get; set; } + /// + /// 再次审核 1.已同意不需要审核 2.已同意需要审核 + /// + public string CountersignAgian { get; set; } + + /// + /// 是否超时通知 + /// + public bool IsOvertimeMessage { get; set; } + /// + /// 第一次通知 单位(时) + /// + public int OvertimeMessageStart { get; set; } + /// + /// 间隔通知 单位(时) + /// + public int OvertimeMessageInterval { get; set; } + /// + /// 超时流转时间 单位(时) + /// + public int OvertimeGo { get; set; } + + /// + /// 超时通知策略 1.短信 2.邮箱 3.微信 4.IM + /// + public string OvertimeMessageType { get; set; } + /// + /// 脚本执行类型 1SQL 2接口 3IOC + /// + public string ExecuteType { get; set; } + /// + /// 执行SQL数据库编码 + /// + public string SqlDb { get; set; } + /// + /// 执行SQL语句 + /// + public string SqlStr { get; set; } + /// + /// 撤回的时候执行SQL语句 + /// + public string SqlStrRevoke { get; set; } + /// + /// 调用接口 + /// + public string ApiUrl { get; set; } + /// + /// 撤回的时候调用接口 + /// + public string ApiUrlRevoke { get; set; } + /// + /// 注入类名 + /// + public string Ioc { get; set; } + /// + /// 撤回的时候注入类名 + /// + public string IocRevoke { get; set; } + /// + /// 是否异步 + /// + public bool IsAsync { get; set; } + /// + /// 流程模板ID + /// + public string WfschemeId { get; set; } + /// + /// 线条流转条件 + /// + public string LineConditions { get; set; } + + } +} diff --git a/OpenAuth.App/BaseApp/WFProcess/WfMethodParameter.cs b/OpenAuth.App/BaseApp/WFProcess/WfMethodParameter.cs new file mode 100644 index 0000000..b8c92e2 --- /dev/null +++ b/OpenAuth.App/BaseApp/WFProcess/WfMethodParameter.cs @@ -0,0 +1,49 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace OpenAuth.App.WorkFlow +{ + public class WfMethodParameter + { + /// + /// 流程进程Id + /// + public string ProcessId { get; set; } + /// + /// 子流程进程主键 + /// + public string ChildProcessId { get; set; } + /// + /// 当前任务Id + /// + public string TaskId { get; set; } + /// + /// 当前节点名称 + /// + public string UnitName { get; set; } + /// + /// 操作码 + /// + public string Code { get; set; } + /// + /// 当前操作用户 + /// + public string UserId { get; set; } + /// + /// 当前用户账号 + /// + public string UserAccount { get; set; } + /// + /// 当前用户公司 + /// + public string CompanyId { get; set; } + /// + /// 当前用户部门 + /// + public string DepartmentId { get; set; } + + } +} diff --git a/OpenAuth.App/BaseApp/WFSchemeInfo/Response/WFSchemeDto.cs b/OpenAuth.App/BaseApp/WFSchemeInfo/Response/WFSchemeDto.cs new file mode 100644 index 0000000..3332d44 --- /dev/null +++ b/OpenAuth.App/BaseApp/WFSchemeInfo/Response/WFSchemeDto.cs @@ -0,0 +1,25 @@ +using OpenAuth.Repository.Domain; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace OpenAuth.App.Response +{ + public class WFSchemeDto + { + /// + /// 基础信息 + /// + public WFSchemeInfo Schemeinfo { get; set; } + /// + /// 权限信息 + /// + public List SchemeAuthList { get; set; } + /// + /// 模板信息 + /// + public WFScheme Scheme { get; set; } + } +} diff --git a/OpenAuth.App/BaseApp/WFSchemeInfo/WFSchemeApp.cs b/OpenAuth.App/BaseApp/WFSchemeInfo/WFSchemeApp.cs new file mode 100644 index 0000000..53d781b --- /dev/null +++ b/OpenAuth.App/BaseApp/WFSchemeInfo/WFSchemeApp.cs @@ -0,0 +1,57 @@ +using OpenAuth.App.Base; +using OpenAuth.Repository.Domain; +using OpenAuth.Repository; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using SqlSugar; +using OpenAuth.App.Interface; +using Infrastructure; +using OpenAuth.App.BaseApp.Base; +using OpenAuth.App.Request; + +namespace OpenAuth.App +{ + public class WFSchemeApp : SqlSugarBaseApp + { + ISqlSugarClient client; + public WFSchemeApp(ISugarUnitOfWork unitWork, ISimpleClient repository, IAuth auth) : base(unitWork, repository, auth) + { + client = base.Repository.AsSugarClient(); + } + + + + public async Task>> GetSchemePageList(PageReq pageReq, string schemeInfoId) + { + RefAsync totalCount = 0; + + var list = await client.Queryable() + .Where(a => a.SchemeInfoId == schemeInfoId) + .OrderByDescending(a => a.CreateDate) + .ToPageListAsync(pageReq.page, pageReq.limit, totalCount); + + return new PageInfo> + { + Items = list, + Total = totalCount + }; + } + + + /// + /// 获取模板列表 + /// + /// 流程信息主键 + /// + public async Task> GetSchemeList(string schemeInfoId) + { + return await client.Queryable() + .Where(t => t.SchemeInfoId == schemeInfoId) + .OrderByDescending(t => t.CreateDate) + .ToListAsync(); + } + } +} diff --git a/OpenAuth.App/BaseApp/WFSchemeInfo/WFSchemeInfoApp.cs b/OpenAuth.App/BaseApp/WFSchemeInfo/WFSchemeInfoApp.cs new file mode 100644 index 0000000..b872f97 --- /dev/null +++ b/OpenAuth.App/BaseApp/WFSchemeInfo/WFSchemeInfoApp.cs @@ -0,0 +1,292 @@ +using Infrastructure; +using Infrastructure.Helpers; +using Microsoft.Extensions.Configuration; +using OpenAuth.App.BaseApp.Base; +using OpenAuth.App.Interface; +using OpenAuth.App.Request; +using OpenAuth.Repository; +using OpenAuth.Repository.Domain; +using SqlSugar; + +namespace OpenAuth.App +{ + public class WFSchemeInfoApp : SqlSugarBaseApp + { + ISqlSugarClient client; + private IConfiguration configuration; + + public WFSchemeInfoApp( + ISugarUnitOfWork unitWork, + ISimpleClient repository, + IAuth auth, IConfiguration configuration) : base(unitWork, repository, auth) + { + client = base.Repository.AsSugarClient(); + this.configuration = configuration; + } + + public async Task>> LoadPage(PageReq pageReq, string category, string ids) + { + RefAsync totalCount = 0; + + var idList = new string[0]; + if (!string.IsNullOrEmpty(ids)) + { + idList = ids.Split(','); + } + + var list = await client.Queryable() + .LeftJoin((info, s) => info.SchemeId == s.Id) + .WhereIF(!string.IsNullOrEmpty(pageReq.key), + info => info.Name.Contains(pageReq.key) || info.Code.Contains(pageReq.key)) + .WhereIF(!string.IsNullOrEmpty(category), info => info.Category == category) + .WhereIF(!string.IsNullOrEmpty(ids), info => idList.Contains(info.Id)) + .OrderByDescending((info, s) => s.CreateDate) + .Select((info, s) => new WFSchemeInfo() + { + AuthType = info.AuthType, + Id = info.Id, + Category = info.Category, + Code = info.Code, + Color = info.Color, + Description = info.Description, + EnabledMark = info.EnabledMark, + Icon = info.Icon, + IsInApp = info.IsInApp, + Mark = info.Mark, + Name = info.Name, + SchemeId = info.SchemeId, + + Type = s.Type, + CreateDate = s.CreateDate, + CreateUserId = s.CreateUserId, + CreateUserName = s.CreateUserName + }).ToPageListAsync(pageReq.page, pageReq.limit, totalCount); + + return new PageInfo> + { + Items = list, + Total = totalCount + }; + } + + public async Task> Load() + { + return await base.Repository.GetListAsync(a => a.EnabledMark == 1); + } + + public async Task> GetInfoList() + { + var currentUser = _auth.GetCurrentUser(); + + var userId = currentUser.User.Id.ToString(); + var postIds = currentUser.Positions.Select(a => a.Id.ToString()).ToList(); + var roleIds = currentUser.Roles.Select(a => a.Id.ToString()).ToList(); + + List ids = new List(); + if (!string.IsNullOrEmpty(userId)) + { + ids.Add(userId); + } + + ids.AddRange(postIds); + ids.AddRange(roleIds); + var list = await client.Queryable().Where(t => ids.ToArray().Contains(t.ObjId)).ToListAsync(); + List schemeinfoIds = new List(); + foreach (var item in list) + { + schemeinfoIds.Add(item.SchemeInfoId); + } + + return await client.Queryable() + .LeftJoin((t, s) => t.SchemeId == s.Id) + .Where((t, s) => + t.EnabledMark == 1 && + t.Mark == 1 && + (schemeinfoIds.ToArray().Contains(t.Id) || t.AuthType == 1) && + s.Type == 1).ToListAsync(); + } + + + public async Task GetInfoEntityByCode(string code) + { + return await base.Repository.GetFirstAsync(t => t.Code == code); + } + + + public async Task GetSchemeEntity(string keyValue) + { + return await client.Queryable().FirstAsync(a => a.Id == keyValue); + } + + public async Task> GetAuthList(string schemeInfoId) + { + return await client.Queryable().Where(t => t.SchemeInfoId == schemeInfoId).ToListAsync(); + } + + + /// + /// 获取模板的实体 + /// + /// 主键 + /// + public async Task GetScheme(string keyValue) + { + return await client.Queryable().FirstAsync(a => a.Id == keyValue); + } + + + public async Task> UpdateScheme(string schemeInfoId, string schemeId) + { + WFScheme nWFSchemeEntity = await GetScheme(schemeId); + + var flag = false; + if (nWFSchemeEntity.Type != 1) + { + flag = await base.Repository + .UpdateSetColumnsTrueAsync(a => new WFSchemeInfo() { SchemeId = schemeId, EnabledMark = 0 }, + a => a.Id == schemeInfoId); + } + else + { + flag = await base.Repository + .UpdateSetColumnsTrueAsync(a => new WFSchemeInfo() { SchemeId = schemeId }, + a => a.Id == schemeInfoId); + } + + return new Response + { + Result = flag, + Message = flag == true ? "success" : "error" + }; + } + + /// + /// + /// + /// 主键吧? + /// + /// + /// + /// + public async Task> SaveEntity(string keyValue, WFSchemeInfo infoEntity, WFScheme schemeEntity, + IEnumerable authList) + { + // 如果存在schemeEntity content 解码 + if (!string.IsNullOrEmpty(schemeEntity.Content)) + { + var code = configuration.GetSection("AppSetting:ASECode").Value; + schemeEntity.Content = + AESHelper.AesDecrypt(schemeEntity.Content, code); //"12345678901234567890123456789012" + } + + if (!string.IsNullOrEmpty(keyValue)) + { + WFScheme oldNWFSchemeEntity = await GetScheme(infoEntity.SchemeId); + if (oldNWFSchemeEntity.Content == schemeEntity.Content && oldNWFSchemeEntity.Type == schemeEntity.Type) + { + schemeEntity = null; + } + } + + using (var uwo = UnitWork.CreateContext()) + { + if (string.IsNullOrEmpty(keyValue)) + { + infoEntity.Id = Guid.NewGuid().ToString(); + infoEntity.EnabledMark = 1; + } + else + { + infoEntity.Id = keyValue; + } + + #region 模板信息 + + if (schemeEntity != null) + { + schemeEntity.SchemeInfoId = infoEntity.Id; + schemeEntity.Id = Guid.NewGuid().ToString(); + schemeEntity.CreateDate = DateTime.Now; + schemeEntity.CreateUserId = _auth.GetUserId(); + schemeEntity.CreateUserName = _auth.GetUserName(); + await uwo.WFScheme.InsertAsync(schemeEntity); + infoEntity.SchemeId = schemeEntity.Id; + } + + #endregion + + #region 模板基础信息 + + if (!string.IsNullOrEmpty(keyValue)) + { + await uwo.WFSchemeInfo.UpdateAsync(infoEntity); + } + else + { + await uwo.WFSchemeInfo.InsertAsync(infoEntity); + } + + #endregion + + #region 流程模板权限信息 + + string schemeInfoId = infoEntity.Id; + await uwo.WFSchemeAuth.DeleteAsync(t => t.SchemeInfoId.Equals(schemeInfoId)); + foreach (var item in authList) + { + item.Id = Guid.NewGuid().ToString(); + item.SchemeInfoId = schemeInfoId; + await uwo.WFSchemeAuth.InsertAsync(item); + } + + #endregion + + var flag = uwo.Commit(); + + return new Response + { + Result = flag, + Message = flag == true ? "success" : "error" + }; + } + } + + /// + /// 更新自定义表单模板状态 + /// + /// 模板信息主键 + /// 状态1启用0禁用 + public async Task> UpdateState(string schemeInfoId, int state) + { + var flag = await base.Repository.UpdateSetColumnsTrueAsync(a => new WFSchemeInfo() { EnabledMark = state }, + a => a.Id == schemeInfoId); + return new Response + { + Result = flag, + Message = flag == true ? "success" : "error" + }; + } + + public async Task> DeleteEntity(string keyValue) + { + using (var uow = base.UnitWork.CreateContext()) + { + await uow.WFSchemeInfo.DeleteAsync(t => t.Id.Equals(keyValue)); + await uow.WFSchemeAuth.DeleteAsync(t => t.SchemeInfoId.Equals(keyValue)); + await uow.WFScheme.DeleteAsync(t => t.SchemeInfoId.Equals(keyValue)); + + var falg = uow.Commit(); + return new Response + { + Result = falg, + Message = (falg == true ? "success" : "error") + }; + } + } + + public WFSchemeInfo SelectBySchemeCode(string schemeCode) + { + return Repository.GetFirst(a => a.Code == schemeCode); + } + } +} \ No newline at end of file diff --git a/OpenAuth.App/BaseApp/WFStamp/WFStampApp.cs b/OpenAuth.App/BaseApp/WFStamp/WFStampApp.cs new file mode 100644 index 0000000..f13ef01 --- /dev/null +++ b/OpenAuth.App/BaseApp/WFStamp/WFStampApp.cs @@ -0,0 +1,170 @@ +using Infrastructure; +using Microsoft.VisualStudio.TestPlatform.CommunicationUtilities; +using OpenAuth.App.Base; +using OpenAuth.App.Interface; +using OpenAuth.App.Request; +using OpenAuth.App.Response; +using OpenAuth.Repository; +using OpenAuth.Repository.Domain; +using SqlSugar; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using OpenAuth.App.BaseApp.Base; + +namespace OpenAuth.App +{ + public class WFStampApp : SqlSugarBaseApp + { + public WFStampApp(ISugarUnitOfWork unitWork, ISimpleClient repository, IAuth auth) : base(unitWork, repository, auth) + { + } + + #region 获取数据 + /// + /// 获取签章列表 + /// + /// 用户id + /// + public async Task> GetList(string userId) + { + return await base.Repository.GetListAsync(t => t.UserIds.Contains(userId) && t.EnabledMark == 1); + } + /// + /// 获取管理分页列表 + /// + /// 分页参数 + /// 查询参数 + /// + public async Task>> GetPageList(PageReq pageReq, WFStamp queryParams) + { + RefAsync totalCount = 0; + + var expression = Expressionable.Create() + .AndIF(!string.IsNullOrEmpty(queryParams.StampName), t => t.StampName.Contains(queryParams.StampName)) + .AndIF(queryParams.EnabledMark != null, (t => t.EnabledMark == queryParams.EnabledMark)) + .AndIF(!string.IsNullOrEmpty(queryParams.StampType), t => t.StampType == queryParams.StampType); + + var list = await base.Repository + .GetPageListAsync(expression.ToExpression(), + new PageModel + { + PageIndex = pageReq.page, + PageSize = pageReq.limit, + TotalCount = totalCount + }); + + return new PageInfo> + { + Items = list, + Total = totalCount + }; + } + #endregion + + #region 提交数据 + + /// + /// 保存印章信息(新增/编辑) + /// + /// 主键 + /// 实体 + public async Task> SaveEntity(string keyValue, WFStamp entity) + { + var flag = false; + entity.User = _auth.GetUserId(); + entity.CreateDate = DateTime.Now; + if (string.IsNullOrEmpty(keyValue)) + { + entity.StampId = Guid.NewGuid().ToString(); //产生印章编号 + flag = await base.Repository.InsertAsync(entity); + } + else + { + entity.StampId = keyValue; + flag = await base.Repository.UpdateAsync(entity); + } + + return new Response + { + Result = flag, + Message = flag == true ? "success" : "error" + }; + } + + /// + /// 删除印章信息 + /// + /// 主键 + public async Task> DeleteEntity(string keyVlaue) + { + var flag = await base.Repository.DeleteByIdAsync(keyVlaue);//删除操作 + return new Response + { + Result = flag, + Message = flag == true ? "success" : "error" + }; + } + #endregion + + #region 扩展方法 + /// + /// 更新数据状态 + /// + /// 主键 + /// 状态 1启用 0禁用 + public async Task> UpdateState(string keyValue, int state) + { + var flag = await base.Repository + .UpdateSetColumnsTrueAsync( + a => new WFStamp { EnabledMark = state }, + a => a.StampId == keyValue + );//删除操作 + return new Response + { + Result = flag, + Message = flag == true ? "success" : "error" + }; + } + + public async Task GetEntity(string keyValue) + { + return await base.Repository.GetByIdAsync(keyValue); + } + + /// + /// 密码匹配 + /// + /// 主键 + /// 密码 + /// + public async Task> EqualPassword(string keyValue, string password) + { + var reponse = new Response(); + + WFStamp entity = await GetEntity(keyValue); + if (entity.Password.Equals(password))//加密后进行对比 + { + reponse.Result = true; + reponse.Message = "success"; + } + else + { + reponse.Result = false; + reponse.Message = "error"; + } + return reponse; + } + + /// + /// 签章图片归档并验证密码 + /// + /// 主键 + /// 密码 + /// + public Task ToWfImg(string keyValue, string password) { return null; } + #endregion + } +} diff --git a/OpenAuth.App/BaseApp/WFTask/Request/WFTaskDto.cs b/OpenAuth.App/BaseApp/WFTask/Request/WFTaskDto.cs new file mode 100644 index 0000000..8ab60b5 --- /dev/null +++ b/OpenAuth.App/BaseApp/WFTask/Request/WFTaskDto.cs @@ -0,0 +1,33 @@ +using OpenAuth.Repository.Domain; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace OpenAuth.App.Request +{ + public class WFTaskDto + { + /// + /// 当前任务 + /// + public WFTask Task { get; set; } + /// + /// 流程进程 + /// + public WFProcess Process { get; set; } + /// + /// 流程模板信息 + /// + public WFScheme Scheme { get; set; } + /// + /// 审核日志 + /// + public IEnumerable Logs { get; set; } + /// + /// 任务列表 + /// + public IEnumerable Tasks { get; set; } + } +} diff --git a/OpenAuth.App/BaseApp/WFTask/Response/ExtendInfo.cs b/OpenAuth.App/BaseApp/WFTask/Response/ExtendInfo.cs new file mode 100644 index 0000000..2d3a83c --- /dev/null +++ b/OpenAuth.App/BaseApp/WFTask/Response/ExtendInfo.cs @@ -0,0 +1,9 @@ +namespace OpenAuth.App.BaseApp.WFTask.Response; + +public class ExtendInfo +{ + public string Field { get; set; } + public object Value { get; set; } + public string Label { get; set; } + public string FieldName { get; set; } +} \ No newline at end of file diff --git a/OpenAuth.App/BaseApp/WFTask/Response/RetractRes.cs b/OpenAuth.App/BaseApp/WFTask/Response/RetractRes.cs new file mode 100644 index 0000000..f084c20 --- /dev/null +++ b/OpenAuth.App/BaseApp/WFTask/Response/RetractRes.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace OpenAuth.App.BaseApp.WFTask.Response +{ + public class RetractRes + { + public string ProcessId { get; set; } + public DateTime MaxCreatedDate { get; set; } + } +} diff --git a/OpenAuth.App/BaseApp/WFTask/WFTaskApp.cs b/OpenAuth.App/BaseApp/WFTask/WFTaskApp.cs new file mode 100644 index 0000000..1323844 --- /dev/null +++ b/OpenAuth.App/BaseApp/WFTask/WFTaskApp.cs @@ -0,0 +1,935 @@ +using System.Data; +using DocumentFormat.OpenXml.Office2010.Excel; +using Infrastructure; +using Infrastructure.Extensions; +using JetBrains.Annotations; +using Microsoft.Extensions.Configuration; +using Moq; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using NPOI.SS.Formula.Functions; +using OpenAuth.App.BaseApp.Base; +using OpenAuth.App.BaseApp.WFTask.Response; +using OpenAuth.App.FormScheme; +using OpenAuth.App.FormScheme.Response; +using OpenAuth.App.Interface; +using OpenAuth.App.Request; +using OpenAuth.Repository; +using OpenAuth.Repository.Domain; +using SqlSugar; + +namespace OpenAuth.App.BaseApp.WFTask +{ + public class WFTaskApp : SqlSugarBaseApp + { + ISqlSugarClient client; + private readonly FormSchemeApp _formSchemeApp; + private readonly WFProcessApp _processApp; + private WFSchemeInfoApp _schemeInfoApp; + private readonly IConfiguration _configuration; + + public WFTaskApp(ISugarUnitOfWork unitWork, ISimpleClient repository, + IAuth auth, + FormSchemeApp formSchemeApp, + WFProcessApp processApp, + WFSchemeInfoApp schemeInfoAppApp, + IConfiguration configuration + ) : + base(unitWork, repository, auth) + { + client = base.Repository.AsSugarClient(); + _formSchemeApp = formSchemeApp; + _processApp = processApp; + _schemeInfoApp = schemeInfoAppApp; + _configuration = configuration; + } + + #region 任务 + + /// + /// 获取我的代办任务列表 + /// + /// 分页参数 + /// 查询参数 + /// 是否允许批量审核 + /// + public async Task>> GetMyUncompletedPageList(PageReq pageReq, + WFProcessSearchDto searchParams, string category, bool isBatchAudit = false) + { + RefAsync totalCount = 0; + var userId = _auth.GetUserId(); + + var expression = Expressionable.Create() + //.And(t => t.UserId == userId) + .And(t => t.State == 1) + .And(t => t.Type != 2) + .AndIF(!string.IsNullOrEmpty(searchParams.Keyword), + t => t.ProcessTitle.Contains(searchParams.Keyword) || t.UnitName.Contains(searchParams.Keyword)) + .AndIF(!string.IsNullOrEmpty(searchParams.Code), t => t.ProcessCode == searchParams.Code) + .AndIF(searchParams.StartDate != null && searchParams.EndDate != null, + t => t.CreateDate >= searchParams.StartDate && t.CreateDate <= searchParams.EndDate) + .AndIF(isBatchAudit, t => t.IsBatchAudit == 1); + + var list = await client.Queryable() + .Where(expression.ToExpression()) + .LeftJoin((t, u) => t.Id == u.TaskId && u.UserId == userId) + .Where((t, u) => u.UserId == userId || t.UserId == userId) + .LeftJoin((t, u, p) => t.ProcessId == p.Id) + .LeftJoin((t, u, p, s) => p.SchemeId == s.Id) + .LeftJoin((t, u, p, s, i) => s.SchemeInfoId == i.Id) + .WhereIF(!string.IsNullOrEmpty(category), (t, u, p, s, i) => i.Category == category) + .OrderByDescending((t, u, p, s, i) => t.CreateDate) + .Select((t, u, p, s, i) => new Repository.Domain.WFTask() + { + Id = t.Id.SelectAll(), + SchemeContent = s.Content, + InstanceInfo = p.InstanceInfo + }) + .ToPageListAsync(pageReq.page, pageReq.limit, totalCount); + // 遍历补充信息 + foreach (var wfTask in list) + { + // 获取表单数据 + var unitId = wfTask.UnitId; // 当前节点id + //var process = await _processApp.GetEntity(wfTask.ProcessId); + //var scheme = await _schemeInfoApp.GetSchemeEntity(process.SchemeId); + //var content = (JObject)JsonConvert.DeserializeObject(scheme.Content); + var content = (JObject)JsonConvert.DeserializeObject(wfTask.SchemeContent); + + var wfData = (JArray)content["wfData"]; + // + var instanceInfo = wfTask.InstanceInfo; + var processId = instanceInfo.pkeyValue; + var pkey = instanceInfo.pkey; + foreach (var jToken in wfData) + { + var id = jToken["id"]?.ToString(); + if (id != null && id.Equals(unitId)) + { + var first = jToken; + // fromVersion 为 schemeId + var formVersion = first["formVerison"]?.ToString(); + if (!string.IsNullOrEmpty(formVersion)) + { + var authFields = (JArray)jToken["authFields"]; + var showFields = new Dictionary(); + foreach (var authField in authFields) + { + var label = authField["label"].ToString(); + var field = authField["field"].ToString(); + var fieldName = authField["fieldName"]?.ToString(); + if (authField["upShow"].ToBool().Equals(true)) + { + showFields.Add(field, + new ExtendInfo() { Field = field, Label = label, FieldName = fieldName }); + } + } + + var sql = "select "; + var selectFields = ""; + foreach (var showField in showFields) + { + selectFields += $"\"{showField.Value.FieldName}\"" + " as " + showField.Value.Field + + ","; + } + + selectFields = selectFields.TrimEnd(','); + + var formScheme = await _formSchemeApp.GetScheme(formVersion); + FormSchemeNewModel formSchemeModel = formScheme.Scheme.ToObject(); + FormDbTableInfo mainTable = formSchemeModel.Db.Find(t => t.Type == "main"); + sql += selectFields + $" FROM \"{mainTable.Name}\" t where \"{pkey}\" = @Id"; + + var dataList = await client.Ado.GetDataTableAsync(sql, new { Id = processId }); + + // 实现一个新的查询方法 + // var dataList = await _formSchemeApp.GetFormDataNew(formVersion, "Id", processId); + // var data = dataList.FirstOrDefault().Value; + // 实际只有一行数据 + foreach (DataRow dataRow in dataList.Rows) + { + foreach (var showField in showFields) + { + var field = showField.Key; + var value = dataRow[field]; + showField.Value.Value = value; + } + } + + wfTask.ExtendInfo = showFields.Values; + } + } + } + + wfTask.SchemeContent = null; + wfTask.InstanceInfo = null; + } + + return new PageInfo> + { + Items = list, + Total = totalCount + }; + } + + + /// + /// 获取我的已办任务列表 + /// + /// 分页参数 + /// 查询参数 + /// + public async Task>> GetMyCompletedPageList(PageReq pageReq, + WFProcessSearchDto searchParams, string category) + { + RefAsync totalCount = 0; + var userId = _auth.GetUserId(); + + var queryable = client.Queryable() + .LeftJoin((t, t1) => t.Id == t1.TaskId) + .LeftJoin((t, t1, t2) => t.ProcessId == t2.Id) + .LeftJoin((t, t1, t2, t3) => t2.SchemeId == t3.Id) + .LeftJoin((t, t1, t2, t3, t4) => t3.SchemeInfoId == t4.Id); + var exp = Expressionable.Create() + .And((t, t1) => t1.IsLast == 1) + .And((t, t1) => t.State == 3 || t.State == 5) + .And((t, t1) => t.Type == 1 || t.Type == 3 || t.Type == 5 || t.Type == 6||t.Type==7) + .And((t, t1) => t.UserId == userId || t1.UserId == userId) + .AndIF(!string.IsNullOrEmpty(searchParams.Keyword), + (t, t1) => t.ProcessTitle.Contains(searchParams.Keyword) || + t.UnitName.Contains(searchParams.Keyword)) + .AndIF(!string.IsNullOrEmpty(searchParams.Code), (t, t1) => t.ProcessCode == searchParams.Code) + .AndIF(searchParams.StartDate != null && searchParams.EndDate != null, + (t, t1) => t.CreateDate >= searchParams.StartDate && t.CreateDate <= searchParams.EndDate) + .ToExpression(); + var list = await queryable.Where(exp) + .WhereIF(!string.IsNullOrEmpty(category), (t, t1, t2, t3, t4) => t4.Category == category).Select( + (t, t1, t2, t3, t4) => new Repository.Domain.WFTask() + { + Id = t.Id, + Type = t.Type, + ProcessTitle = t.ProcessTitle, + ProcessCode = t.ProcessCode, + ProcessId = t.ProcessId, + ChildProcessId = t.ChildProcessId, + ProcessUserId = t.ProcessUserId, + ProcessUserName = t.ProcessUserName, + Token = t.Token, + UnitId = t.UnitId, + UnitName = t.UnitName, + PrevUnitId = t.PrevUnitId, + PrevUnitName = t.PrevUnitName, + PrevToken = t.PrevToken, + UserId = t.UserId, + UserName = t.UserName, + UserDepartmentId = t.UserDepartmentId, + UserCompanyId = t.UserCompanyId, + State = t.State, + IsAgree = t.IsAgree, + IsLast = t.IsLast, + Sort = t.Sort, + TimeoutAction = t.TimeoutAction, + TimeoutNotice = t.TimeoutNotice, + TimeoutInterval = t.TimeoutInterval, + TimeoutStrategy = t.TimeoutStrategy, + CreateUserId = t.CreateUserId, + CreateUserName = t.CreateUserName, + IsUrge = t.IsUrge, + UrgeTime = t.UrgeTime, + FirstId = t.FirstId, + IsBatchAudit = t.IsBatchAudit, + IsReject = t.IsReject, + PrevTaskId = t.PrevTaskId, + UpdateTaskId = t.UpdateTaskId, + + IsCancel = t1.IsCancel, + CreateDate = t1.CreateDate, + OperationName = t1.OperationName, + MakeUserId = t1.UserId, + + IsFinished = t2.IsFinished + }).ToPageListAsync(pageReq.page, pageReq.limit, totalCount); + + return new PageInfo> + { + Items = list, + Total = totalCount + }; + } + + + /// + /// 获取可撤回的任务 + /// + /// 分页参数 + /// 查询参数 + /// + public PageInfo> GetRetractPageList(PageReq pageReq, + WFProcessSearchDto searchParams, string category) + { + int totalCount = 0; + var userId = _auth.GetUserId(); + + var query = client.Queryable() + .Where(t => t.IsLast == 1) + .Where(t => t.State == 3 || t.State == 5) + .Where(t => t.Type == 1 || t.Type == 3 || t.Type == 5 || t.Type == 6 || t.Type == 7) + .Where(t => t.CreateDate != null) + .WhereIF(!string.IsNullOrEmpty(searchParams.Keyword), t => t.ProcessTitle.Contains(searchParams.Keyword) || t.UnitName.Contains(searchParams.Keyword)) + .WhereIF(!string.IsNullOrEmpty(searchParams.Code), t => t.ProcessCode == searchParams.Code) + .WhereIF(searchParams.StartDate != null && searchParams.EndDate != null, t => t.CreateDate >= searchParams.StartDate && t.CreateDate <= searchParams.EndDate) + .Select(t => new + { + index2 = SqlFunc.RowNumber($"{t.CreateDate} desc ", t.ProcessId), + ProcessTitle = t.ProcessTitle, + UnitId = t.UnitId, + Id = t.Id, + ProcessId = t.ProcessId, + UnitName = t.UnitName, + UserId = t.UserId, + UserName = t.UserName, + State = t.State, + CreateDate = t.CreateDate, + Type = t.Type, + ProcessCode = t.ProcessCode + }) + .MergeTable() + .Where(it => it.index2 == 1) + .Where(r => r.UserId == userId) + .OrderByDescending(r => r.CreateDate) + .Select(r => new + { + r.Id, + r.ProcessTitle, + r.ProcessId, + r.UnitId, + r.UnitName, + r.UserId, + r.UserName, + r.State, + r.CreateDate, + r.Type, + r.ProcessCode, + }); + var result = query.ToPageList(pageReq.page, pageReq.limit); + totalCount = query.Count(); + + return new PageInfo> + { + Items = result, + Total = totalCount + }; + } + + /// + /// 获取我的传阅任务列表 + /// + /// 分页参数 + /// 查询参数 + /// + public async Task>> GetMyReadPageList(PageReq pageReq, + WFProcessSearchDto searchParams, + string category) + { + RefAsync totalCount = 0; + var userId = _auth.GetUserId(); + + var expression = Expressionable.Create() + .And(t => t.UserId == userId) + .And(t => t.Type == 2) + .AndIF(!string.IsNullOrEmpty(searchParams.Keyword), + t => t.ProcessTitle.Contains(searchParams.Keyword) || t.UnitName.Contains(searchParams.Keyword)) + .AndIF(!string.IsNullOrEmpty(searchParams.Code), t => t.ProcessCode == searchParams.Code) + .AndIF(searchParams.StartDate != null && searchParams.EndDate != null, + t => t.CreateDate >= searchParams.StartDate && t.CreateDate <= searchParams.EndDate); + + + var list = await client.Queryable() + .Where(expression.ToExpression()) + .LeftJoin((t, p) => t.ProcessId == p.Id) + .LeftJoin((t, p, s) => p.SchemeId == s.Id) + .LeftJoin((t, p, s, i) => s.SchemeInfoId == i.Id) + .WhereIF(!string.IsNullOrEmpty(category), (t, p, s, i) => i.Category == category) + .OrderByDescending((t, p, s, i) => t.CreateDate) + .ToPageListAsync(pageReq.page, pageReq.limit, totalCount); + + return new PageInfo> + { + Items = list, + Total = totalCount + }; + } + + /// + /// 获取我的委托任务列表 + /// + /// 分页参数 + /// 查询参数 + /// + public async Task>> GetMyDelegatePageList(PageReq pageReq, + WFProcessSearchDto searchParams, string category) + { + RefAsync totalCount = 0; + + var pageInfo = new PageInfo>(); + + string userId = _auth.GetUserId(); + DateTime datetime = DateTime.Now; + + var expression = Expressionable.Create(); + var delegateList = await client.Queryable() + .Where(t => t.ToUserId == userId && t.BeginDate >= datetime && t.EndDate <= datetime && t.Type == 0) + .ToListAsync(); + + if (delegateList.Count == 0) + { + pageInfo.Total = 0; + return pageInfo; + } + + + foreach (var item in delegateList) + { + var relationList = await client.Queryable() + .Where(a => a.DelegateRuleId == item.Id).ToListAsync(); + string relation = ""; + foreach (var relationItem in relationList) + { + relation += $"'{relationItem.SchemeInfoId}'"; + } + + expression = expression.Or(t => + t.UserId == item.CreateUserId && relation.Contains("'" + t.ProcessCode + "'")); + } + + var exp = Expressionable.Create() + .And(t => t.UserId != userId) + .And(t => t.State == 1) + .And(t => t.Type != 2) + .AndIF(!string.IsNullOrEmpty(searchParams.Keyword), + t => t.ProcessTitle.Contains(searchParams.Keyword) || t.UnitName.Contains(searchParams.Keyword)) + .AndIF(!string.IsNullOrEmpty(searchParams.Code), t => t.ProcessCode == searchParams.Code) + .AndIF(searchParams.StartDate != null && searchParams.EndDate != null, + t => t.CreateDate >= searchParams.StartDate && t.CreateDate <= searchParams.EndDate); + + exp = exp.And(expression.ToExpression()); + + var list = await client.Queryable() + .Where(expression.ToExpression()) + .LeftJoin((t, p) => t.ProcessId == p.Id) + .LeftJoin((t, p, s) => p.SchemeId == s.Id) + .LeftJoin((t, p, s, i) => s.SchemeInfoId == i.Id) + .WhereIF(!string.IsNullOrEmpty(category), (t, p, s, i) => i.Category == category) + .OrderByDescending((t, p, s, i) => t.CreateDate) + .ToPageListAsync(pageReq.page, pageReq.limit, totalCount); + + return new PageInfo> + { + Items = list, + Total = totalCount + }; + } + + + /// + /// 获取流程任务实体 + /// + /// 密钥 + /// 用户id + /// + public async Task GetEntityByToken(string token, string userId) + { + return await base.Repository.GetFirstAsync(t => t.Token == token && t.UserId == userId && t.State == 1); + } + + /// + /// 获取流程任务实体 + /// + /// 主键 + /// + public Repository.Domain.WFTask GetEntity(string keyValue) + { + return base.Repository.GetFirst(a => a.Id == keyValue); + } + + /// + /// 获取任务关联的用户 + /// + /// 主键 + /// + public List GetUsers(string keyValue) + { + List users = new List(); + var us = client.Queryable().Where(r => r.TaskId == keyValue).ToList(); + if (us.Count > 0) + { + users = us.Select(r => r.UserId).ToList(); + } + + return users; + } + + /// + /// 获取任务列表 + /// + /// 流程实例主键 + /// 任务类型 + /// 上一个任务Id值 + /// + public async Task> GetList(string processId, int type, string prevTaskId) + { + return await base.Repository.GetListAsync(t => + t.ProcessId == processId && t.Type == type && t.PrevTaskId == prevTaskId); + } + + /// + /// 获取任务列表 + /// + /// 流程实例主键 + /// 上一个任务token值 + /// + public async Task> GetList(string processId, string prevToken) + { + return await base.Repository.GetListAsync(t => t.ProcessId == processId && t.PrevToken == prevToken); + } + + /// + /// 获取未完成的任务 + /// + /// 流程进程主键 + /// + public async Task> GetUnFinishTaskList(string processId) + { + return await base.Repository.GetListAsync(t => + t.ProcessId == processId && t.State == 1 && (t.Type == 1 || t.Type == 2 || t.Type == 3 || t.Type == 4 || + t.Type == 5 || t.Type == 6||t.Type==7)); + } + + /// + /// 获取等待任务列表(用于网关判断是否所有支路都执行完毕) + /// + /// 流程实例Id + /// 流程单元节点Id + /// + public async Task> GetAwaitTaskList(string processId, string unitId) + { + return await base.Repository.GetListAsync(t => + t.ProcessId == processId && t.UnitId == unitId && t.Type == 21 && t.State == 1); + } + + /// + /// 获取最近的任务 + /// + /// 流程实例Id + /// 流程单元节点Id + /// + public async Task> GetLastTaskList(string processId, string unitId) + { + return await client.Queryable() + .Where(t => t.ProcessId == processId && t.UnitId == unitId && t.IsLast == 1 && + (t.Type == 1 || t.Type == 3 || t.Type == 4 || t.Type == 5||t.Type==7)) + .OrderBy(t => t.Sort) + .ToListAsync(); + } + + /// + /// 获取最近的不是驳回产生的任务 + /// + /// 流程实例Id + /// 流程单元节点Id + /// + public Task GetLastNotRejectTask(string processId, string unitId) + { + return null; + } + + /// + /// 获取最近的完成任务 + /// + /// 流程实例Id + /// 流程单元节点Id + /// + public async Task> GetLastFinishTaskList(string processId, string unitId) + { + return await base.Repository.GetListAsync(t => + t.ProcessId == processId && t.UnitId == unitId && (t.Type == 1||t.Type==7) && t.IsLast == 1 && t.State == 3); + } + + /// + /// 获取完成任务 + /// + /// 流程实例Id + /// + public async Task> GetFinishTaskList(string processId, List ids) + { + return await base.Repository.GetListAsync(t => t.ProcessId == processId && !ids.Contains(t.Id)); + } + + /// + /// 获取节点任务的最大人数 + /// + /// 流程实例Id + /// 流程单元节点Id + /// + public Task GetTaskUserMaxNum(string processId, string unitId) + { + return null; + } + + /// + /// 保存任务 + /// + /// 任务日志 + public async Task> Add(Repository.Domain.WFTask wfTaskEntity) + { + wfTaskEntity.CreateDate = DateTime.Now; + wfTaskEntity.CreateUserId = _auth.GetUserId(); + wfTaskEntity.CreateUserName = _auth.GetUserName(); + wfTaskEntity.Id = Guid.NewGuid().ToString(); + wfTaskEntity.IsLast = 1; + + var flag = await base.Repository.InsertAsync(wfTaskEntity); + + return new Response + { + Result = flag, + Message = flag == true ? "success" : "error" + }; + } + + /// + /// 更新任务状态 + /// + /// + /// + /// + public async Task> Update(string processId, string unitId) + { + var flag = await base.Repository.UpdateSetColumnsTrueAsync(a => new Repository.Domain.WFTask { IsLast = 0 }, + a => a.ProcessId == processId && a.UnitId == unitId); + return new Response + { + Result = flag, + Message = flag == true ? "success" : "error" + }; + } + + /// + /// 更新任务 + /// + /// 任务日志 + public Response Update(Repository.Domain.WFTask wfTaskEntity) + { + var flag = base.Repository.Update(wfTaskEntity); + + return new Response + { + Result = flag, + Message = flag == true ? "success" : "error" + }; + } + + /// + /// 关闭任务 + /// + /// 流程主键 + /// 任务令牌 + /// 任务id(除去该任务不关闭) + /// + public async Task> CloseTask(string processId, string token, string taskId = "") + { + var expression = Expressionable.Create() + .And(t => t.ProcessId == processId && t.Token == token) + .AndIF(!string.IsNullOrEmpty(taskId), t => t.Id != taskId).ToExpression(); + + var flag = await base.Repository.UpdateSetColumnsTrueAsync(t => new Repository.Domain.WFTask { State = 4 }, + expression); + return new Response + { + Result = flag, + Message = flag == true ? "success" : "error" + }; + } + + /// + /// 关闭任务 + /// + /// 流程进程 + /// 单元节点id + /// 任务类型 + /// 任务id + /// + public async Task> CloseTask(string processId, string unitId, int type, string taskId) + { + var flag = await base.Repository.UpdateSetColumnsTrueAsync( + t => new Repository.Domain.WFTask { State = 4, UpdateTaskId = taskId }, + t => t.ProcessId == processId && t.UnitId == unitId && t.Type == type); + return new Response + { + Result = flag, + Message = flag == true ? "success" : "error" + }; + } + + /// + /// 打开任务 + /// + /// 流程进程 + /// 单元节点id + /// 任务类型 + /// 任务id + /// + public async Task> OpenTask(string processId, string unitId, int type, string taskId) + { + var flag = await base.Repository.UpdateSetColumnsTrueAsync( + t => new Repository.Domain.WFTask { State = 1, UpdateTaskId = taskId }, + t => t.ProcessId == processId && t.UnitId == unitId && t.Type == type); + return new Response + { + Result = flag, + Message = flag == true ? "success" : "error" + }; + } + + /// + /// 删除任务 + /// + /// 流程进程 + /// + public async Task> Delete(string processId) + { + var flag = await base.Repository.DeleteAsync(t => t.ProcessId == processId); + return new Response + { + Result = flag, + Message = flag == true ? "success" : "error" + }; + } + + /// + /// 删除任务 + /// + /// 流程进程 + /// 来源任务ID + /// + public async Task> Delete(string processId, string prevTaskId) + { + var flag = await base.Repository.DeleteAsync(t => t.ProcessId == processId && t.PrevTaskId == prevTaskId); + return new Response + { + Result = flag, + Message = flag == true ? "success" : "error" + }; + } + + /// + /// 删除任务 + /// + /// 任务主键 + /// + public async Task> DeleteByFirstId(string id) + { + var flag = await base.Repository.DeleteAsync(t => t.FirstId == id); + return new Response + { + Result = flag, + Message = flag == true ? "success" : "error" + }; + } + + /// + /// 作废任务 + /// + /// 流程进程 + /// + public async Task> VirtualDelete(string processId) + { + var flag = await base.Repository.UpdateSetColumnsTrueAsync(t => new Repository.Domain.WFTask { State = 7 }, + t => t.ProcessId == processId && t.State == 1); + return new Response + { + Result = flag, + Message = flag == true ? "success" : "error" + }; + } + + #endregion + + #region 日志 + + /// + /// 获取流程进程的任务处理日志 + /// + /// 流程进程主键 + /// + public async Task> GetLogList(string processId) + { + return await client.Queryable().Where(a => a.ProcessId == processId).OrderBy(r => r.CreateDate) + .ToListAsync(); + } + + /// + /// 获取任务日志列表 + /// + /// 流程实例Id + /// 流程单元节点Id + /// + public async Task> GetLogList(string processId, string unitId) + { + return await client.Queryable().Where(a => a.ProcessId == processId && a.UnitId == unitId) + .OrderByDescending(t => t.CreateDate).ToListAsync(); + } + + /// + /// 获取任务日志列表 + /// + /// 任务Id + /// + public async Task GetLogEntity(string taskId) + { + return await client.Queryable().Where(t => t.TaskId == taskId).FirstAsync(); + } + + /// + /// 更新任务日志状态为不是最近处理的 + /// + /// + /// + /// + public async Task> UpdateLog(string processId, string unitId) + { + var userId = _auth.GetUserId(); + var count = await client.Updateable() + .SetColumns(a => new WFTaskLog() { IsLast = 0 }) + .Where(a => a.ProcessId == processId && a.UnitId == unitId && a.IsLast == 1 && a.UserId == userId) + .ExecuteCommandAsync(); + return new Response + { + Result = count > 0, + Message = count > 0 ? "success" : "error" + }; + } + + + /// + /// 关闭任务日志撤销 + /// + /// 流程主键 + /// 任务主键 + /// + public async Task> CloseTaskLogCancel(string processId, string taskId) + { + var count = await client.Updateable() + .SetColumns(a => new WFTaskLog() { IsCancel = 0 }) + .Where(a => a.ProcessId == processId && a.TaskId == taskId) + .ExecuteCommandAsync(); + return new Response + { + Result = count > 0, + Message = count > 0 ? "success" : "error" + }; + } + + /// + /// 关闭任务日志撤销 + /// + /// 流程任务Id + /// + public async Task> CloseTaskLogCancel(string taskId) + { + var count = await client.Updateable() + .SetColumns(a => new WFTaskLog() { IsCancel = 0 }) + .Where(a => a.TaskId == taskId) + .ExecuteCommandAsync(); + return new Response + { + Result = count > 0, + Message = count > 0 ? "success" : "error" + }; + } + + + /// + /// 保存任务日志 + /// + /// 任务日志 + public async Task> AddLog(WFTaskLog wfTaskLogEntity) + { + wfTaskLogEntity.CreateDate = DateTime.Now; + wfTaskLogEntity.UserId = _auth.GetUserId(); + wfTaskLogEntity.UserName = _auth.GetUserNickName(); + wfTaskLogEntity.Id = Guid.NewGuid().ToString(); + var count = await client.Insertable(wfTaskLogEntity).ExecuteCommandAsync(); + return new Response + { + Result = count > 0, + Message = count > 0 ? "success" : "error" + }; + } + + /// + /// 删除任务日志 + /// + /// 流程进程 + /// + public async Task> DeleteLog(string processId) + { + var count = await client.Deleteable() + .Where(a => a.ProcessId == processId) + .ExecuteCommandAsync(); + return new Response + { + Result = count > 0, + Message = count > 0 ? "success" : "error" + }; + } + + /// + /// 删除任务日志 + /// + /// 流程进程 + /// 来源任务密钥 + /// + public async Task> DeleteLog(string processId, string taskId) + { + var count = await client.Deleteable() + .Where(a => a.ProcessId == processId && a.TaskId == taskId && a.IsLast == 1) + .ExecuteCommandAsync(); + return new Response + { + Result = count > 0, + Message = count > 0 ? "success" : "error" + }; + } + + /// + /// 删除任务日志 + /// + /// 任务主键 + /// + public async Task> DeleteLogByTaskId(string taskId) + { + var count = await client.Deleteable() + .Where(a => a.TaskId == taskId) + .ExecuteCommandAsync(); + return new Response + { + Result = count > 0, + Message = count > 0 ? "success" : "error" + }; + } + + #endregion + + + public Repository.Domain.WFTask GetNodeByProcessId(string id) + { + return Repository.GetFirst(a => a.ProcessId == id); + } + + public void OriginalAdd(Repository.Domain.WFTask node) + { + Repository.AsInsertable(node).IgnoreColumnsNull().ExecuteCommand(); + } + } +} \ No newline at end of file diff --git a/OpenAuth.App/Common/CommonData.cs b/OpenAuth.App/Common/CommonData.cs new file mode 100644 index 0000000..970116b --- /dev/null +++ b/OpenAuth.App/Common/CommonData.cs @@ -0,0 +1,45 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace OpenAuth.App.Common +{ + public class CommonData + { + /// + /// 执行cmd命令 + /// + /// + /// + public static void ExeCmdProcess(string strDir, string _path) + { + Process p = new Process(); + p.StartInfo.FileName = "cmd.exe "; + p.StartInfo.UseShellExecute = false; + p.StartInfo.RedirectStandardInput = true; + p.StartInfo.RedirectStandardOutput = true; + p.StartInfo.RedirectStandardError = true; + p.StartInfo.CreateNoWindow = false; + + //指定执行程序的目录 + p.StartInfo.WorkingDirectory = _path; + //开始执行 + p.Start(); + //Console.WriteLine("command:" + strInput); + p.StandardInput.WriteLine(strDir.ToString() + "&exit"); + + //执行结果返回 + string output = p.StandardOutput.ReadToEnd(); + + //等待执行完成 + p.WaitForExit(); + + //关闭程序 + p.Close(); + } + + } +} diff --git a/OpenAuth.App/Const/TubanZhuanTi.cs b/OpenAuth.App/Const/TubanZhuanTi.cs new file mode 100644 index 0000000..fb3114a --- /dev/null +++ b/OpenAuth.App/Const/TubanZhuanTi.cs @@ -0,0 +1,15 @@ +namespace OpenAuth.App.Common +{ + public struct TubanZhuanTi { + + public const string weifayongdi = "Subject_WFYD"; + public const string feifacaikuang = "Subject_FFCK"; + public const string zhongdianwenti1 = "Subject_ZDWT1"; + public const string zhongdianwenti2 = "Subject_ZDWT2"; + public const string gdflh = "Subject_GDFLH"; + public const string wfydwp = "Subject_WPWF"; + public const string xcsj = "Subject_XCSJ"; + public const string sthx = "Subject_STHX"; + public const string stxf = "Subject_STXF"; + } +} diff --git a/OpenAuth.App/DbExtension.cs b/OpenAuth.App/DbExtension.cs new file mode 100644 index 0000000..d425ea5 --- /dev/null +++ b/OpenAuth.App/DbExtension.cs @@ -0,0 +1,53 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Data; +using System.Linq; +using System.Text; +using Infrastructure; +using SqlSugar; + +namespace OpenAuth.App +{ + public class DbExtension + { + private ISqlSugarClient _context; + + public DbExtension(ISqlSugarClient context) + { + _context = context; + } + + /// + /// 获取数据库一个表的所有属性值及属性描述 + /// + /// 模块名称/表名 + /// + public List GetProperties(Type type) + { + var result = new List(); + + var entity = _context.EntityMaintenance.GetEntityInfo(type); + + foreach (var column in entity.Columns) + { + object[] objs = column.PropertyInfo.GetCustomAttributes(typeof(DescriptionAttribute), true); + var description = objs.Length > 0 ? ((DescriptionAttribute)objs[0]).Description : column.ColumnDescription; + if (string.IsNullOrEmpty(description)) description = column.DbColumnName; + + object[] browsableObjs = column.PropertyInfo.GetCustomAttributes(typeof(BrowsableAttribute), true); + bool browsable = browsableObjs == null || browsableObjs.Length == 0 || + ((BrowsableAttribute)browsableObjs[0]).Browsable; + + result.Add(new KeyDescription + { + Key = column.DbColumnName, + Description = description, + Browsable = browsable, + Type = column.PropertyInfo.PropertyType.Name + }); + } + return result; + } + } +} \ No newline at end of file diff --git a/OpenAuth.App/OpenAuth.App.csproj b/OpenAuth.App/OpenAuth.App.csproj new file mode 100644 index 0000000..5a2ff4b --- /dev/null +++ b/OpenAuth.App/OpenAuth.App.csproj @@ -0,0 +1,54 @@ + + + + net6.0 + enable + + + + bin\Debug\net5.0\OpenAuth.App.xml + 1701;1702;1591;1573;1572;1570 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/OpenAuth.Identity/Config.cs b/OpenAuth.Identity/Config.cs new file mode 100644 index 0000000..1429d1e --- /dev/null +++ b/OpenAuth.Identity/Config.cs @@ -0,0 +1,69 @@ +using System.Collections.Generic; +using System.Security.Claims; +using IdentityModel; +using IdentityServer4; +using IdentityServer4.Models; + +namespace OpenAuth.IdentityServer +{ + public static class Config + { + #region scopes + public static IEnumerable ApiScopes => new List + { + new ApiScope("api1", "My API") + }; + #endregion + + #region clients + /// + /// 客户端信息 + /// + /// + public static IEnumerable GetClients() + { + return new[] + { + new Client + { + ClientId = "weigh", + AllowedGrantTypes = GrantTypes.ClientCredentials, + ClientSecrets = + { + new Secret("jibeikuangchang".Sha256()) + }, + AllowedScopes = { "api1" } + } + }; + } + #endregion + + #region Resources + public static IEnumerable GetIdentityResources() + { + return new IdentityResource[] + { + new IdentityResources.OpenId(), + new IdentityResources.Profile(), + }; + } + #endregion + + #region Api + /// + /// API信息 + /// + /// + public static IEnumerable GetApis() + { + return new[] + { + new ApiResource("openauthapi", "OpenAuth.WebApi") + { + UserClaims = { ClaimTypes.Name, JwtClaimTypes.Name } + } + }; + } + #endregion + } +} \ No newline at end of file diff --git a/OpenAuth.Identity/CustomProfileService.cs b/OpenAuth.Identity/CustomProfileService.cs new file mode 100644 index 0000000..1edd218 --- /dev/null +++ b/OpenAuth.Identity/CustomProfileService.cs @@ -0,0 +1,101 @@ +using System.Linq; +using System.Security.Claims; +using System.Threading.Tasks; +using IdentityModel; +using IdentityServer4.Extensions; +using IdentityServer4.Models; +using IdentityServer4.Services; +using IdentityServer4.Test; +using Infrastructure; +using Microsoft.Extensions.Logging; +using OpenAuth.App; +using OpenAuth.Repository.Domain; + +namespace OpenAuth.IdentityServer +{ + public class CustomProfileService : IProfileService + { + /// + /// The logger + /// + protected readonly ILogger Logger; + + protected UserManagerApp UserManager; + + /// + /// Initializes a new instance of the class. + /// + /// The users. + /// The logger. + public CustomProfileService(ILogger logger, UserManagerApp userManager) + { + Logger = logger; + UserManager = userManager; + } + + /// + /// 只要有关用户的身份信息单元被请求(例如在令牌创建期间或通过用户信息终点),就会调用此方法 + /// + /// The context. + /// + public virtual Task GetProfileDataAsync(ProfileDataRequestContext context) + { + context.LogProfileRequest(Logger); + + //判断是否有请求Claim信息 + if (context.RequestedClaimTypes.Any()) + { + var user = GetUserById(long.Parse(context.Subject.GetSubjectId())); + if (user != null) + { + //调用此方法以后内部会进行过滤,只将用户请求的Claim加入到 context.IssuedClaims 集合中 这样我们的请求方便能正常获取到所需Claim + var claims = new[] + { + new Claim(ClaimTypes.Name, user.Account), //请求用户的账号,这个可以保证User.Identity.Name有值 + new Claim(JwtClaimTypes.Name, user.Name), //请求用户的姓名 + }; + //返回apiresource中定义的claims + context.AddRequestedClaims(claims); + } + } + + context.LogIssuedClaims(Logger); + + return Task.CompletedTask; + } + + /// + /// 验证用户是否有效 例如:token创建或者验证 + /// + /// The context. + /// + public virtual Task IsActiveAsync(IsActiveContext context) + { + Logger.LogDebug("IsActive called from: {caller}", context.Caller); + + var user = GetUserById(long.Parse(context.Subject.GetSubjectId())); + context.IsActive = user?.Status == 0; + return Task.CompletedTask; + } + + private SysUser GetUserById(long id) + { + SysUser user; + if (id == -1) + { + user = new SysUser + { + Account = Define.SYSTEM_USERNAME, + Id = -1, + Name = Define.SYSTEM_USERNAME + }; + } + else + { + user = UserManager.Get(id); + } + + return user; + } + } +} diff --git a/OpenAuth.Identity/OpenAuth.IdentityServer.csproj b/OpenAuth.Identity/OpenAuth.IdentityServer.csproj new file mode 100644 index 0000000..4736818 --- /dev/null +++ b/OpenAuth.Identity/OpenAuth.IdentityServer.csproj @@ -0,0 +1,34 @@ + + + + net6.0 + enable + + + + 1701;1702;1591;1573;1572;1570 + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/OpenAuth.Identity/Program.cs b/OpenAuth.Identity/Program.cs new file mode 100644 index 0000000..c00d185 --- /dev/null +++ b/OpenAuth.Identity/Program.cs @@ -0,0 +1,43 @@ +// Copyright (c) Brock Allen & Dominick Baier. All rights reserved. +// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. + + +using System; +using Autofac.Extensions.DependencyInjection; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Hosting; +using Serilog; +using Serilog.Events; +using Serilog.Sinks.SystemConsole.Themes; + +namespace OpenAuth.IdentityServer +{ + public class Program + { + public static void Main(string[] args) + { + Console.Title = "IdentityServer4"; + + CreateWebHostBuilder(args).Build().Run(); + } + + public static IHostBuilder CreateWebHostBuilder(string[] args) + { + return Host.CreateDefaultBuilder(args) + .UseServiceProviderFactory(new AutofacServiceProviderFactory()) //将默认ServiceProviderFactory指定为AutofacServiceProviderFactory + .ConfigureWebHostDefaults(webBuilder => + { + webBuilder.UseUrls("http://*:12796").UseStartup(); + }).UseSerilog((context, configuration) => + { + configuration + .MinimumLevel.Debug() + .MinimumLevel.Override("Microsoft", LogEventLevel.Warning) + .MinimumLevel.Override("System", LogEventLevel.Warning) + .MinimumLevel.Override("Microsoft.AspNetCore.Authentication", LogEventLevel.Information) + .Enrich.FromLogContext() + .WriteTo.Console(outputTemplate: "[{Timestamp:HH:mm:ss} {Level}] {SourceContext}{NewLine}{Message:lj}{NewLine}{Exception}{NewLine}", theme: AnsiConsoleTheme.Literate); + }); + } + } +} \ No newline at end of file diff --git a/OpenAuth.Identity/Properties/launchSettings.json b/OpenAuth.Identity/Properties/launchSettings.json new file mode 100644 index 0000000..699c7ca --- /dev/null +++ b/OpenAuth.Identity/Properties/launchSettings.json @@ -0,0 +1,13 @@ +{ + "profiles": { + "IdentityServer": { + "commandName": "Project", + "launchBrowser": false, + "launchUrl": ".well-known/openid-configuration", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + }, + "applicationUrl": "http://localhost:12796" + } + } +} \ No newline at end of file diff --git a/OpenAuth.Identity/Quickstart/Account/AccountController.cs b/OpenAuth.Identity/Quickstart/Account/AccountController.cs new file mode 100644 index 0000000..fd21fa2 --- /dev/null +++ b/OpenAuth.Identity/Quickstart/Account/AccountController.cs @@ -0,0 +1,384 @@ +// Copyright (c) Brock Allen & Dominick Baier. All rights reserved. +// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. + + +using System; +using System.Linq; +using System.Threading.Tasks; +using IdentityModel; +using IdentityServer4.Events; +using IdentityServer4.Extensions; +using IdentityServer4.Models; +using IdentityServer4.Services; +using IdentityServer4.Stores; +using Infrastructure; +using Microsoft.AspNetCore.Authentication; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; +using OpenAuth.App; +using OpenAuth.Repository.Domain; + +namespace OpenAuth.IdentityServer.Quickstart.Account +{ + /// + /// This sample controller implements a typical login/logout/provision workflow for local and external accounts. + /// The login service encapsulates the interactions with the user data store. This data store is in-memory only and cannot be used for production! + /// The interaction service provides a way for the UI to communicate with identityserver for validation and context retrieval + /// + [SecurityHeaders] + [AllowAnonymous] + public class AccountController : Controller + { + private readonly UserManagerApp _userManager; + private readonly IIdentityServerInteractionService _interaction; + private readonly IClientStore _clientStore; + private readonly IAuthenticationSchemeProvider _schemeProvider; + private readonly IEventService _events; + + public AccountController( + IIdentityServerInteractionService interaction, + IClientStore clientStore, + IAuthenticationSchemeProvider schemeProvider, + IEventService events, UserManagerApp userManager) + { + + _interaction = interaction; + _clientStore = clientStore; + _schemeProvider = schemeProvider; + _events = events; + _userManager = userManager; + } + + /// + /// Entry point into the login workflow + /// + [HttpGet] + public async Task Login(string returnUrl) + { + // build a model so we know what to show on the login page + var vm = await BuildLoginViewModelAsync(returnUrl); + + if (vm.IsExternalLoginOnly) + { + // we only have one option for logging in and it's an external provider + return RedirectToAction("Challenge", "External", new { provider = vm.ExternalLoginScheme, returnUrl }); + } + + return View(vm); + } + + /// + /// Handle postback from username/password login + /// + [HttpPost] + [ValidateAntiForgeryToken] + public async Task Login(LoginInputModel model, string button) + { + // check if we are in the context of an authorization request + var context = await _interaction.GetAuthorizationContextAsync(model.ReturnUrl); + + // the user clicked the "cancel" button + if (button != "login") + { + if (context != null) + { + // if the user cancels, send a result back into IdentityServer as if they + // denied the consent (even if this client does not require consent). + // this will send back an access denied OIDC error response to the client. + await _interaction.GrantConsentAsync(context, ConsentResponse.Denied); + + // we can trust model.ReturnUrl since GetAuthorizationContextAsync returned non-null + if (await _clientStore.IsPkceClientAsync(context.ClientId)) + { + // if the client is PKCE then we assume it's native, so this change in how to + // return the response is for better UX for the end user. + return View("Redirect", new RedirectViewModel { RedirectUrl = model.ReturnUrl }); + } + + return Redirect(model.ReturnUrl); + } + else + { + // since we don't have a valid context, then we just go back to the home page + return Redirect("~/"); + } + } + + if (ModelState.IsValid) + { + SysUser user; + if (model.Username == Define.SYSTEM_USERNAME && model.Password == Define.SYSTEM_USERPWD) + { + user = new SysUser + { + Account = Define.SYSTEM_USERNAME, + Password = Define.SYSTEM_USERPWD, + Id = Define.SYSTEM_USERNAME + }; + } + else + { + user = _userManager.GetByAccount(model.Username); + } + + if (user != null &&(user.Password ==model.Password)) + { + if (user.Status != 0) //判断用户状态 + { + await _events.RaiseAsync(new UserLoginFailureEvent(model.Username, "invalid user status")); + ModelState.AddModelError(string.Empty, "user.status must be 0"); + var err = await BuildLoginViewModelAsync(model); + return View(err); + } + + await _events.RaiseAsync(new UserLoginSuccessEvent(user.Account, user.Id, user.Account)); + + // only set explicit expiration here if user chooses "remember me". + // otherwise we rely upon expiration configured in cookie middleware. + AuthenticationProperties props = null; + if (AccountOptions.AllowRememberLogin && model.RememberLogin) + { + props = new AuthenticationProperties + { + IsPersistent = true, + ExpiresUtc = DateTimeOffset.UtcNow.Add(AccountOptions.RememberMeLoginDuration) + }; + }; + + // issue authentication cookie with subject ID and username + await HttpContext.SignInAsync(user.Id, user.Account, props); + + if (context != null) + { + if (await _clientStore.IsPkceClientAsync(context.ClientId)) + { + // if the client is PKCE then we assume it's native, so this change in how to + // return the response is for better UX for the end user. + return View("Redirect", new RedirectViewModel { RedirectUrl = model.ReturnUrl }); + } + + // we can trust model.ReturnUrl since GetAuthorizationContextAsync returned non-null + return Redirect(model.ReturnUrl); + } + + // request for a local page + if (Url.IsLocalUrl(model.ReturnUrl)) + { + return Redirect(model.ReturnUrl); + } + else if (string.IsNullOrEmpty(model.ReturnUrl)) + { + return Redirect("~/"); + } + else + { + // user might have clicked on a malicious link - should be logged + throw new Exception("invalid return URL"); + } + } + + await _events.RaiseAsync(new UserLoginFailureEvent(model.Username, "invalid credentials", clientId:context?.ClientId)); + ModelState.AddModelError(string.Empty, AccountOptions.InvalidCredentialsErrorMessage); + } + + // something went wrong, show form with error + var vm = await BuildLoginViewModelAsync(model); + return View(vm); + } + + + /// + /// Show logout page + /// + [HttpGet] + public async Task Logout(string logoutId) + { + // build a model so the logout page knows what to display + var vm = await BuildLogoutViewModelAsync(logoutId); + + if (vm.ShowLogoutPrompt == false) + { + // if the request for logout was properly authenticated from IdentityServer, then + // we don't need to show the prompt and can just log the user out directly. + return await Logout(vm); + } + + return View(vm); + } + + /// + /// Handle logout page postback + /// + [HttpPost] + [ValidateAntiForgeryToken] + public async Task Logout(LogoutInputModel model) + { + // build a model so the logged out page knows what to display + var vm = await BuildLoggedOutViewModelAsync(model.LogoutId); + + if (User?.Identity.IsAuthenticated == true) + { + // delete local authentication cookie + await HttpContext.SignOutAsync(); + + // raise the logout event + await _events.RaiseAsync(new UserLogoutSuccessEvent(User.GetSubjectId(), User.GetDisplayName())); + } + + // check if we need to trigger sign-out at an upstream identity provider + if (vm.TriggerExternalSignout) + { + // build a return URL so the upstream provider will redirect back + // to us after the user has logged out. this allows us to then + // complete our single sign-out processing. + string url = Url.Action("Logout", new { logoutId = vm.LogoutId }); + + // this triggers a redirect to the external provider for sign-out + return SignOut(new AuthenticationProperties { RedirectUri = url }, vm.ExternalAuthenticationScheme); + } + + return View("LoggedOut", vm); + } + + [HttpGet] + public IActionResult AccessDenied() + { + return View(); + } + + + /*****************************************/ + /* helper APIs for the AccountController */ + /*****************************************/ + private async Task BuildLoginViewModelAsync(string returnUrl) + { + var context = await _interaction.GetAuthorizationContextAsync(returnUrl); + if (context?.IdP != null && await _schemeProvider.GetSchemeAsync(context.IdP) != null) + { + var local = context.IdP == IdentityServer4.IdentityServerConstants.LocalIdentityProvider; + + // this is meant to short circuit the UI and only trigger the one external IdP + var vm = new LoginViewModel + { + EnableLocalLogin = local, + ReturnUrl = returnUrl, + Username = context?.LoginHint, + }; + + if (!local) + { + vm.ExternalProviders = new[] { new ExternalProvider { AuthenticationScheme = context.IdP } }; + } + + return vm; + } + + var schemes = await _schemeProvider.GetAllSchemesAsync(); + + var providers = schemes + .Where(x => x.DisplayName != null || + (x.Name.Equals(AccountOptions.WindowsAuthenticationSchemeName, StringComparison.OrdinalIgnoreCase)) + ) + .Select(x => new ExternalProvider + { + DisplayName = x.DisplayName, + AuthenticationScheme = x.Name + }).ToList(); + + var allowLocal = true; + if (context?.ClientId != null) + { + var client = await _clientStore.FindEnabledClientByIdAsync(context.ClientId); + if (client != null) + { + allowLocal = client.EnableLocalLogin; + + if (client.IdentityProviderRestrictions != null && client.IdentityProviderRestrictions.Any()) + { + providers = providers.Where(provider => client.IdentityProviderRestrictions.Contains(provider.AuthenticationScheme)).ToList(); + } + } + } + + return new LoginViewModel + { + AllowRememberLogin = AccountOptions.AllowRememberLogin, + EnableLocalLogin = allowLocal && AccountOptions.AllowLocalLogin, + ReturnUrl = returnUrl, + Username = context?.LoginHint, + ExternalProviders = providers.ToArray() + }; + } + + private async Task BuildLoginViewModelAsync(LoginInputModel model) + { + var vm = await BuildLoginViewModelAsync(model.ReturnUrl); + vm.Username = model.Username; + vm.RememberLogin = model.RememberLogin; + return vm; + } + + private async Task BuildLogoutViewModelAsync(string logoutId) + { + var vm = new LogoutViewModel { LogoutId = logoutId, ShowLogoutPrompt = AccountOptions.ShowLogoutPrompt }; + + if (User?.Identity.IsAuthenticated != true) + { + // if the user is not authenticated, then just show logged out page + vm.ShowLogoutPrompt = false; + return vm; + } + + var context = await _interaction.GetLogoutContextAsync(logoutId); + if (context?.ShowSignoutPrompt == false) + { + // it's safe to automatically sign-out + vm.ShowLogoutPrompt = false; + return vm; + } + + // show the logout prompt. this prevents attacks where the user + // is automatically signed out by another malicious web page. + return vm; + } + + private async Task BuildLoggedOutViewModelAsync(string logoutId) + { + // get context information (client name, post logout redirect URI and iframe for federated signout) + var logout = await _interaction.GetLogoutContextAsync(logoutId); + + var vm = new LoggedOutViewModel + { + AutomaticRedirectAfterSignOut = AccountOptions.AutomaticRedirectAfterSignOut, + PostLogoutRedirectUri = logout?.PostLogoutRedirectUri, + ClientName = string.IsNullOrEmpty(logout?.ClientName) ? logout?.ClientId : logout?.ClientName, + SignOutIframeUrl = logout?.SignOutIFrameUrl, + LogoutId = logoutId + }; + + if (User?.Identity.IsAuthenticated == true) + { + var idp = User.FindFirst(JwtClaimTypes.IdentityProvider)?.Value; + if (idp != null && idp != IdentityServer4.IdentityServerConstants.LocalIdentityProvider) + { + var providerSupportsSignout = await HttpContext.GetSchemeSupportsSignOutAsync(idp); + if (providerSupportsSignout) + { + if (vm.LogoutId == null) + { + // if there's no current logout context, we need to create one + // this captures necessary info from the current logged in user + // before we signout and redirect away to the external IdP for signout + vm.LogoutId = await _interaction.CreateLogoutContextAsync(); + } + + vm.ExternalAuthenticationScheme = idp; + } + } + } + + return vm; + } + } +} diff --git a/OpenAuth.Identity/Quickstart/Account/AccountOptions.cs b/OpenAuth.Identity/Quickstart/Account/AccountOptions.cs new file mode 100644 index 0000000..a451ce0 --- /dev/null +++ b/OpenAuth.Identity/Quickstart/Account/AccountOptions.cs @@ -0,0 +1,25 @@ +// Copyright (c) Brock Allen & Dominick Baier. All rights reserved. +// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. + + +using System; + +namespace OpenAuth.IdentityServer.Quickstart.Account +{ + public class AccountOptions + { + public static bool AllowLocalLogin = true; + public static bool AllowRememberLogin = true; + public static TimeSpan RememberMeLoginDuration = TimeSpan.FromDays(30); + + public static bool ShowLogoutPrompt = true; + public static bool AutomaticRedirectAfterSignOut = true; //允许注销后跳转 + + // specify the Windows authentication scheme being used + public static readonly string WindowsAuthenticationSchemeName = Microsoft.AspNetCore.Server.IISIntegration.IISDefaults.AuthenticationScheme; + // if user uses windows auth, should we load the groups from windows + public static bool IncludeWindowsGroups = false; + + public static string InvalidCredentialsErrorMessage = "Invalid username or password"; + } +} diff --git a/OpenAuth.Identity/Quickstart/Account/ExternalController.cs b/OpenAuth.Identity/Quickstart/Account/ExternalController.cs new file mode 100644 index 0000000..e3cd696 --- /dev/null +++ b/OpenAuth.Identity/Quickstart/Account/ExternalController.cs @@ -0,0 +1,251 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Security.Claims; +using System.Security.Principal; +using System.Threading.Tasks; +using IdentityModel; +using IdentityServer4.Events; +using IdentityServer4.Services; +using IdentityServer4.Stores; +using IdentityServer4.Test; +using Microsoft.AspNetCore.Authentication; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; + +namespace OpenAuth.IdentityServer.Quickstart.Account +{ + [SecurityHeaders] + [AllowAnonymous] + public class ExternalController : Controller + { + private readonly TestUserStore _users; + private readonly IIdentityServerInteractionService _interaction; + private readonly IClientStore _clientStore; + private readonly ILogger _logger; + private readonly IEventService _events; + + public ExternalController( + IIdentityServerInteractionService interaction, + IClientStore clientStore, + IEventService events, + ILogger logger, + TestUserStore users = null) + { + // if the TestUserStore is not in DI, then we'll just use the global users collection + // this is where you would plug in your own custom identity management library (e.g. ASP.NET Identity) + _users = users ?? new TestUserStore(TestUsers.Users); + + _interaction = interaction; + _clientStore = clientStore; + _logger = logger; + _events = events; + } + + /// + /// initiate roundtrip to external authentication provider + /// + [HttpGet] + public async Task Challenge(string provider, string returnUrl) + { + if (string.IsNullOrEmpty(returnUrl)) returnUrl = "~/"; + + // validate returnUrl - either it is a valid OIDC URL or back to a local page + if (Url.IsLocalUrl(returnUrl) == false && _interaction.IsValidReturnUrl(returnUrl) == false) + { + // user might have clicked on a malicious link - should be logged + throw new Exception("invalid return URL"); + } + + if (AccountOptions.WindowsAuthenticationSchemeName == provider) + { + // windows authentication needs special handling + return await ProcessWindowsLoginAsync(returnUrl); + } + else + { + // start challenge and roundtrip the return URL and scheme + var props = new AuthenticationProperties + { + RedirectUri = Url.Action(nameof(Callback)), + Items = + { + { "returnUrl", returnUrl }, + { "scheme", provider }, + } + }; + + return Challenge(props, provider); + } + } + + /// + /// Post processing of external authentication + /// + [HttpGet] + public async Task Callback() + { + // read external identity from the temporary cookie + var result = await HttpContext.AuthenticateAsync(IdentityServer4.IdentityServerConstants.ExternalCookieAuthenticationScheme); + if (result?.Succeeded != true) + { + throw new Exception("External authentication error"); + } + + if (_logger.IsEnabled(LogLevel.Debug)) + { + var externalClaims = result.Principal.Claims.Select(c => $"{c.Type}: {c.Value}"); + _logger.LogDebug("External claims: {@claims}", externalClaims); + } + + // lookup our user and external provider info + var (user, provider, providerUserId, claims) = FindUserFromExternalProvider(result); + if (user == null) + { + // this might be where you might initiate a custom workflow for user registration + // in this sample we don't show how that would be done, as our sample implementation + // simply auto-provisions new external user + user = AutoProvisionUser(provider, providerUserId, claims); + } + + // this allows us to collect any additonal claims or properties + // for the specific prtotocols used and store them in the local auth cookie. + // this is typically used to store data needed for signout from those protocols. + var additionalLocalClaims = new List(); + var localSignInProps = new AuthenticationProperties(); + ProcessLoginCallbackForOidc(result, additionalLocalClaims, localSignInProps); + ProcessLoginCallbackForWsFed(result, additionalLocalClaims, localSignInProps); + ProcessLoginCallbackForSaml2p(result, additionalLocalClaims, localSignInProps); + + // issue authentication cookie for user + await HttpContext.SignInAsync(user.SubjectId, user.Username, provider, localSignInProps, additionalLocalClaims.ToArray()); + + // delete temporary cookie used during external authentication + await HttpContext.SignOutAsync(IdentityServer4.IdentityServerConstants.ExternalCookieAuthenticationScheme); + + // retrieve return URL + var returnUrl = result.Properties.Items["returnUrl"] ?? "~/"; + + // check if external login is in the context of an OIDC request + var context = await _interaction.GetAuthorizationContextAsync(returnUrl); + await _events.RaiseAsync(new UserLoginSuccessEvent(provider, providerUserId, user.SubjectId, user.Username, true, context?.ClientId)); + + if (context != null) + { + if (await _clientStore.IsPkceClientAsync(context.ClientId)) + { + // if the client is PKCE then we assume it's native, so this change in how to + // return the response is for better UX for the end user. + return View("Redirect", new RedirectViewModel { RedirectUrl = returnUrl }); + } + } + + return Redirect(returnUrl); + } + + private async Task ProcessWindowsLoginAsync(string returnUrl) + { + // see if windows auth has already been requested and succeeded + var result = await HttpContext.AuthenticateAsync(AccountOptions.WindowsAuthenticationSchemeName); + if (result?.Principal is WindowsPrincipal wp) + { + // we will issue the external cookie and then redirect the + // user back to the external callback, in essence, treating windows + // auth the same as any other external authentication mechanism + var props = new AuthenticationProperties() + { + RedirectUri = Url.Action("Callback"), + Items = + { + { "returnUrl", returnUrl }, + { "scheme", AccountOptions.WindowsAuthenticationSchemeName }, + } + }; + + var id = new ClaimsIdentity(AccountOptions.WindowsAuthenticationSchemeName); + id.AddClaim(new Claim(JwtClaimTypes.Subject, wp.Identity.Name)); + id.AddClaim(new Claim(JwtClaimTypes.Name, wp.Identity.Name)); + + // add the groups as claims -- be careful if the number of groups is too large + if (AccountOptions.IncludeWindowsGroups) + { + var wi = wp.Identity as WindowsIdentity; + var groups = wi.Groups.Translate(typeof(NTAccount)); + var roles = groups.Select(x => new Claim(JwtClaimTypes.Role, x.Value)); + id.AddClaims(roles); + } + + await HttpContext.SignInAsync( + IdentityServer4.IdentityServerConstants.ExternalCookieAuthenticationScheme, + new ClaimsPrincipal(id), + props); + return Redirect(props.RedirectUri); + } + else + { + // trigger windows auth + // since windows auth don't support the redirect uri, + // this URL is re-triggered when we call challenge + return Challenge(AccountOptions.WindowsAuthenticationSchemeName); + } + } + + private (TestUser user, string provider, string providerUserId, IEnumerable claims) FindUserFromExternalProvider(AuthenticateResult result) + { + var externalUser = result.Principal; + + // try to determine the unique id of the external user (issued by the provider) + // the most common claim type for that are the sub claim and the NameIdentifier + // depending on the external provider, some other claim type might be used + var userIdClaim = externalUser.FindFirst(JwtClaimTypes.Subject) ?? + externalUser.FindFirst(ClaimTypes.NameIdentifier) ?? + throw new Exception("Unknown userid"); + + // remove the user id claim so we don't include it as an extra claim if/when we provision the user + var claims = externalUser.Claims.ToList(); + claims.Remove(userIdClaim); + + var provider = result.Properties.Items["scheme"]; + var providerUserId = userIdClaim.Value; + + // find external user + var user = _users.FindByExternalProvider(provider, providerUserId); + + return (user, provider, providerUserId, claims); + } + + private TestUser AutoProvisionUser(string provider, string providerUserId, IEnumerable claims) + { + var user = _users.AutoProvisionUser(provider, providerUserId, claims.ToList()); + return user; + } + + private void ProcessLoginCallbackForOidc(AuthenticateResult externalResult, List localClaims, AuthenticationProperties localSignInProps) + { + // if the external system sent a session id claim, copy it over + // so we can use it for single sign-out + var sid = externalResult.Principal.Claims.FirstOrDefault(x => x.Type == JwtClaimTypes.SessionId); + if (sid != null) + { + localClaims.Add(new Claim(JwtClaimTypes.SessionId, sid.Value)); + } + + // if the external provider issued an id_token, we'll keep it for signout + var id_token = externalResult.Properties.GetTokenValue("id_token"); + if (id_token != null) + { + localSignInProps.StoreTokens(new[] { new AuthenticationToken { Name = "id_token", Value = id_token } }); + } + } + + private void ProcessLoginCallbackForWsFed(AuthenticateResult externalResult, List localClaims, AuthenticationProperties localSignInProps) + { + } + + private void ProcessLoginCallbackForSaml2p(AuthenticateResult externalResult, List localClaims, AuthenticationProperties localSignInProps) + { + } + } +} diff --git a/OpenAuth.Identity/Quickstart/Account/ExternalProvider.cs b/OpenAuth.Identity/Quickstart/Account/ExternalProvider.cs new file mode 100644 index 0000000..9e8c9f7 --- /dev/null +++ b/OpenAuth.Identity/Quickstart/Account/ExternalProvider.cs @@ -0,0 +1,12 @@ +// Copyright (c) Brock Allen & Dominick Baier. All rights reserved. +// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. + + +namespace OpenAuth.IdentityServer.Quickstart.Account +{ + public class ExternalProvider + { + public string DisplayName { get; set; } + public string AuthenticationScheme { get; set; } + } +} \ No newline at end of file diff --git a/OpenAuth.Identity/Quickstart/Account/LoggedOutViewModel.cs b/OpenAuth.Identity/Quickstart/Account/LoggedOutViewModel.cs new file mode 100644 index 0000000..3eae772 --- /dev/null +++ b/OpenAuth.Identity/Quickstart/Account/LoggedOutViewModel.cs @@ -0,0 +1,19 @@ +// Copyright (c) Brock Allen & Dominick Baier. All rights reserved. +// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. + + +namespace OpenAuth.IdentityServer.Quickstart.Account +{ + public class LoggedOutViewModel + { + public string PostLogoutRedirectUri { get; set; } + public string ClientName { get; set; } + public string SignOutIframeUrl { get; set; } + + public bool AutomaticRedirectAfterSignOut { get; set; } = false; + + public string LogoutId { get; set; } + public bool TriggerExternalSignout => ExternalAuthenticationScheme != null; + public string ExternalAuthenticationScheme { get; set; } + } +} \ No newline at end of file diff --git a/OpenAuth.Identity/Quickstart/Account/LoginInputModel.cs b/OpenAuth.Identity/Quickstart/Account/LoginInputModel.cs new file mode 100644 index 0000000..58fd09c --- /dev/null +++ b/OpenAuth.Identity/Quickstart/Account/LoginInputModel.cs @@ -0,0 +1,18 @@ +// Copyright (c) Brock Allen & Dominick Baier. All rights reserved. +// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. + + +using System.ComponentModel.DataAnnotations; + +namespace OpenAuth.IdentityServer.Quickstart.Account +{ + public class LoginInputModel + { + [Required] + public string Username { get; set; } + [Required] + public string Password { get; set; } + public bool RememberLogin { get; set; } + public string ReturnUrl { get; set; } + } +} \ No newline at end of file diff --git a/OpenAuth.Identity/Quickstart/Account/LoginViewModel.cs b/OpenAuth.Identity/Quickstart/Account/LoginViewModel.cs new file mode 100644 index 0000000..86b273a --- /dev/null +++ b/OpenAuth.Identity/Quickstart/Account/LoginViewModel.cs @@ -0,0 +1,22 @@ +// Copyright (c) Brock Allen & Dominick Baier. All rights reserved. +// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. + + +using System; +using System.Collections.Generic; +using System.Linq; + +namespace OpenAuth.IdentityServer.Quickstart.Account +{ + public class LoginViewModel : LoginInputModel + { + public bool AllowRememberLogin { get; set; } = true; + public bool EnableLocalLogin { get; set; } = true; + + public IEnumerable ExternalProviders { get; set; } = Enumerable.Empty(); + public IEnumerable VisibleExternalProviders => ExternalProviders.Where(x => !String.IsNullOrWhiteSpace(x.DisplayName)); + + public bool IsExternalLoginOnly => EnableLocalLogin == false && ExternalProviders?.Count() == 1; + public string ExternalLoginScheme => IsExternalLoginOnly ? ExternalProviders?.SingleOrDefault()?.AuthenticationScheme : null; + } +} \ No newline at end of file diff --git a/OpenAuth.Identity/Quickstart/Account/LogoutInputModel.cs b/OpenAuth.Identity/Quickstart/Account/LogoutInputModel.cs new file mode 100644 index 0000000..06f745a --- /dev/null +++ b/OpenAuth.Identity/Quickstart/Account/LogoutInputModel.cs @@ -0,0 +1,11 @@ +// Copyright (c) Brock Allen & Dominick Baier. All rights reserved. +// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. + + +namespace OpenAuth.IdentityServer.Quickstart.Account +{ + public class LogoutInputModel + { + public string LogoutId { get; set; } + } +} diff --git a/OpenAuth.Identity/Quickstart/Account/LogoutViewModel.cs b/OpenAuth.Identity/Quickstart/Account/LogoutViewModel.cs new file mode 100644 index 0000000..056cdf9 --- /dev/null +++ b/OpenAuth.Identity/Quickstart/Account/LogoutViewModel.cs @@ -0,0 +1,11 @@ +// Copyright (c) Brock Allen & Dominick Baier. All rights reserved. +// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. + + +namespace OpenAuth.IdentityServer.Quickstart.Account +{ + public class LogoutViewModel : LogoutInputModel + { + public bool ShowLogoutPrompt { get; set; } = true; + } +} diff --git a/OpenAuth.Identity/Quickstart/Account/RedirectViewModel.cs b/OpenAuth.Identity/Quickstart/Account/RedirectViewModel.cs new file mode 100644 index 0000000..6a6da8d --- /dev/null +++ b/OpenAuth.Identity/Quickstart/Account/RedirectViewModel.cs @@ -0,0 +1,12 @@ +// Copyright (c) Brock Allen & Dominick Baier. All rights reserved. +// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. + + + +namespace OpenAuth.IdentityServer.Quickstart.Account +{ + public class RedirectViewModel + { + public string RedirectUrl { get; set; } + } +} \ No newline at end of file diff --git a/OpenAuth.Identity/Quickstart/Consent/ConsentController.cs b/OpenAuth.Identity/Quickstart/Consent/ConsentController.cs new file mode 100644 index 0000000..95343f8 --- /dev/null +++ b/OpenAuth.Identity/Quickstart/Consent/ConsentController.cs @@ -0,0 +1,266 @@ +// Copyright (c) Brock Allen & Dominick Baier. All rights reserved. +// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. + + +using System.Linq; +using System.Threading.Tasks; +using IdentityServer4.Events; +using IdentityServer4.Extensions; +using IdentityServer4.Models; +using IdentityServer4.Services; +using IdentityServer4.Stores; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; +using OpenAuth.IdentityServer.Quickstart.Account; + +namespace OpenAuth.IdentityServer.Quickstart.Consent +{ + /// + /// This controller processes the consent UI + /// + [SecurityHeaders] + [Authorize] + public class ConsentController : Controller + { + private readonly IIdentityServerInteractionService _interaction; + private readonly IClientStore _clientStore; + private readonly IResourceStore _resourceStore; + private readonly IEventService _events; + private readonly ILogger _logger; + + public ConsentController( + IIdentityServerInteractionService interaction, + IClientStore clientStore, + IResourceStore resourceStore, + IEventService events, + ILogger logger) + { + _interaction = interaction; + _clientStore = clientStore; + _resourceStore = resourceStore; + _events = events; + _logger = logger; + } + + /// + /// Shows the consent screen + /// + /// + /// + [HttpGet] + public async Task Index(string returnUrl) + { + var vm = await BuildViewModelAsync(returnUrl); + if (vm != null) + { + return View("Index", vm); + } + + return View("Error"); + } + + /// + /// Handles the consent screen postback + /// + [HttpPost] + [ValidateAntiForgeryToken] + public async Task Index(ConsentInputModel model) + { + var result = await ProcessConsent(model); + + if (result.IsRedirect) + { + if (await _clientStore.IsPkceClientAsync(result.ClientId)) + { + // if the client is PKCE then we assume it's native, so this change in how to + // return the response is for better UX for the end user. + return View("Redirect", new RedirectViewModel { RedirectUrl = result.RedirectUri }); + } + + return Redirect(result.RedirectUri); + } + + if (result.HasValidationError) + { + ModelState.AddModelError(string.Empty, result.ValidationError); + } + + if (result.ShowView) + { + return View("Index", result.ViewModel); + } + + return View("Error"); + } + + /*****************************************/ + /* helper APIs for the ConsentController */ + /*****************************************/ + private async Task ProcessConsent(ConsentInputModel model) + { + var result = new ProcessConsentResult(); + + // validate return url is still valid + var request = await _interaction.GetAuthorizationContextAsync(model.ReturnUrl); + if (request == null) return result; + + ConsentResponse grantedConsent = null; + + // user clicked 'no' - send back the standard 'access_denied' response + if (model?.Button == "no") + { + grantedConsent = ConsentResponse.Denied; + + // emit event + await _events.RaiseAsync(new ConsentDeniedEvent(User.GetSubjectId(), request.ClientId, request.ScopesRequested)); + } + // user clicked 'yes' - validate the data + else if (model?.Button == "yes") + { + // if the user consented to some scope, build the response model + if (model.ScopesConsented != null && model.ScopesConsented.Any()) + { + var scopes = model.ScopesConsented; + if (ConsentOptions.EnableOfflineAccess == false) + { + scopes = scopes.Where(x => x != IdentityServer4.IdentityServerConstants.StandardScopes.OfflineAccess); + } + + grantedConsent = new ConsentResponse + { + RememberConsent = model.RememberConsent, + ScopesConsented = scopes.ToArray() + }; + + // emit event + await _events.RaiseAsync(new ConsentGrantedEvent(User.GetSubjectId(), request.ClientId, request.ScopesRequested, grantedConsent.ScopesConsented, grantedConsent.RememberConsent)); + } + else + { + result.ValidationError = ConsentOptions.MustChooseOneErrorMessage; + } + } + else + { + result.ValidationError = ConsentOptions.InvalidSelectionErrorMessage; + } + + if (grantedConsent != null) + { + // communicate outcome of consent back to identityserver + await _interaction.GrantConsentAsync(request, grantedConsent); + + // indicate that's it ok to redirect back to authorization endpoint + result.RedirectUri = model.ReturnUrl; + result.ClientId = request.ClientId; + } + else + { + // we need to redisplay the consent UI + result.ViewModel = await BuildViewModelAsync(model.ReturnUrl, model); + } + + return result; + } + + private async Task BuildViewModelAsync(string returnUrl, ConsentInputModel model = null) + { + var request = await _interaction.GetAuthorizationContextAsync(returnUrl); + if (request != null) + { + var client = await _clientStore.FindEnabledClientByIdAsync(request.ClientId); + if (client != null) + { + var resources = await _resourceStore.FindEnabledResourcesByScopeAsync(request.ScopesRequested); + if (resources != null && (resources.IdentityResources.Any() || resources.ApiResources.Any())) + { + return CreateConsentViewModel(model, returnUrl, request, client, resources); + } + else + { + _logger.LogError("No scopes matching: {0}", request.ScopesRequested.Aggregate((x, y) => x + ", " + y)); + } + } + else + { + _logger.LogError("Invalid client id: {0}", request.ClientId); + } + } + else + { + _logger.LogError("No consent request matching request: {0}", returnUrl); + } + + return null; + } + + private ConsentViewModel CreateConsentViewModel( + ConsentInputModel model, string returnUrl, + AuthorizationRequest request, + Client client, Resources resources) + { + var vm = new ConsentViewModel + { + RememberConsent = model?.RememberConsent ?? true, + ScopesConsented = model?.ScopesConsented ?? Enumerable.Empty(), + + ReturnUrl = returnUrl, + + ClientName = client.ClientName ?? client.ClientId, + ClientUrl = client.ClientUri, + ClientLogoUrl = client.LogoUri, + AllowRememberConsent = client.AllowRememberConsent + }; + + vm.IdentityScopes = resources.IdentityResources.Select(x => CreateScopeViewModel(x, vm.ScopesConsented.Contains(x.Name) || model == null)).ToArray(); + vm.ResourceScopes = resources.ApiResources.SelectMany(x => x.Scopes).Select(x => CreateScopeViewModel(x, vm.ScopesConsented.Contains(x.Name) || model == null)).ToArray(); + if (ConsentOptions.EnableOfflineAccess && resources.OfflineAccess) + { + vm.ResourceScopes = vm.ResourceScopes.Union(new ScopeViewModel[] { + GetOfflineAccessScope(vm.ScopesConsented.Contains(IdentityServer4.IdentityServerConstants.StandardScopes.OfflineAccess) || model == null) + }); + } + + return vm; + } + + private ScopeViewModel CreateScopeViewModel(IdentityResource identity, bool check) + { + return new ScopeViewModel + { + Name = identity.Name, + DisplayName = identity.DisplayName, + Description = identity.Description, + Emphasize = identity.Emphasize, + Required = identity.Required, + Checked = check || identity.Required + }; + } + + public ScopeViewModel CreateScopeViewModel(Scope scope, bool check) + { + return new ScopeViewModel + { + Name = scope.Name, + DisplayName = scope.DisplayName, + Description = scope.Description, + Emphasize = scope.Emphasize, + Required = scope.Required, + Checked = check || scope.Required + }; + } + + private ScopeViewModel GetOfflineAccessScope(bool check) + { + return new ScopeViewModel + { + Name = IdentityServer4.IdentityServerConstants.StandardScopes.OfflineAccess, + DisplayName = ConsentOptions.OfflineAccessDisplayName, + Description = ConsentOptions.OfflineAccessDescription, + Emphasize = true, + Checked = check + }; + } + } +} \ No newline at end of file diff --git a/OpenAuth.Identity/Quickstart/Consent/ConsentInputModel.cs b/OpenAuth.Identity/Quickstart/Consent/ConsentInputModel.cs new file mode 100644 index 0000000..7aeb4c4 --- /dev/null +++ b/OpenAuth.Identity/Quickstart/Consent/ConsentInputModel.cs @@ -0,0 +1,16 @@ +// Copyright (c) Brock Allen & Dominick Baier. All rights reserved. +// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. + + +using System.Collections.Generic; + +namespace OpenAuth.IdentityServer.Quickstart.Consent +{ + public class ConsentInputModel + { + public string Button { get; set; } + public IEnumerable ScopesConsented { get; set; } + public bool RememberConsent { get; set; } + public string ReturnUrl { get; set; } + } +} \ No newline at end of file diff --git a/OpenAuth.Identity/Quickstart/Consent/ConsentOptions.cs b/OpenAuth.Identity/Quickstart/Consent/ConsentOptions.cs new file mode 100644 index 0000000..d472725 --- /dev/null +++ b/OpenAuth.Identity/Quickstart/Consent/ConsentOptions.cs @@ -0,0 +1,16 @@ +// Copyright (c) Brock Allen & Dominick Baier. All rights reserved. +// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. + + +namespace OpenAuth.IdentityServer.Quickstart.Consent +{ + public class ConsentOptions + { + public static bool EnableOfflineAccess = true; + public static string OfflineAccessDisplayName = "Offline Access"; + public static string OfflineAccessDescription = "Access to your applications and resources, even when you are offline"; + + public static readonly string MustChooseOneErrorMessage = "You must pick at least one permission"; + public static readonly string InvalidSelectionErrorMessage = "Invalid selection"; + } +} diff --git a/OpenAuth.Identity/Quickstart/Consent/ConsentViewModel.cs b/OpenAuth.Identity/Quickstart/Consent/ConsentViewModel.cs new file mode 100644 index 0000000..77a9044 --- /dev/null +++ b/OpenAuth.Identity/Quickstart/Consent/ConsentViewModel.cs @@ -0,0 +1,19 @@ +// Copyright (c) Brock Allen & Dominick Baier. All rights reserved. +// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. + + +using System.Collections.Generic; + +namespace OpenAuth.IdentityServer.Quickstart.Consent +{ + public class ConsentViewModel : ConsentInputModel + { + public string ClientName { get; set; } + public string ClientUrl { get; set; } + public string ClientLogoUrl { get; set; } + public bool AllowRememberConsent { get; set; } + + public IEnumerable IdentityScopes { get; set; } + public IEnumerable ResourceScopes { get; set; } + } +} diff --git a/OpenAuth.Identity/Quickstart/Consent/ProcessConsentResult.cs b/OpenAuth.Identity/Quickstart/Consent/ProcessConsentResult.cs new file mode 100644 index 0000000..e3b3f00 --- /dev/null +++ b/OpenAuth.Identity/Quickstart/Consent/ProcessConsentResult.cs @@ -0,0 +1,19 @@ +// Copyright (c) Brock Allen & Dominick Baier. All rights reserved. +// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. + + +namespace OpenAuth.IdentityServer.Quickstart.Consent +{ + public class ProcessConsentResult + { + public bool IsRedirect => RedirectUri != null; + public string RedirectUri { get; set; } + public string ClientId { get; set; } + + public bool ShowView => ViewModel != null; + public ConsentViewModel ViewModel { get; set; } + + public bool HasValidationError => ValidationError != null; + public string ValidationError { get; set; } + } +} diff --git a/OpenAuth.Identity/Quickstart/Consent/ScopeViewModel.cs b/OpenAuth.Identity/Quickstart/Consent/ScopeViewModel.cs new file mode 100644 index 0000000..a75451a --- /dev/null +++ b/OpenAuth.Identity/Quickstart/Consent/ScopeViewModel.cs @@ -0,0 +1,16 @@ +// Copyright (c) Brock Allen & Dominick Baier. All rights reserved. +// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. + + +namespace OpenAuth.IdentityServer.Quickstart.Consent +{ + public class ScopeViewModel + { + public string Name { get; set; } + public string DisplayName { get; set; } + public string Description { get; set; } + public bool Emphasize { get; set; } + public bool Required { get; set; } + public bool Checked { get; set; } + } +} diff --git a/OpenAuth.Identity/Quickstart/Device/DeviceAuthorizationInputModel.cs b/OpenAuth.Identity/Quickstart/Device/DeviceAuthorizationInputModel.cs new file mode 100644 index 0000000..c78b6a2 --- /dev/null +++ b/OpenAuth.Identity/Quickstart/Device/DeviceAuthorizationInputModel.cs @@ -0,0 +1,13 @@ +// Copyright (c) Brock Allen & Dominick Baier. All rights reserved. +// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. + + +using OpenAuth.IdentityServer.Quickstart.Consent; + +namespace OpenAuth.IdentityServer.Quickstart.Device +{ + public class DeviceAuthorizationInputModel : ConsentInputModel + { + public string UserCode { get; set; } + } +} \ No newline at end of file diff --git a/OpenAuth.Identity/Quickstart/Device/DeviceAuthorizationViewModel.cs b/OpenAuth.Identity/Quickstart/Device/DeviceAuthorizationViewModel.cs new file mode 100644 index 0000000..a1b2bb1 --- /dev/null +++ b/OpenAuth.Identity/Quickstart/Device/DeviceAuthorizationViewModel.cs @@ -0,0 +1,14 @@ +// Copyright (c) Brock Allen & Dominick Baier. All rights reserved. +// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. + + +using OpenAuth.IdentityServer.Quickstart.Consent; + +namespace OpenAuth.IdentityServer.Quickstart.Device +{ + public class DeviceAuthorizationViewModel : ConsentViewModel + { + public string UserCode { get; set; } + public bool ConfirmUserCode { get; set; } + } +} \ No newline at end of file diff --git a/OpenAuth.Identity/Quickstart/Device/DeviceController.cs b/OpenAuth.Identity/Quickstart/Device/DeviceController.cs new file mode 100644 index 0000000..15fe30c --- /dev/null +++ b/OpenAuth.Identity/Quickstart/Device/DeviceController.cs @@ -0,0 +1,243 @@ +// Copyright (c) Brock Allen & Dominick Baier. All rights reserved. +// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. + + +using System; +using System.Linq; +using System.Threading.Tasks; +using IdentityServer4.Configuration; +using IdentityServer4.Events; +using IdentityServer4.Extensions; +using IdentityServer4.Models; +using IdentityServer4.Services; +using IdentityServer4.Stores; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; +using OpenAuth.IdentityServer.Quickstart.Consent; + +namespace OpenAuth.IdentityServer.Quickstart.Device +{ + [Authorize] + [SecurityHeaders] + public class DeviceController : Controller + { + private readonly IDeviceFlowInteractionService _interaction; + private readonly IClientStore _clientStore; + private readonly IResourceStore _resourceStore; + private readonly IEventService _events; + private readonly IOptions _options; + private readonly ILogger _logger; + + public DeviceController( + IDeviceFlowInteractionService interaction, + IClientStore clientStore, + IResourceStore resourceStore, + IEventService eventService, + IOptions options, + ILogger logger) + { + _interaction = interaction; + _clientStore = clientStore; + _resourceStore = resourceStore; + _events = eventService; + _options = options; + _logger = logger; + } + + [HttpGet] + public async Task Index() + { + string userCodeParamName = _options.Value.UserInteraction.DeviceVerificationUserCodeParameter; + string userCode = Request.Query[userCodeParamName]; + if (string.IsNullOrWhiteSpace(userCode)) return View("UserCodeCapture"); + + var vm = await BuildViewModelAsync(userCode); + if (vm == null) return View("Error"); + + vm.ConfirmUserCode = true; + return View("UserCodeConfirmation", vm); + } + + [HttpPost] + [ValidateAntiForgeryToken] + public async Task UserCodeCapture(string userCode) + { + var vm = await BuildViewModelAsync(userCode); + if (vm == null) return View("Error"); + + return View("UserCodeConfirmation", vm); + } + + [HttpPost] + [ValidateAntiForgeryToken] + public async Task Callback(DeviceAuthorizationInputModel model) + { + if (model == null) throw new ArgumentNullException(nameof(model)); + + var result = await ProcessConsent(model); + if (result.HasValidationError) return View("Error"); + + return View("Success"); + } + + private async Task ProcessConsent(DeviceAuthorizationInputModel model) + { + var result = new ProcessConsentResult(); + + var request = await _interaction.GetAuthorizationContextAsync(model.UserCode); + if (request == null) return result; + + ConsentResponse grantedConsent = null; + + // user clicked 'no' - send back the standard 'access_denied' response + if (model.Button == "no") + { + grantedConsent = ConsentResponse.Denied; + + // emit event + await _events.RaiseAsync(new ConsentDeniedEvent(User.GetSubjectId(), request.ClientId, request.ScopesRequested)); + } + // user clicked 'yes' - validate the data + else if (model.Button == "yes") + { + // if the user consented to some scope, build the response model + if (model.ScopesConsented != null && model.ScopesConsented.Any()) + { + var scopes = model.ScopesConsented; + if (ConsentOptions.EnableOfflineAccess == false) + { + scopes = scopes.Where(x => x != IdentityServer4.IdentityServerConstants.StandardScopes.OfflineAccess); + } + + grantedConsent = new ConsentResponse + { + RememberConsent = model.RememberConsent, + ScopesConsented = scopes.ToArray() + }; + + // emit event + await _events.RaiseAsync(new ConsentGrantedEvent(User.GetSubjectId(), request.ClientId, request.ScopesRequested, grantedConsent.ScopesConsented, grantedConsent.RememberConsent)); + } + else + { + result.ValidationError = ConsentOptions.MustChooseOneErrorMessage; + } + } + else + { + result.ValidationError = ConsentOptions.InvalidSelectionErrorMessage; + } + + if (grantedConsent != null) + { + // communicate outcome of consent back to identityserver + await _interaction.HandleRequestAsync(model.UserCode, grantedConsent); + + // indicate that's it ok to redirect back to authorization endpoint + result.RedirectUri = model.ReturnUrl; + result.ClientId = request.ClientId; + } + else + { + // we need to redisplay the consent UI + result.ViewModel = await BuildViewModelAsync(model.UserCode, model); + } + + return result; + } + + private async Task BuildViewModelAsync(string userCode, DeviceAuthorizationInputModel model = null) + { + var request = await _interaction.GetAuthorizationContextAsync(userCode); + if (request != null) + { + var client = await _clientStore.FindEnabledClientByIdAsync(request.ClientId); + if (client != null) + { + var resources = await _resourceStore.FindEnabledResourcesByScopeAsync(request.ScopesRequested); + if (resources != null && (resources.IdentityResources.Any() || resources.ApiResources.Any())) + { + return CreateConsentViewModel(userCode, model, client, resources); + } + else + { + _logger.LogError("No scopes matching: {0}", request.ScopesRequested.Aggregate((x, y) => x + ", " + y)); + } + } + else + { + _logger.LogError("Invalid client id: {0}", request.ClientId); + } + } + + return null; + } + + private DeviceAuthorizationViewModel CreateConsentViewModel(string userCode, DeviceAuthorizationInputModel model, Client client, Resources resources) + { + var vm = new DeviceAuthorizationViewModel + { + UserCode = userCode, + + RememberConsent = model?.RememberConsent ?? true, + ScopesConsented = model?.ScopesConsented ?? Enumerable.Empty(), + + ClientName = client.ClientName ?? client.ClientId, + ClientUrl = client.ClientUri, + ClientLogoUrl = client.LogoUri, + AllowRememberConsent = client.AllowRememberConsent + }; + + vm.IdentityScopes = resources.IdentityResources.Select(x => CreateScopeViewModel(x, vm.ScopesConsented.Contains(x.Name) || model == null)).ToArray(); + vm.ResourceScopes = resources.ApiResources.SelectMany(x => x.Scopes).Select(x => CreateScopeViewModel(x, vm.ScopesConsented.Contains(x.Name) || model == null)).ToArray(); + if (ConsentOptions.EnableOfflineAccess && resources.OfflineAccess) + { + vm.ResourceScopes = vm.ResourceScopes.Union(new[] + { + GetOfflineAccessScope(vm.ScopesConsented.Contains(IdentityServer4.IdentityServerConstants.StandardScopes.OfflineAccess) || model == null) + }); + } + + return vm; + } + + private ScopeViewModel CreateScopeViewModel(IdentityResource identity, bool check) + { + return new ScopeViewModel + { + Name = identity.Name, + DisplayName = identity.DisplayName, + Description = identity.Description, + Emphasize = identity.Emphasize, + Required = identity.Required, + Checked = check || identity.Required + }; + } + + public ScopeViewModel CreateScopeViewModel(Scope scope, bool check) + { + return new ScopeViewModel + { + Name = scope.Name, + DisplayName = scope.DisplayName, + Description = scope.Description, + Emphasize = scope.Emphasize, + Required = scope.Required, + Checked = check || scope.Required + }; + } + private ScopeViewModel GetOfflineAccessScope(bool check) + { + return new ScopeViewModel + { + Name = IdentityServer4.IdentityServerConstants.StandardScopes.OfflineAccess, + DisplayName = ConsentOptions.OfflineAccessDisplayName, + Description = ConsentOptions.OfflineAccessDescription, + Emphasize = true, + Checked = check + }; + } + } +} \ No newline at end of file diff --git a/OpenAuth.Identity/Quickstart/Diagnostics/DiagnosticsController.cs b/OpenAuth.Identity/Quickstart/Diagnostics/DiagnosticsController.cs new file mode 100644 index 0000000..e4a9c45 --- /dev/null +++ b/OpenAuth.Identity/Quickstart/Diagnostics/DiagnosticsController.cs @@ -0,0 +1,29 @@ +// Copyright (c) Brock Allen & Dominick Baier. All rights reserved. +// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. + + +using System.Linq; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Authentication; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; + +namespace OpenAuth.IdentityServer.Quickstart.Diagnostics +{ + [SecurityHeaders] + [Authorize] + public class DiagnosticsController : Controller + { + public async Task Index() + { + var localAddresses = new string[] { "127.0.0.1", "::1", HttpContext.Connection.LocalIpAddress.ToString() }; + if (!localAddresses.Contains(HttpContext.Connection.RemoteIpAddress.ToString())) + { + return NotFound(); + } + + var model = new DiagnosticsViewModel(await HttpContext.AuthenticateAsync()); + return View(model); + } + } +} \ No newline at end of file diff --git a/OpenAuth.Identity/Quickstart/Diagnostics/DiagnosticsViewModel.cs b/OpenAuth.Identity/Quickstart/Diagnostics/DiagnosticsViewModel.cs new file mode 100644 index 0000000..e24c242 --- /dev/null +++ b/OpenAuth.Identity/Quickstart/Diagnostics/DiagnosticsViewModel.cs @@ -0,0 +1,32 @@ +// Copyright (c) Brock Allen & Dominick Baier. All rights reserved. +// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. + + +using System.Collections.Generic; +using System.Text; +using IdentityModel; +using Microsoft.AspNetCore.Authentication; +using Newtonsoft.Json; + +namespace OpenAuth.IdentityServer.Quickstart.Diagnostics +{ + public class DiagnosticsViewModel + { + public DiagnosticsViewModel(AuthenticateResult result) + { + AuthenticateResult = result; + + if (result.Properties.Items.ContainsKey("client_list")) + { + var encoded = result.Properties.Items["client_list"]; + var bytes = Base64Url.Decode(encoded); + var value = Encoding.UTF8.GetString(bytes); + + Clients = JsonConvert.DeserializeObject(value); + } + } + + public AuthenticateResult AuthenticateResult { get; } + public IEnumerable Clients { get; } = new List(); + } +} \ No newline at end of file diff --git a/OpenAuth.Identity/Quickstart/Extensions.cs b/OpenAuth.Identity/Quickstart/Extensions.cs new file mode 100644 index 0000000..560e4b9 --- /dev/null +++ b/OpenAuth.Identity/Quickstart/Extensions.cs @@ -0,0 +1,25 @@ +using System.Threading.Tasks; +using IdentityServer4.Stores; + +namespace OpenAuth.IdentityServer.Quickstart +{ + public static class Extensions + { + /// + /// Determines whether the client is configured to use PKCE. + /// + /// The store. + /// The client identifier. + /// + public static async Task IsPkceClientAsync(this IClientStore store, string client_id) + { + if (!string.IsNullOrWhiteSpace(client_id)) + { + var client = await store.FindEnabledClientByIdAsync(client_id); + return client?.RequirePkce == true; + } + + return false; + } + } +} diff --git a/OpenAuth.Identity/Quickstart/Grants/GrantsController.cs b/OpenAuth.Identity/Quickstart/Grants/GrantsController.cs new file mode 100644 index 0000000..c1d2695 --- /dev/null +++ b/OpenAuth.Identity/Quickstart/Grants/GrantsController.cs @@ -0,0 +1,96 @@ +// Copyright (c) Brock Allen & Dominick Baier. All rights reserved. +// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. + + +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using IdentityServer4.Events; +using IdentityServer4.Extensions; +using IdentityServer4.Services; +using IdentityServer4.Stores; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; + +namespace OpenAuth.IdentityServer.Quickstart.Grants +{ + /// + /// This sample controller allows a user to revoke grants given to clients + /// + [SecurityHeaders] + [Authorize] + public class GrantsController : Controller + { + private readonly IIdentityServerInteractionService _interaction; + private readonly IClientStore _clients; + private readonly IResourceStore _resources; + private readonly IEventService _events; + + public GrantsController(IIdentityServerInteractionService interaction, + IClientStore clients, + IResourceStore resources, + IEventService events) + { + _interaction = interaction; + _clients = clients; + _resources = resources; + _events = events; + } + + /// + /// Show list of grants + /// + [HttpGet] + public async Task Index() + { + return View("Index", await BuildViewModelAsync()); + } + + /// + /// Handle postback to revoke a client + /// + [HttpPost] + [ValidateAntiForgeryToken] + public async Task Revoke(string clientId) + { + await _interaction.RevokeUserConsentAsync(clientId); + await _events.RaiseAsync(new GrantsRevokedEvent(User.GetSubjectId(), clientId)); + + return RedirectToAction("Index"); + } + + private async Task BuildViewModelAsync() + { + var grants = await _interaction.GetAllUserConsentsAsync(); + + var list = new List(); + foreach(var grant in grants) + { + var client = await _clients.FindClientByIdAsync(grant.ClientId); + if (client != null) + { + var resources = await _resources.FindResourcesByScopeAsync(grant.Scopes); + + var item = new GrantViewModel() + { + ClientId = client.ClientId, + ClientName = client.ClientName ?? client.ClientId, + ClientLogoUrl = client.LogoUri, + ClientUrl = client.ClientUri, + Created = grant.CreationTime, + Expires = grant.Expiration, + IdentityGrantNames = resources.IdentityResources.Select(x => x.DisplayName ?? x.Name).ToArray(), + ApiGrantNames = resources.ApiResources.Select(x => x.DisplayName ?? x.Name).ToArray() + }; + + list.Add(item); + } + } + + return new GrantsViewModel + { + Grants = list + }; + } + } +} \ No newline at end of file diff --git a/OpenAuth.Identity/Quickstart/Grants/GrantsViewModel.cs b/OpenAuth.Identity/Quickstart/Grants/GrantsViewModel.cs new file mode 100644 index 0000000..4281aeb --- /dev/null +++ b/OpenAuth.Identity/Quickstart/Grants/GrantsViewModel.cs @@ -0,0 +1,26 @@ +// Copyright (c) Brock Allen & Dominick Baier. All rights reserved. +// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. + + +using System; +using System.Collections.Generic; + +namespace OpenAuth.IdentityServer.Quickstart.Grants +{ + public class GrantsViewModel + { + public IEnumerable Grants { get; set; } + } + + public class GrantViewModel + { + public string ClientId { get; set; } + public string ClientName { get; set; } + public string ClientUrl { get; set; } + public string ClientLogoUrl { get; set; } + public DateTime Created { get; set; } + public DateTime? Expires { get; set; } + public IEnumerable IdentityGrantNames { get; set; } + public IEnumerable ApiGrantNames { get; set; } + } +} \ No newline at end of file diff --git a/OpenAuth.Identity/Quickstart/Home/ErrorViewModel.cs b/OpenAuth.Identity/Quickstart/Home/ErrorViewModel.cs new file mode 100644 index 0000000..cf06e2c --- /dev/null +++ b/OpenAuth.Identity/Quickstart/Home/ErrorViewModel.cs @@ -0,0 +1,22 @@ +// Copyright (c) Brock Allen & Dominick Baier. All rights reserved. +// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. + + +using IdentityServer4.Models; + +namespace OpenAuth.IdentityServer.Quickstart.Home +{ + public class ErrorViewModel + { + public ErrorViewModel() + { + } + + public ErrorViewModel(string error) + { + Error = new ErrorMessage { Error = error }; + } + + public ErrorMessage Error { get; set; } + } +} \ No newline at end of file diff --git a/OpenAuth.Identity/Quickstart/Home/HomeController.cs b/OpenAuth.Identity/Quickstart/Home/HomeController.cs new file mode 100644 index 0000000..2f89b2a --- /dev/null +++ b/OpenAuth.Identity/Quickstart/Home/HomeController.cs @@ -0,0 +1,64 @@ +// Copyright (c) Brock Allen & Dominick Baier. All rights reserved. +// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. + + +using System.Threading.Tasks; +using IdentityServer4.Services; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; + +namespace OpenAuth.IdentityServer.Quickstart.Home +{ + [SecurityHeaders] + [AllowAnonymous] + public class HomeController : Controller + { + private readonly IIdentityServerInteractionService _interaction; + private readonly IHostEnvironment _environment; + private readonly ILogger _logger; + + public HomeController(IIdentityServerInteractionService interaction, IHostEnvironment environment, ILogger logger) + { + _interaction = interaction; + _environment = environment; + _logger = logger; + } + + public IActionResult Index() + { + if (_environment.IsDevelopment()) + { + // only show in development + return View(); + } + + _logger.LogInformation("Homepage is disabled in production. Returning 404."); + return NotFound(); + } + + /// + /// Shows the error page + /// + public async Task Error(string errorId) + { + var vm = new ErrorViewModel(); + + // retrieve error details from identityserver + var message = await _interaction.GetErrorContextAsync(errorId); + if (message != null) + { + vm.Error = message; + + if (!_environment.IsDevelopment()) + { + // only show in development + message.ErrorDescription = null; + } + } + + return View("Error", vm); + } + } +} \ No newline at end of file diff --git a/OpenAuth.Identity/Quickstart/SecurityHeadersAttribute.cs b/OpenAuth.Identity/Quickstart/SecurityHeadersAttribute.cs new file mode 100644 index 0000000..9b28dfe --- /dev/null +++ b/OpenAuth.Identity/Quickstart/SecurityHeadersAttribute.cs @@ -0,0 +1,56 @@ +// Copyright (c) Brock Allen & Dominick Baier. All rights reserved. +// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. + + +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.Filters; + +namespace OpenAuth.IdentityServer.Quickstart +{ + public class SecurityHeadersAttribute : ActionFilterAttribute + { + public override void OnResultExecuting(ResultExecutingContext context) + { + var result = context.Result; + if (result is ViewResult) + { + // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Content-Type-Options + // if (!context.HttpContext.Response.Headers.ContainsKey("X-Content-Type-Options")) + // { + // context.HttpContext.Response.Headers.Add("X-Content-Type-Options", "nosniff"); + // } + // + // // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Frame-Options + // if (!context.HttpContext.Response.Headers.ContainsKey("X-Frame-Options")) + // { + // context.HttpContext.Response.Headers.Add("X-Frame-Options", "SAMEORIGIN"); + // } + + // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy + // var csp = "default-src 'self'; object-src 'none'; frame-ancestors 'none'; sandbox allow-forms allow-same-origin allow-scripts; base-uri 'self';"; + // also consider adding upgrade-insecure-requests once you have HTTPS in place for production + //csp += "upgrade-insecure-requests;"; + // also an example if you need client images to be displayed from twitter + // csp += "img-src 'self' https://pbs.twimg.com;"; + + // once for standards compliant browsers + // if (!context.HttpContext.Response.Headers.ContainsKey("Content-Security-Policy")) + // { + // context.HttpContext.Response.Headers.Add("Content-Security-Policy", csp); + // } + // // and once again for IE + // if (!context.HttpContext.Response.Headers.ContainsKey("X-Content-Security-Policy")) + // { + // context.HttpContext.Response.Headers.Add("X-Content-Security-Policy", csp); + // } + + // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Referrer-Policy + // var referrer_policy = "no-referrer"; + // if (!context.HttpContext.Response.Headers.ContainsKey("Referrer-Policy")) + // { + // context.HttpContext.Response.Headers.Add("Referrer-Policy", referrer_policy); + // } + } + } + } +} diff --git a/OpenAuth.Identity/Quickstart/TestUsers.cs b/OpenAuth.Identity/Quickstart/TestUsers.cs new file mode 100644 index 0000000..7735c10 --- /dev/null +++ b/OpenAuth.Identity/Quickstart/TestUsers.cs @@ -0,0 +1,27 @@ +// Copyright (c) Brock Allen & Dominick Baier. All rights reserved. +// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. + + +using System.Collections.Generic; +using System.Security.Claims; +using IdentityModel; +using IdentityServer4.Test; +using Infrastructure; +using OpenAuth.App; + +namespace OpenAuth.IdentityServer.Quickstart +{ + public class TestUsers + { + public static List Users = new List + { + new TestUser{SubjectId = "System", Username = Define.SYSTEM_USERNAME, Password = Define.SYSTEM_USERPWD, + Claims = + { + new Claim(JwtClaimTypes.Name, "System"), + new Claim(JwtClaimTypes.GivenName, "yubao"), + new Claim(JwtClaimTypes.FamilyName, "lee")} + } + }; + } +} \ No newline at end of file diff --git a/OpenAuth.Identity/SameSiteCookiesServiceCollectionExtensions.cs b/OpenAuth.Identity/SameSiteCookiesServiceCollectionExtensions.cs new file mode 100644 index 0000000..752f8c0 --- /dev/null +++ b/OpenAuth.Identity/SameSiteCookiesServiceCollectionExtensions.cs @@ -0,0 +1,136 @@ +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.DependencyInjection; + +namespace OpenAuth.IdentityServer +{ + public static class SameSiteCookiesServiceCollectionExtensions + { + /// + /// -1 defines the unspecified value, which tells ASPNET Core to NOT + /// send the SameSite attribute. With ASPNET Core 3.1 the + /// enum will have a definition for + /// Unspecified. + /// + private const SameSiteMode Unspecified = (SameSiteMode) (-1); + + /// + /// Configures a cookie policy to properly set the SameSite attribute + /// for Browsers that handle unknown values as Strict. Ensure that you + /// add the + /// into the pipeline before sending any cookies! + /// + /// + /// Minimum ASPNET Core Version required for this code: + /// - 2.1.14 + /// - 2.2.8 + /// - 3.0.1 + /// - 3.1.0-preview1 + /// Starting with version 80 of Chrome (to be released in February 2020) + /// cookies with NO SameSite attribute are treated as SameSite=Lax. + /// In order to always get the cookies send they need to be set to + /// SameSite=None. But since the current standard only defines Lax and + /// Strict as valid values there are some browsers that treat invalid + /// values as SameSite=Strict. We therefore need to check the browser + /// and either send SameSite=None or prevent the sending of SameSite=None. + /// Relevant links: + /// - https://tools.ietf.org/html/draft-west-first-party-cookies-07#section-4.1 + /// - https://tools.ietf.org/html/draft-west-cookie-incrementalism-00 + /// - https://www.chromium.org/updates/same-site + /// - https://devblogs.microsoft.com/aspnet/upcoming-samesite-cookie-changes-in-asp-net-and-asp-net-core/ + /// - https://bugs.webkit.org/show_bug.cgi?id=198181 + /// + /// The service collection to register into. + /// The modified . + public static IServiceCollection ConfigureNonBreakingSameSiteCookies(this IServiceCollection services) + { + services.Configure(options => + { + options.MinimumSameSitePolicy = Unspecified; + options.OnAppendCookie = cookieContext => + CheckSameSite(cookieContext.Context, cookieContext.CookieOptions); + options.OnDeleteCookie = cookieContext => + CheckSameSite(cookieContext.Context, cookieContext.CookieOptions); + }); + + return services; + } + + private static void CheckSameSite(HttpContext httpContext, CookieOptions options) + { + if (options.SameSite == SameSiteMode.None) + { + var userAgent = httpContext.Request.Headers["User-Agent"].ToString(); + + // if (DisallowsSameSiteNone(userAgent)) + // { + options.SameSite = Unspecified; + // } + } + } + + /// + /// Checks if the UserAgent is known to interpret an unknown value as Strict. + /// For those the property should be + /// set to . + /// + /// + /// This code is taken from Microsoft: + /// https://devblogs.microsoft.com/aspnet/upcoming-samesite-cookie-changes-in-asp-net-and-asp-net-core/ + /// + /// The user agent string to check. + /// Whether the specified user agent (browser) accepts SameSite=None or not. + private static bool DisallowsSameSiteNone(string userAgent) + { + // Cover all iOS based browsers here. This includes: + // - Safari on iOS 12 for iPhone, iPod Touch, iPad + // - WkWebview on iOS 12 for iPhone, iPod Touch, iPad + // - Chrome on iOS 12 for iPhone, iPod Touch, iPad + // All of which are broken by SameSite=None, because they use the + // iOS networking stack. + // Notes from Thinktecture: + // Regarding https://caniuse.com/#search=samesite iOS versions lower + // than 12 are not supporting SameSite at all. Starting with version 13 + // unknown values are NOT treated as strict anymore. Therefore we only + // need to check version 12. + if (userAgent.Contains("CPU iPhone OS 12") + || userAgent.Contains("iPad; CPU OS 12")) + { + return true; + } + + // Cover Mac OS X based browsers that use the Mac OS networking stack. + // This includes: + // - Safari on Mac OS X. + // This does not include: + // - Chrome on Mac OS X + // because they do not use the Mac OS networking stack. + // Notes from Thinktecture: + // Regarding https://caniuse.com/#search=samesite MacOS X versions lower + // than 10.14 are not supporting SameSite at all. Starting with version + // 10.15 unknown values are NOT treated as strict anymore. Therefore we + // only need to check version 10.14. + if (userAgent.Contains("Safari") + && userAgent.Contains("Macintosh; Intel Mac OS X 10_14") + && userAgent.Contains("Version/")) + { + return true; + } + + // Cover Chrome 50-69, because some versions are broken by SameSite=None + // and none in this range require it. + // Note: this covers some pre-Chromium Edge versions, + // but pre-Chromium Edge does not require SameSite=None. + // Notes from Thinktecture: + // We can not validate this assumption, but we trust Microsofts + // evaluation. And overall not sending a SameSite value equals to the same + // behavior as SameSite=None for these old versions anyways. + if (userAgent.Contains("Chrome/5") || userAgent.Contains("Chrome/6")) + { + return true; + } + + return false; + } + } +} \ No newline at end of file diff --git a/OpenAuth.Identity/Startup.cs b/OpenAuth.Identity/Startup.cs new file mode 100644 index 0000000..28f2a9b --- /dev/null +++ b/OpenAuth.Identity/Startup.cs @@ -0,0 +1,81 @@ +using System; +using System.Linq; +using Autofac; +using Infrastructure; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using OpenAuth.App; +using OpenAuth.Repository; + +namespace OpenAuth.IdentityServer +{ + public class Startup + { + public IHostEnvironment Environment { get; } + public IConfiguration Configuration { get; } + public Startup(IConfiguration configuration, IHostEnvironment environment) + { + Configuration = configuration; + Environment = environment; + } + + public void ConfigureServices(IServiceCollection services) + { + services.AddControllersWithViews(); + + var builder = services.AddIdentityServer() + .AddDeveloperSigningCredential() + .AddInMemoryApiScopes(Config.ApiScopes) + .AddInMemoryClients(Config.GetClients()); + //.AddInMemoryIdentityResources(Config.GetIdentityResources()) + //.AddInMemoryApiResources(Config.GetApis()) + //.AddProfileService(); + + services.ConfigureNonBreakingSameSiteCookies(); + + services.AddCors(); + + builder.AddDeveloperSigningCredential(); + + services.AddAuthentication(); + + //映射配置文件 + services.Configure(Configuration.GetSection("AppSetting")); + } + + public void ConfigureContainer(ContainerBuilder builder) + { + AutofacExt.InitAutofac(builder); + } + + public void Configure(IApplicationBuilder app, IHostEnvironment env) + { + if (env.IsDevelopment()) + { + app.UseDeveloperExceptionPage(); + } + + + app.UseCookiePolicy(); + + //todo:测试可以允许任意跨域,正式环境要加权限 + app.UseCors(builder => builder.AllowAnyOrigin() + .AllowAnyMethod() + .AllowAnyHeader()); + + app.UseStaticFiles(); + app.UseRouting(); + + app.UseIdentityServer(); + app.UseAuthorization(); + + app.UseEndpoints(endpoints => + { + endpoints.MapDefaultControllerRoute(); + }); + } + } +} \ No newline at end of file diff --git a/OpenAuth.Identity/Views/Account/AccessDenied.cshtml b/OpenAuth.Identity/Views/Account/AccessDenied.cshtml new file mode 100644 index 0000000..ff22987 --- /dev/null +++ b/OpenAuth.Identity/Views/Account/AccessDenied.cshtml @@ -0,0 +1,8 @@ + +
+ + +

You do not have access to that resource.

+
\ No newline at end of file diff --git a/OpenAuth.Identity/Views/Account/LoggedOut.cshtml b/OpenAuth.Identity/Views/Account/LoggedOut.cshtml new file mode 100644 index 0000000..14db8f9 --- /dev/null +++ b/OpenAuth.Identity/Views/Account/LoggedOut.cshtml @@ -0,0 +1,34 @@ +@model OpenAuth.IdentityServer.Quickstart.Account.LoggedOutViewModel + +@{ + // set this so the layout rendering sees an anonymous user + ViewData["signed-out"] = true; +} + + + +@section scripts +{ + @if (Model.AutomaticRedirectAfterSignOut) + { + + } +} diff --git a/OpenAuth.Identity/Views/Account/Login.cshtml b/OpenAuth.Identity/Views/Account/Login.cshtml new file mode 100644 index 0000000..d906d90 --- /dev/null +++ b/OpenAuth.Identity/Views/Account/Login.cshtml @@ -0,0 +1,92 @@ +@model OpenAuth.IdentityServer.Quickstart.Account.LoginViewModel + + \ No newline at end of file diff --git a/OpenAuth.Identity/Views/Account/Logout.cshtml b/OpenAuth.Identity/Views/Account/Logout.cshtml new file mode 100644 index 0000000..648fecc --- /dev/null +++ b/OpenAuth.Identity/Views/Account/Logout.cshtml @@ -0,0 +1,21 @@ +@model OpenAuth.IdentityServer.Quickstart.Account.LogoutViewModel + +
+ + +
+
+

Would you like to logout of IdentityServer?

+
+ +
+
+ +
+
+
+
+
+
\ No newline at end of file diff --git a/OpenAuth.Identity/Views/Consent/Index.cshtml b/OpenAuth.Identity/Views/Consent/Index.cshtml new file mode 100644 index 0000000..e990320 --- /dev/null +++ b/OpenAuth.Identity/Views/Consent/Index.cshtml @@ -0,0 +1,82 @@ +@model OpenAuth.IdentityServer.Quickstart.Consent.ConsentViewModel + + \ No newline at end of file diff --git a/OpenAuth.Identity/Views/Device/Success.cshtml b/OpenAuth.Identity/Views/Device/Success.cshtml new file mode 100644 index 0000000..f41cdef --- /dev/null +++ b/OpenAuth.Identity/Views/Device/Success.cshtml @@ -0,0 +1,6 @@ + \ No newline at end of file diff --git a/OpenAuth.Identity/Views/Device/UserCodeCapture.cshtml b/OpenAuth.Identity/Views/Device/UserCodeCapture.cshtml new file mode 100644 index 0000000..cbe38dd --- /dev/null +++ b/OpenAuth.Identity/Views/Device/UserCodeCapture.cshtml @@ -0,0 +1,14 @@ +@model string + + \ No newline at end of file diff --git a/OpenAuth.Identity/Views/Device/UserCodeConfirmation.cshtml b/OpenAuth.Identity/Views/Device/UserCodeConfirmation.cshtml new file mode 100644 index 0000000..9861210 --- /dev/null +++ b/OpenAuth.Identity/Views/Device/UserCodeConfirmation.cshtml @@ -0,0 +1,93 @@ +@model OpenAuth.IdentityServer.Quickstart.Device.DeviceAuthorizationViewModel + + \ No newline at end of file diff --git a/OpenAuth.Identity/Views/Diagnostics/Index.cshtml b/OpenAuth.Identity/Views/Diagnostics/Index.cshtml new file mode 100644 index 0000000..c14c6d4 --- /dev/null +++ b/OpenAuth.Identity/Views/Diagnostics/Index.cshtml @@ -0,0 +1,32 @@ +@model OpenAuth.IdentityServer.Quickstart.Diagnostics.DiagnosticsViewModel + +

Authentication cookie

+ +

Claims

+
+ @foreach (var claim in Model.AuthenticateResult.Principal.Claims) + { +
@claim.Type
+
@claim.Value
+ } +
+ +

Properties

+
+ @foreach (var prop in Model.AuthenticateResult.Properties.Items) + { +
@prop.Key
+
@prop.Value
+ } +
+ +@if (Model.Clients.Any()) +{ +

Clients

+
    + @foreach (var client in Model.Clients) + { +
  • @client
  • + } +
+} \ No newline at end of file diff --git a/OpenAuth.Identity/Views/Grants/Index.cshtml b/OpenAuth.Identity/Views/Grants/Index.cshtml new file mode 100644 index 0000000..d34f007 --- /dev/null +++ b/OpenAuth.Identity/Views/Grants/Index.cshtml @@ -0,0 +1,79 @@ +@model OpenAuth.IdentityServer.Quickstart.Grants.GrantsViewModel + +
+ + + @if (Model.Grants.Any() == false) + { +
+
+
+ You have not given access to any applications +
+
+
+ } + else + { + foreach (var grant in Model.Grants) + { +
+
+ @if (grant.ClientLogoUrl != null) + { + + } +
+
+
@grant.ClientName
+
+ Created: @grant.Created.ToString("yyyy-MM-dd") +
+ @if (grant.Expires.HasValue) + { +
+ Expires: @grant.Expires.Value.ToString("yyyy-MM-dd") +
+ } + @if (grant.IdentityGrantNames.Any()) + { +
+
Identity Grants
+
    + @foreach (var name in grant.IdentityGrantNames) + { +
  • @name
  • + } +
+
+ } + @if (grant.ApiGrantNames.Any()) + { +
+
API Grants
+
    + @foreach (var name in grant.ApiGrantNames) + { +
  • @name
  • + } +
+
+ } +
+
+
+ + +
+
+
+ } + } +
\ No newline at end of file diff --git a/OpenAuth.Identity/Views/Home/Index.cshtml b/OpenAuth.Identity/Views/Home/Index.cshtml new file mode 100644 index 0000000..5720569 --- /dev/null +++ b/OpenAuth.Identity/Views/Home/Index.cshtml @@ -0,0 +1,39 @@ +@{ + var version = typeof(IdentityServer4.Hosting.IdentityServerMiddleware).Assembly.GetName().Version.ToString(); +} + +
+ + +
+
+

+ IdentityServer publishes a + discovery document + where you can find metadata and links to all the endpoints, key material, etc. +

+
+
+

+ Click here to manage your stored grants. +

+
+
+
+
+

+ Here are links to the + source code repository, + and ready to use samples. +

+
+
+
diff --git a/OpenAuth.Identity/Views/Shared/Error.cshtml b/OpenAuth.Identity/Views/Shared/Error.cshtml new file mode 100644 index 0000000..407f057 --- /dev/null +++ b/OpenAuth.Identity/Views/Shared/Error.cshtml @@ -0,0 +1,40 @@ +@model OpenAuth.IdentityServer.Quickstart.Home.ErrorViewModel + +@{ + var error = Model?.Error?.Error; + var errorDescription = Model?.Error?.ErrorDescription; + var request_id = Model?.Error?.RequestId; +} + +
+ + +
+
+
+ Sorry, there was an error + + @if (error != null) + { + + + : @error + + + + if (errorDescription != null) + { +
@errorDescription
+ } + } +
+ + @if (request_id != null) + { +
Request Id: @request_id
+ } +
+
+
diff --git a/OpenAuth.Identity/Views/Shared/Redirect.cshtml b/OpenAuth.Identity/Views/Shared/Redirect.cshtml new file mode 100644 index 0000000..73a7040 --- /dev/null +++ b/OpenAuth.Identity/Views/Shared/Redirect.cshtml @@ -0,0 +1,7 @@ +@model OpenAuth.IdentityServer.Quickstart.Account.RedirectViewModel + +

You are now being returned to the application.

+

Once complete, you may close this tab

+ + + diff --git a/OpenAuth.Identity/Views/Shared/_Layout.cshtml b/OpenAuth.Identity/Views/Shared/_Layout.cshtml new file mode 100644 index 0000000..e825983 --- /dev/null +++ b/OpenAuth.Identity/Views/Shared/_Layout.cshtml @@ -0,0 +1,72 @@ +@using IdentityServer4.Extensions +@{ + string name = null; + if (!true.Equals(ViewData["signed-out"])) + { + name = Context.User?.GetDisplayName(); + } +} + + + + + + + OpenAuth.Net认证中心- 最好用的.net权限工作流框架|.net core快速开发框架|.net core权限管理|.net core工作流 + + + + + + + + + + +
+ @RenderBody() +
+ + + + + @RenderSection("scripts", required: false) + + diff --git a/OpenAuth.Identity/Views/Shared/_ScopeListItem.cshtml b/OpenAuth.Identity/Views/Shared/_ScopeListItem.cshtml new file mode 100644 index 0000000..1a3a6fd --- /dev/null +++ b/OpenAuth.Identity/Views/Shared/_ScopeListItem.cshtml @@ -0,0 +1,34 @@ +@model OpenAuth.IdentityServer.Quickstart.Consent.ScopeViewModel + +
  • + + @if (Model.Required) + { + (required) + } + @if (Model.Description != null) + { + + } +
  • \ No newline at end of file diff --git a/OpenAuth.Identity/Views/Shared/_ValidationSummary.cshtml b/OpenAuth.Identity/Views/Shared/_ValidationSummary.cshtml new file mode 100644 index 0000000..674d68d --- /dev/null +++ b/OpenAuth.Identity/Views/Shared/_ValidationSummary.cshtml @@ -0,0 +1,7 @@ +@if (ViewContext.ModelState.IsValid == false) +{ +
    + Error +
    +
    +} \ No newline at end of file diff --git a/OpenAuth.Identity/Views/_ViewImports.cshtml b/OpenAuth.Identity/Views/_ViewImports.cshtml new file mode 100644 index 0000000..afa82bb --- /dev/null +++ b/OpenAuth.Identity/Views/_ViewImports.cshtml @@ -0,0 +1 @@ +@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers diff --git a/OpenAuth.Identity/Views/_ViewStart.cshtml b/OpenAuth.Identity/Views/_ViewStart.cshtml new file mode 100644 index 0000000..a5f1004 --- /dev/null +++ b/OpenAuth.Identity/Views/_ViewStart.cshtml @@ -0,0 +1,3 @@ +@{ + Layout = "_Layout"; +} diff --git a/OpenAuth.Identity/appsettings.Development.json b/OpenAuth.Identity/appsettings.Development.json new file mode 100644 index 0000000..e203e94 --- /dev/null +++ b/OpenAuth.Identity/appsettings.Development.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Debug", + "System": "Information", + "Microsoft": "Information" + } + } +} diff --git a/OpenAuth.Identity/appsettings.Production.json b/OpenAuth.Identity/appsettings.Production.json new file mode 100644 index 0000000..b28e46b --- /dev/null +++ b/OpenAuth.Identity/appsettings.Production.json @@ -0,0 +1,16 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Warning" + } + }, + "AllowedHosts": "*", + "ConnectionStrings": { + "OpenAuthDBContext": "server=127.0.0.1;user id=root;database=openauthdb;password=000000" //my sql + }, + "AppSetting": { + "DbTypes": { + "OpenAuthDBContext":"MySql" //数据库类型:SqlServer、MySql、Oracle + } + } +} diff --git a/OpenAuth.Identity/appsettings.json b/OpenAuth.Identity/appsettings.json new file mode 100644 index 0000000..681c672 --- /dev/null +++ b/OpenAuth.Identity/appsettings.json @@ -0,0 +1,11 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Warning" + } + }, + "AllowedHosts": "*", + "ConnectionStrings": { + "OpenAuthDBContext": "" + } +} diff --git a/OpenAuth.Identity/tempkey.jwk b/OpenAuth.Identity/tempkey.jwk new file mode 100644 index 0000000..326af7c --- /dev/null +++ b/OpenAuth.Identity/tempkey.jwk @@ -0,0 +1 @@ +{"AdditionalData":{},"Alg":"RS256","Crv":null,"D":"FZBC8mpgLd9n9VG6MQfc1XLbCOgWRzGEuVdXr1lDrjO9Yb8mj09FhssAJ-J6K0Pm-UgTQUmtNzvmZSCz_2rToPDaj64GbhXBSA7SxDjwAxDZuE-7T3v9delijy3eksEmSZLN51Ymmt3Ta0QKF-lINBMD6HyGSQsceYeKED66N2DQ_5IzwReAH9J529pzVfvHceQFTwdfTGs_lxRoJZiitaeKS3C0Fekondwq8jvJdpRCdTqZq9azXSLhSnynB3AQ94pnm8nHmZA1WitwPcL2uh2655tmMYd8QT7nWJOUVkpwcI2E32peko_RwhkHQDMy4fFluZW0s2AFPWYMN-LhHQ","DP":"V-7TrHWriNm9mdFc0ziyeCFhdB_dFEJPNAlJis5XJbshufjH3DfnOmhSP_vyo2k_ynaJp0pKNoIX0bvaVTBN2yHyrbyJA9Wl_KNIPYvUoWDS8eBPJ-ROS7rE4La2fnkJRMiD5SOwNFgcXpURdORiyCF6xAAefhCvlr8ww9WORkE","DQ":"V7B-gNOY4YG6LjLf32De5GyUUtRTzBgkwnw7StLtGDyMhfXrs3kzqSS7DumpJIeLWiuU0Yw8vV5a9dUTYbjrrFon-mEReK4ZAKjPy3DRQIQXsvSrIXlPRn6_hxzNpjR6iw4lsrhfE5L1Z6IrVdWyHSBvk0DdgfH7k1dGFWfl0Ak","E":"AQAB","K":null,"KeyId":"58F23E7865126BE9B4F964EC9719EC4D","Kid":"58F23E7865126BE9B4F964EC9719EC4D","Kty":"RSA","N":"okUXzORMUQAGQMbuHuzNGONpufShLixiQ1D0SrDerg823XoG82J30L-fq2uOtF57bztOJGsSKdUSlBCcnpSInt02EqdRogAoYhB8QOUrW4gXjNCTL78BnSP2ulE9e6f2rHZjH6juPvn7gVnXX_S3Oo-fc9uhv1BDZFM7_RVxQvf98759T3GM-Qj0NZR467YsdqqGrdq98hnXTD8EFHAjMEZKrOmkkGT4lINLmylf91cENNzJ-gf9QjtO3j3t2TRbtnktPpc2Wbw9f8sZoAefJG026sENZNttJL72h149U12d4GG8WRKtPZWPHKhHyc9Gq2XWdEKE8ohE12EHZlsIVQ","Oth":null,"P":"xa7j24z_JNvObtlrYtLOsKYFPz0hVfvQtJ-XpAnaPWuxOLc8Ch1BWucZ7QQZARaCR9qFdesT9W4ALJKDGapRl-JVkJKbHuY7vBO2qef-Jh_uvdnKrh2zbNSW3NXFdHl22RiGKWADBWmGhaIApkTGOM0pozjJL-esCxbT6HNfga8","Q":"0iPHZecnU2kjTvC-FVmCw6LsQweWkETBAes8T_ZWv5NPJCfa8bSBk2sD0N2XER43NR9BdiTA2SAXjbkrKTDDpz0ve69iFJb8uxV1kwuwVUVibfl3igUsl9oDkxSZ2GembmOuYbWBLc_uk194h7RKA7F86HPIKS7Rgf4Q99scazs","QI":"n4-4jQ9GnBZvNSJ4ls0u74IShfsJkG63VaZgypTnhItPHGDIzwCI_VdASKN55RXMWSr0lfDVjNDBKgqdgFJczsxaaDm8fUiA0e7Mso6DznpmYqvtJ-cL7xAKIY9aJrb_3YqQILAtTjcZh9aYr-dDY4qgnQmmQ3GpL7tCU0t8zb8","Use":null,"X":null,"X5t":null,"X5tS256":null,"X5u":null,"Y":null,"KeySize":2048,"HasPrivateKey":true,"CryptoProviderFactory":{"CryptoProviderCache":{},"CustomCryptoProvider":null,"CacheSignatureProviders":true,"SignatureProviderObjectPoolCacheSize":48}} \ No newline at end of file diff --git a/OpenAuth.Identity/tempkey.rsa b/OpenAuth.Identity/tempkey.rsa new file mode 100644 index 0000000..7b7bf59 --- /dev/null +++ b/OpenAuth.Identity/tempkey.rsa @@ -0,0 +1 @@ +{"KeyId":"a33ecf9fadd0a1cd0aa7fbe05ab80eed","Parameters":{"D":"KqUVQKufCYXPJPp6/wCKYMX0044mHb/N+ZKjCQh7F5GQOvGpSEINAiPBuCEroyORDdT4jMwfTJYb4OPH9hOddBxLBZuKCNVZVlv4kxauiKMnUkEneIwOJL5thw5seAR1qnGxKZMby/1NlpqriY7ahYC5zHdlRm387xOz1/AtrjKilh5wYnSZOayq3lvjY8MRjf9v/89jnFNqM3i34ruUk5wOZgXwe6cNAaOG4xYJJ79+2x0hhqzwaDrpTfPQmmogDKf511OrcO1A6QokLN/qjdAYMFt0HLI6DN9GDePYjB8zUoXigtjO8Th4zHyUW2O2rHpsqExBALw1OGPaIeD97Q==","DP":"N6eDU4ySwI7VEFPUTwuWnRPxWoJWkzuTy19alD/2+c7wlcaYiIi26qZLdwL3j95xH645WxlKOMWFkU8HedQSUtCpNCEcKu7ejS/Gir/65V0fs3SxF1JT56RGF8E4Ma+vsIRRzKBsVtIuNca3mgDpdI1gxmuCradee2Z4fhKe3bU=","DQ":"26nTIdNfj7Ps68BSE74LynW4JdsUQorHMTGXgJfTSVth/hBkBbjF98Tjta2iG1EMRAJHveJ29lCQzxVYhxsrd1obyuD3mTX+n+k0ciHYAdGzPQKIel/iP8OYrsDvTgioAuS0Jy7WowT9TK7zC/yb/MbaqvQls1KcjF2jjSstpes=","Exponent":"AQAB","InverseQ":"ge27mwfvTlhFmMs1lMRG+WpFPrNHQtZFNPFoajJ9qL12VRaT0vodqW4M9MRp6Vs+Xvy+3pNzWkJRVZQq0HTN0oXIYgpSdG8stbt3g8orbTLsS8NFKWHnDsiKK1O1Dd9AsKoQmQbfPhRxECdWVQsCgTsedU6f24sJBIoBluF25BE=","Modulus":"+BjnkJNAxS78/tUd6trgJXV1Ltvh0Yw7NTa2qHlbD6wvR7IaXAWqJffzucyXFaOafs63tKiIXh1Tfhg+lQXIxXoJohyrz7exLqOkn4OvLGu3fjRDCF/2M2riDyJIGDNlt09e0NnrwQfUo1rhqr5k3N9T85LeETWA5i0EVRV3eanEbEOlNHtVPDEFm3LEKFm/FWxkchKnrjFz0t/2trsjo3gLDkslXKFkC/F9wPPldh5wCabVmr5pcKZpCPLnSzrRS35gF/g0+Fd9pZoflC+c798OsKFcsDDTKmtoRwU4FiX8I/z5H1pF2VHMEHpvU4akRD92TfmkKr+VdK+yiIZjxQ==","P":"/MQyAzEKfLXoWrT3tnrwFJ/sYv0A2Azl5wF9R1PmfBPphHA74KgIAe//88Q5PyhKDWaGW2BtUewr+fqMvEHtdXYwjYqz9q/NZ3hiZl+wDFkT6lz4ufwDKEZa+bUiAen0b8Ruk9sdwXSBJwLBq+rqEBaE1vPrlU68dOS2dMow5kc=","Q":"+0Vq//ZIhAadpc8riyD10/n9CkTiGecVc7WGsk6vfeDac2PaQvcDUDs/LeP7qT2Q9+2kra6jA7hOrPCxyP6PLukNOdqcmrdU+z2UJGZVLCZJ7RJJSt4H1fP26cJ/x1egkubVwImKiapYSpXWYLx9K8wTixZQHRAdzsK3qr1sD5M="}} \ No newline at end of file diff --git a/OpenAuth.Identity/wwwroot/css/site.css b/OpenAuth.Identity/wwwroot/css/site.css new file mode 100644 index 0000000..a0bbbed --- /dev/null +++ b/OpenAuth.Identity/wwwroot/css/site.css @@ -0,0 +1,82 @@ +body { + margin-top: 65px; +} +.navbar-header { + position: relative; + top: -4px; +} +.navbar-brand > .icon-banner { + position: relative; + top: -2px; + display: inline; +} +.icon { + position: relative; + top: -10px; +} +.logged-out iframe { + display: none; + width: 0; + height: 0; +} +.page-consent .client-logo { + float: left; +} +.page-consent .client-logo img { + width: 80px; + height: 80px; +} +.page-consent .consent-buttons { + margin-top: 25px; +} +.page-consent .consent-form .consent-scopecheck { + display: inline-block; + margin-right: 5px; +} +.page-consent .consent-form .consent-description { + margin-left: 25px; +} +.page-consent .consent-form .consent-description label { + font-weight: normal; +} +.page-consent .consent-form .consent-remember { + padding-left: 16px; +} +.grants .page-header { + margin-bottom: 10px; +} +.grants .grant { + margin-top: 20px; + padding-bottom: 20px; + border-bottom: 1px solid lightgray; +} +.grants .grant img { + width: 100px; + height: 100px; +} +.grants .grant .clientname { + font-size: 140%; + font-weight: bold; +} +.grants .grant .granttype { + font-size: 120%; + font-weight: bold; +} +.grants .grant .created { + font-size: 120%; + font-weight: bold; +} +.grants .grant .expires { + font-size: 120%; + font-weight: bold; +} +.grants .grant li { + list-style-type: none; + display: inline; +} +.grants .grant li:after { + content: ', '; +} +.grants .grant li:last-child:after { + content: ''; +} \ No newline at end of file diff --git a/OpenAuth.Identity/wwwroot/css/site.less b/OpenAuth.Identity/wwwroot/css/site.less new file mode 100644 index 0000000..09f5141 --- /dev/null +++ b/OpenAuth.Identity/wwwroot/css/site.less @@ -0,0 +1,116 @@ +body { + margin-top: 65px; +} + +.navbar-header { + position: relative; + top: -4px; +} + +.navbar-brand > .icon-banner { + position: relative; + top: -2px; + display: inline; +} + +.icon { + position: relative; + top: -10px; +} + +.logged-out iframe { + display: none; + width: 0; + height: 0; +} + +.page-consent { + .client-logo { + float: left; + + img { + width: 80px; + height: 80px; + } + } + + .consent-buttons { + margin-top: 25px; + } + + .consent-form { + .consent-scopecheck { + display: inline-block; + margin-right: 5px; + } + + .consent-scopecheck[disabled] { + //visibility:hidden; + } + + .consent-description { + margin-left: 25px; + + label { + font-weight: normal; + } + } + + .consent-remember { + padding-left: 16px; + } + } +} + +.grants { + .page-header { + margin-bottom: 10px; + } + + .grant { + margin-top: 20px; + padding-bottom: 20px; + border-bottom: 1px solid lightgray; + + img { + width: 100px; + height: 100px; + } + + .clientname { + font-size: 140%; + font-weight: bold; + } + + .granttype { + font-size: 120%; + font-weight: bold; + } + + .created { + font-size: 120%; + font-weight: bold; + } + + .expires { + font-size: 120%; + font-weight: bold; + } + + li { + list-style-type: none; + display: inline; + + &:after { + content: ', '; + } + + &:last-child:after { + content: ''; + } + + .displayname { + } + } + } +} diff --git a/OpenAuth.Identity/wwwroot/css/site.min.css b/OpenAuth.Identity/wwwroot/css/site.min.css new file mode 100644 index 0000000..84ae0c4 --- /dev/null +++ b/OpenAuth.Identity/wwwroot/css/site.min.css @@ -0,0 +1 @@ +body{margin-top:65px;}.navbar-header{position:relative;top:-4px;}.navbar-brand>.icon-banner{position:relative;top:-2px;display:inline;}.icon{position:relative;top:-10px;}.logged-out iframe{display:none;width:0;height:0;}.page-consent .client-logo{float:left;}.page-consent .client-logo img{width:80px;height:80px;}.page-consent .consent-buttons{margin-top:25px;}.page-consent .consent-form .consent-scopecheck{display:inline-block;margin-right:5px;}.page-consent .consent-form .consent-description{margin-left:25px;}.page-consent .consent-form .consent-description label{font-weight:normal;}.page-consent .consent-form .consent-remember{padding-left:16px;}.grants .page-header{margin-bottom:10px;}.grants .grant{margin-top:20px;padding-bottom:20px;border-bottom:1px solid #d3d3d3;}.grants .grant img{width:100px;height:100px;}.grants .grant .clientname{font-size:140%;font-weight:bold;}.grants .grant .granttype{font-size:120%;font-weight:bold;}.grants .grant .created{font-size:120%;font-weight:bold;}.grants .grant .expires{font-size:120%;font-weight:bold;}.grants .grant li{list-style-type:none;display:inline;}.grants .grant li:after{content:', ';}.grants .grant li:last-child:after{content:'';} \ No newline at end of file diff --git a/OpenAuth.Identity/wwwroot/favicon.ico b/OpenAuth.Identity/wwwroot/favicon.ico new file mode 100644 index 0000000..ee470e4 Binary files /dev/null and b/OpenAuth.Identity/wwwroot/favicon.ico differ diff --git a/OpenAuth.Identity/wwwroot/icon.jpg b/OpenAuth.Identity/wwwroot/icon.jpg new file mode 100644 index 0000000..e652502 Binary files /dev/null and b/OpenAuth.Identity/wwwroot/icon.jpg differ diff --git a/OpenAuth.Identity/wwwroot/icon.png b/OpenAuth.Identity/wwwroot/icon.png new file mode 100644 index 0000000..cd386d5 Binary files /dev/null and b/OpenAuth.Identity/wwwroot/icon.png differ diff --git a/OpenAuth.Identity/wwwroot/js/signin-redirect.js b/OpenAuth.Identity/wwwroot/js/signin-redirect.js new file mode 100644 index 0000000..6ebc569 --- /dev/null +++ b/OpenAuth.Identity/wwwroot/js/signin-redirect.js @@ -0,0 +1 @@ +window.location.href = document.querySelector("meta[http-equiv=refresh]").getAttribute("data-url"); diff --git a/OpenAuth.Identity/wwwroot/js/signout-redirect.js b/OpenAuth.Identity/wwwroot/js/signout-redirect.js new file mode 100644 index 0000000..cdfc5e7 --- /dev/null +++ b/OpenAuth.Identity/wwwroot/js/signout-redirect.js @@ -0,0 +1,6 @@ +window.addEventListener("load", function () { + var a = document.querySelector("a.PostLogoutRedirectUri"); + if (a) { + window.location = a.href; + } +}); diff --git a/OpenAuth.Identity/wwwroot/lib/bootstrap/css/bootstrap-ie6.css b/OpenAuth.Identity/wwwroot/lib/bootstrap/css/bootstrap-ie6.css new file mode 100644 index 0000000..4fe12e3 --- /dev/null +++ b/OpenAuth.Identity/wwwroot/lib/bootstrap/css/bootstrap-ie6.css @@ -0,0 +1,5773 @@ +/*! + * Bootstrap v2.2.1 + * + * Copyright 2012 Twitter, Inc + * Licensed under the Apache License v2.0 + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Designed and built with all the love in the world @twitter by @mdo and @fat. + */ + +article, +aside, +details, +figcaption, +figure, +footer, +header, +hgroup, +nav, +section { + display: block; +} + +audio, +canvas, +video { + display: inline-block; + *display: inline; + *zoom: 1; +} + +audio:not([controls]) { + display: none; +} + +html { + font-size: 100%; + -webkit-text-size-adjust: 100%; + -ms-text-size-adjust: 100%; +} + +a:focus { + outline: thin dotted #333; + outline: 5px auto -webkit-focus-ring-color; + outline-offset: -2px; +} + +a:hover, +a:active { + outline: 0; +} + +sub, +sup { + position: relative; + font-size: 75%; + line-height: 0; + vertical-align: baseline; +} + +sup { + top: -0.5em; +} + +sub { + bottom: -0.25em; +} + +img { + width: auto\9; + height: auto; + max-width: 100%; + vertical-align: middle; + border: 0; + -ms-interpolation-mode: bicubic; +} + +#map_canvas img, +.google-maps img { + max-width: none; +} + +button, +input, +select, +textarea { + margin: 0; + font-size: 100%; + vertical-align: middle; +} + +button, +input { + *overflow: visible; + line-height: normal; +} + +button::-moz-focus-inner, +input::-moz-focus-inner { + padding: 0; + border: 0; +} + +button, +html input[type="button"], +input[type="reset"], +input[type="submit"] { + cursor: pointer; + -webkit-appearance: button; +} + +input[type="search"] { + -webkit-box-sizing: content-box; + -moz-box-sizing: content-box; + box-sizing: content-box; + -webkit-appearance: textfield; +} + +input[type="search"]::-webkit-search-decoration, +input[type="search"]::-webkit-search-cancel-button { + -webkit-appearance: none; +} + +textarea { + overflow: auto; + vertical-align: top; +} + +.clearfix { + *zoom: 1; +} + +.clearfix:before, +.clearfix:after { + display: table; + line-height: 0; + content: ""; +} + +.clearfix:after { + clear: both; +} + +.hide-text { + font: 0/0 a; + color: transparent; + text-shadow: none; + background-color: transparent; + border: 0; +} + +.input-block-level { + display: block; + width: 100%; + min-height: 30px; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; +} + +body { + margin: 0; + font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; + font-size: 14px; + line-height: 20px; + color: #333333; + background-color: #ffffff; +} + +a { + color: #0088cc; + text-decoration: none; +} + +a:hover { + color: #005580; + text-decoration: underline; +} + +.img-rounded { + -webkit-border-radius: 6px; + -moz-border-radius: 6px; + border-radius: 6px; +} + +.img-polaroid { + padding: 4px; + background-color: #fff; + border: 1px solid #ccc; + border: 1px solid rgba(0, 0, 0, 0.2); + -webkit-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1); + -moz-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1); + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1); +} + +.img-circle { + -webkit-border-radius: 500px; + -moz-border-radius: 500px; + border-radius: 500px; +} + +.row { + margin-left: -20px; + *zoom: 1; +} + +.row:before, +.row:after { + display: table; + line-height: 0; + content: ""; +} + +.row:after { + clear: both; +} + +[class*="span"] { + float: left; + min-height: 1px; + margin-left: 20px; +} + +.container, +.navbar-static-top .container, +.navbar-fixed-top .container, +.navbar-fixed-bottom .container { + width: 940px; +} + +.span12 { + display: inline; + float: left; + width: 940px; + min-height: 1px; + margin-left: 20px; +} + +.span11 { + display: inline; + float: left; + width: 860px; + min-height: 1px; + margin-left: 20px; +} + +.span10 { + display: inline; + float: left; + width: 780px; + min-height: 1px; + margin-left: 20px; +} + +.span9 { + display: inline; + float: left; + width: 700px; + min-height: 1px; + margin-left: 20px; +} + +.span8 { + display: inline; + float: left; + width: 620px; + min-height: 1px; + margin-left: 20px; +} + +.span7 { + display: inline; + float: left; + width: 540px; + min-height: 1px; + margin-left: 20px; +} + +.span6 { + display: inline; + float: left; + width: 460px; + min-height: 1px; + margin-left: 20px; +} + +.span5 { + display: inline; + float: left; + width: 380px; + min-height: 1px; + margin-left: 20px; +} + +.span4 { + display: inline; + float: left; + width: 300px; + min-height: 1px; + margin-left: 20px; +} + +.span3 { + display: inline; + float: left; + width: 220px; + min-height: 1px; + margin-left: 20px; +} + +.span2 { + display: inline; + float: left; + width: 140px; + min-height: 1px; + margin-left: 20px; +} + +.span1 { + display: inline; + float: left; + width: 60px; + min-height: 1px; + margin-left: 20px; +} + +.offset12 { + margin-left: 980px; +} + +.offset11 { + margin-left: 900px; +} + +.offset10 { + margin-left: 820px; +} + +.offset9 { + margin-left: 740px; +} + +.offset8 { + margin-left: 660px; +} + +.offset7 { + margin-left: 580px; +} + +.offset6 { + margin-left: 500px; +} + +.offset5 { + margin-left: 420px; +} + +.offset4 { + margin-left: 340px; +} + +.offset3 { + margin-left: 260px; +} + +.offset2 { + margin-left: 180px; +} + +.offset1 { + margin-left: 100px; +} + +.span-first-child { + margin-left: 10px; +} + +.row-fluid { + width: 100%; + *zoom: 1; +} + +.row-fluid:before, +.row-fluid:after { + display: table; + line-height: 0; + content: ""; +} + +.row-fluid:after { + clear: both; +} + +.row-fluid [class*="span"] { + display: block; + float: left; + width: 100%; + min-height: 30px; + margin-left: 2.127659574468085%; + *margin-left: 2.074468085106383%; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; +} + +.row-fluid [class*="span"]:first-child { + margin-left: 0; +} + +.row-fluid .controls-row [class*="span"] + [class*="span"] { + margin-left: 2.127659574468085%; +} + +.row-fluid .span12 { + display: block; + float: left; + width: 100%; + *width: 99.94680851063829%; + min-height: 30px; + margin-left: 2.127659574468085%; + *margin-left: 2.074468085106383%; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; +} + +.row-fluid .span11 { + display: block; + float: left; + width: 100%; + width: 91.48936170212765%; + *width: 91.43617021276594%; + min-height: 30px; + margin-left: 2.127659574468085%; + *margin-left: 2.074468085106383%; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; +} + +.row-fluid .span10 { + display: block; + float: left; + width: 100%; + width: 82.97872340425532%; + *width: 82.92553191489361%; + min-height: 30px; + margin-left: 2.127659574468085%; + *margin-left: 2.074468085106383%; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; +} + +.row-fluid .span9 { + display: block; + float: left; + width: 100%; + width: 74.46808510638297%; + *width: 74.41489361702126%; + min-height: 30px; + margin-left: 2.127659574468085%; + *margin-left: 2.074468085106383%; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; +} + +.row-fluid .span8 { + display: block; + float: left; + width: 100%; + width: 65.95744680851064%; + *width: 65.90425531914893%; + min-height: 30px; + margin-left: 2.127659574468085%; + *margin-left: 2.074468085106383%; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; +} + +.row-fluid .span7 { + display: block; + float: left; + width: 100%; + width: 57.44680851063829%; + *width: 57.39361702127659%; + min-height: 30px; + margin-left: 2.127659574468085%; + *margin-left: 2.074468085106383%; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; +} + +.row-fluid .span6 { + display: block; + float: left; + width: 100%; + width: 48.93617021276595%; + *width: 48.88297872340425%; + min-height: 30px; + margin-left: 2.127659574468085%; + *margin-left: 2.074468085106383%; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; +} + +.row-fluid .span5 { + display: block; + float: left; + width: 100%; + width: 40.42553191489362%; + *width: 40.37234042553192%; + min-height: 30px; + margin-left: 2.127659574468085%; + *margin-left: 2.074468085106383%; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; +} + +.row-fluid .span4 { + display: block; + float: left; + width: 100%; + width: 31.914893617021278%; + *width: 31.861702127659576%; + min-height: 30px; + margin-left: 2.127659574468085%; + *margin-left: 2.074468085106383%; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; +} + +.row-fluid .span3 { + display: block; + float: left; + width: 100%; + width: 23.404255319148934%; + *width: 23.351063829787233%; + min-height: 30px; + margin-left: 2.127659574468085%; + *margin-left: 2.074468085106383%; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; +} + +.row-fluid .span2 { + display: block; + float: left; + width: 100%; + width: 14.893617021276595%; + *width: 14.840425531914894%; + min-height: 30px; + margin-left: 2.127659574468085%; + *margin-left: 2.074468085106383%; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; +} + +.row-fluid .span1 { + display: block; + float: left; + width: 100%; + width: 6.382978723404255%; + *width: 6.329787234042553%; + min-height: 30px; + margin-left: 2.127659574468085%; + *margin-left: 2.074468085106383%; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; +} + +.row-fluid .offset12 { + margin-left: 104.25531914893617%; + *margin-left: 104.14893617021275%; +} + +.row-fluid .offset12:first-child { + margin-left: 102.12765957446808%; + *margin-left: 102.02127659574467%; +} + +.row-fluid .offset11 { + margin-left: 95.74468085106382%; + *margin-left: 95.6382978723404%; +} + +.row-fluid .offset11:first-child { + margin-left: 93.61702127659574%; + *margin-left: 93.51063829787232%; +} + +.row-fluid .offset10 { + margin-left: 87.23404255319149%; + *margin-left: 87.12765957446807%; +} + +.row-fluid .offset10:first-child { + margin-left: 85.1063829787234%; + *margin-left: 84.99999999999999%; +} + +.row-fluid .offset9 { + margin-left: 78.72340425531914%; + *margin-left: 78.61702127659572%; +} + +.row-fluid .offset9:first-child { + margin-left: 76.59574468085106%; + *margin-left: 76.48936170212764%; +} + +.row-fluid .offset8 { + margin-left: 70.2127659574468%; + *margin-left: 70.10638297872339%; +} + +.row-fluid .offset8:first-child { + margin-left: 68.08510638297872%; + *margin-left: 67.9787234042553%; +} + +.row-fluid .offset7 { + margin-left: 61.70212765957446%; + *margin-left: 61.59574468085106%; +} + +.row-fluid .offset7:first-child { + margin-left: 59.574468085106375%; + *margin-left: 59.46808510638297%; +} + +.row-fluid .offset6 { + margin-left: 53.191489361702125%; + *margin-left: 53.085106382978715%; +} + +.row-fluid .offset6:first-child { + margin-left: 51.063829787234035%; + *margin-left: 50.95744680851063%; +} + +.row-fluid .offset5 { + margin-left: 44.68085106382979%; + *margin-left: 44.57446808510638%; +} + +.row-fluid .offset5:first-child { + margin-left: 42.5531914893617%; + *margin-left: 42.4468085106383%; +} + +.row-fluid .offset4 { + margin-left: 36.170212765957444%; + *margin-left: 36.06382978723405%; +} + +.row-fluid .offset4:first-child { + margin-left: 34.04255319148936%; + *margin-left: 33.93617021276596%; +} + +.row-fluid .offset3 { + margin-left: 27.659574468085104%; + *margin-left: 27.5531914893617%; +} + +.row-fluid .offset3:first-child { + margin-left: 25.53191489361702%; + *margin-left: 25.425531914893618%; +} + +.row-fluid .offset2 { + margin-left: 19.148936170212764%; + *margin-left: 19.04255319148936%; +} + +.row-fluid .offset2:first-child { + margin-left: 17.02127659574468%; + *margin-left: 16.914893617021278%; +} + +.row-fluid .offset1 { + margin-left: 10.638297872340425%; + *margin-left: 10.53191489361702%; +} + +.row-fluid .offset1:first-child { + margin-left: 8.51063829787234%; + *margin-left: 8.404255319148938%; +} + +.row-fluid .span-first-child { + margin-left: 0; +} + +[class*="span"].hide, +.row-fluid [class*="span"].hide { + display: none; +} + +[class*="span"].pull-right, +.row-fluid [class*="span"].pull-right { + float: right; +} + +form { + margin: 0 0 20px; +} + +fieldset { + padding: 0; + margin: 0; + border: 0; +} + +legend { + display: block; + width: 100%; + padding: 0; + margin-bottom: 20px; + *margin-left: -7px; + font-size: 21px; + line-height: 40px; + color: #333333; + border: 0; + border-bottom: 1px solid #e5e5e5; +} + +legend small { + font-size: 15px; + color: #999999; +} + +label, +input, +button, +select, +textarea { + font-size: 14px; + font-weight: normal; + line-height: 20px; +} + +input, +button, +select, +textarea { + font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; +} + +label { + display: block; + margin-bottom: 5px; +} + +select, +textarea, +.input-text, +.input-password, +.input-datetime, +.input-datetime-local, +.input-date, +.input-month, +.input-time, +.input-week, +.input-number, +.input-email, +.input-url, +.input-search, +.input-tel, +.input-color, +.uneditable-input { + display: inline-block; + height: 20px; + padding: 4px 6px; + margin-bottom: 10px; + font-size: 14px; + line-height: 20px; + color: #555555; + vertical-align: middle; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; +} + +input, +textarea, +.uneditable-input { + width: 206px; +} + +textarea { + height: auto; +} + +textarea, +.input-text, +.input-password, +.input-datetime, +.input-datetime-local, +.input-date, +.input-month, +.input-time, +.input-week, +.input-number, +.input-email, +.input-url, +.input-search, +.input-tel, +.input-color, +.uneditable-input { + background-color: #ffffff; + border: 1px solid #cccccc; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); + -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); + -webkit-transition: border linear 0.2s, box-shadow linear 0.2s; + -moz-transition: border linear 0.2s, box-shadow linear 0.2s; + -o-transition: border linear 0.2s, box-shadow linear 0.2s; + transition: border linear 0.2s, box-shadow linear 0.2s; +} + +textarea:focus, +.input-text:focus, +.input-password:focus, +.input-datetime:focus, +.input-datetime-local:focus, +.input-date:focus, +.input-month:focus, +.input-time:focus, +.input-week:focus, +.input-number:focus, +.input-email:focus, +.input-url:focus, +.input-search:focus, +.input-tel:focus, +.input-color:focus, +.uneditable-input:focus { + border-color: rgba(82, 168, 236, 0.8); + outline: 0; + outline: thin dotted \9; + /* IE6-9 */ + + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(82, 168, 236, 0.6); + -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(82, 168, 236, 0.6); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(82, 168, 236, 0.6); +} + +input[type="radio"], +input[type="checkbox"] { + margin: 4px 0 0; + margin-top: 1px \9; + *margin-top: 0; + line-height: normal; + cursor: pointer; +} + +.input-file, +.input-image, +.input-submit, +.input-reset, +.input-button, +.input-radio, +.input-checkbox { + width: auto; +} + +select, +input[type="file"] { + height: 30px; + /* In IE7, the height of the select element cannot be changed by height, only font-size */ + + *margin-top: 4px; + /* For IE7, add top margin to align select with labels */ + + line-height: 30px; +} + +select { + width: 220px; + background-color: #ffffff; + border: 1px solid #cccccc; +} + +.select-multiple, +.select-size { + height: auto; +} + +select:focus, +input[type="file"]:focus, +input[type="radio"]:focus, +input[type="checkbox"]:focus { + outline: thin dotted #333; + outline: 5px auto -webkit-focus-ring-color; + outline-offset: -2px; +} + +.uneditable-input, +.uneditable-textarea { + color: #999999; + cursor: not-allowed; + background-color: #fcfcfc; + border-color: #cccccc; + -webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.025); + -moz-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.025); + box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.025); +} + +.uneditable-input { + overflow: hidden; + white-space: nowrap; +} + +.uneditable-textarea { + width: auto; + height: auto; +} + +input:-moz-placeholder, +textarea:-moz-placeholder { + color: #999999; +} + +input:-ms-input-placeholder, +textarea:-ms-input-placeholder { + color: #999999; +} + +input::-webkit-input-placeholder, +textarea::-webkit-input-placeholder { + color: #999999; +} + +.radio, +.checkbox { + min-height: 20px; + padding-left: 20px; + _padding-left: 0; +} + +.radio input[type="radio"], +.checkbox input[type="checkbox"] { + float: left; + margin-left: -20px; +} + +.controls > .radio:first-child, +.controls > .checkbox:first-child { + padding-top: 5px; +} + +.radio.inline, +.checkbox.inline, +.radio-inline, +.checkbox-inline { + display: inline-block; + padding-top: 5px; + margin-bottom: 0; + vertical-align: middle; +} + +.radio.inline + .radio.inline, +.checkbox.inline + .checkbox.inline, +.radio-inline .checkbox-inline { + margin-left: 10px; +} + +.input-mini { + width: 60px; +} + +.input-small { + width: 90px; +} + +.input-medium { + width: 150px; +} + +.input-large { + width: 210px; +} + +.input-xlarge { + width: 270px; +} + +.input-xxlarge { + width: 530px; +} + +input[class*="span"], +select[class*="span"], +textarea[class*="span"], +.uneditable-input[class*="span"], +.row-fluid input[class*="span"], +.row-fluid select[class*="span"], +.row-fluid textarea[class*="span"], +.row-fluid .uneditable-input[class*="span"] { + float: none; + margin-left: 0; +} + +.input-append input[class*="span"], +.input-append .uneditable-input[class*="span"], +.input-prepend input[class*="span"], +.input-prepend .uneditable-input[class*="span"], +.row-fluid input[class*="span"], +.row-fluid select[class*="span"], +.row-fluid textarea[class*="span"], +.row-fluid .uneditable-input[class*="span"], +.row-fluid .input-prepend [class*="span"], +.row-fluid .input-append [class*="span"] { + display: inline-block; +} + +input, +textarea, +.uneditable-input { + margin-left: 0; +} + +.controls-row [class*="span"] + [class*="span"] { + margin-left: 20px; +} + +input.span12, +textarea.span12 { + float: none; + width: 926px; + margin-left: 0; +} + +.uneditable-input-span12 { + width: 926px; +} + +input.span11, +textarea.span11 { + float: none; + width: 846px; + margin-left: 0; +} + +.uneditable-input-span11 { + width: 846px; +} + +input.span10, +textarea.span10 { + float: none; + width: 766px; + margin-left: 0; +} + +.uneditable-input-span10 { + width: 766px; +} + +input.span9, +textarea.span9 { + float: none; + width: 686px; + margin-left: 0; +} + +.uneditable-input-span9 { + width: 686px; +} + +input.span8, +textarea.span8 { + float: none; + width: 606px; + margin-left: 0; +} + +.uneditable-input-span8 { + width: 606px; +} + +input.span7, +textarea.span7 { + float: none; + width: 526px; + margin-left: 0; +} + +.uneditable-input-span7 { + width: 526px; +} + +input.span6, +textarea.span6 { + float: none; + width: 446px; + margin-left: 0; +} + +.uneditable-input-span6 { + width: 446px; +} + +input.span5, +textarea.span5 { + float: none; + width: 366px; + margin-left: 0; +} + +.uneditable-input-span5 { + width: 366px; +} + +input.span4, +textarea.span4 { + float: none; + width: 286px; + margin-left: 0; +} + +.uneditable-input-span4 { + width: 286px; +} + +input.span3, +textarea.span3 { + float: none; + width: 206px; + margin-left: 0; +} + +.uneditable-input-span3 { + width: 206px; +} + +input.span2, +textarea.span2 { + float: none; + width: 126px; + margin-left: 0; +} + +.uneditable-input-span2 { + width: 126px; +} + +input.span1, +textarea.span1 { + float: none; + width: 46px; + margin-left: 0; +} + +.uneditable-input-span1 { + width: 46px; +} + +.controls-row { + *zoom: 1; +} + +.controls-row:before, +.controls-row:after { + display: table; + line-height: 0; + content: ""; +} + +.controls-row:after { + clear: both; +} + +.controls-row [class*="span"], +.row-fluid .controls-row [class*="span"] { + float: left; +} + +.controls-row .checkbox[class*="span"], +.controls-row .radio[class*="span"] { + padding-top: 5px; +} + +.input-disabled, +.select-disabled, +.textarea-disabled, +.input-readonly, +.select-readonly, +.textarea-readonly { + cursor: not-allowed; + background-color: #eeeeee; +} + +.radio-disabled, +.checkbox-disabled, +.radio-readonly, +.checkbox-readonly { + background-color: transparent; +} + +.control-group-warning label, +.control-group-warning .help-block, +.control-group-warning .help-inline { + color: #c09853; +} + +.control-group-warning * label { + color: inherit; +} + +.control-group-warning .checkbox, +.control-group-warning .radio, +.control-group-warning input, +.control-group-warning select, +.control-group-warning textarea { + color: #c09853; +} + +.control-group-warning input, +.control-group-warning select, +.control-group-warning textarea { + border-color: #c09853; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); + -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); +} + +.control-group-warning input:focus, +.control-group-warning select:focus, +.control-group-warning textarea:focus { + border-color: #a47e3c; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #dbc59e; + -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #dbc59e; + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #dbc59e; +} + +.control-group-warning .input-prepend .add-on, +.control-group-warning .input-append .add-on { + color: #c09853; + background-color: #fcf8e3; + border-color: #c09853; +} + +.control-group-error label, +.control-group-error .help-block, +.control-group-error .help-inline { + color: #b94a48; +} + +.control-group-error * label { + color: inherit; +} + +.control-group-error .checkbox, +.control-group-error .radio, +.control-group-error input, +.control-group-error select, +.control-group-error textarea { + color: #b94a48; +} + +.control-group-error input, +.control-group-error select, +.control-group-error textarea { + border-color: #b94a48; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); + -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); +} + +.control-group-error input:focus, +.control-group-error select:focus, +.control-group-error textarea:focus { + border-color: #953b39; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #d59392; + -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #d59392; + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #d59392; +} + +.control-group-error .input-prepend .add-on, +.control-group-error .input-append .add-on { + color: #b94a48; + background-color: #f2dede; + border-color: #b94a48; +} + +.control-group-success label, +.control-group-success .help-block, +.control-group-success .help-inline { + color: #468847; +} + +.control-group-success * label { + color: inherit; +} + +.control-group-success .checkbox, +.control-group-success .radio, +.control-group-success input, +.control-group-success select, +.control-group-success textarea { + color: #468847; +} + +.control-group-success input, +.control-group-success select, +.control-group-success textarea { + border-color: #468847; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); + -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); +} + +.control-group-success input:focus, +.control-group-success select:focus, +.control-group-success textarea:focus { + border-color: #356635; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #7aba7b; + -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #7aba7b; + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #7aba7b; +} + +.control-group-success .input-prepend .add-on, +.control-group-success .input-append .add-on { + color: #468847; + background-color: #dff0d8; + border-color: #468847; +} + +.control-group-info label, +.control-group-info .help-block, +.control-group-info .help-inline { + color: #3a87ad; +} + +.control-group-info * label { + color: inherit; +} + +.control-group-info .checkbox, +.control-group-info .radio, +.control-group-info input, +.control-group-info select, +.control-group-info textarea { + color: #3a87ad; +} + +.control-group-info input, +.control-group-info select, +.control-group-info textarea { + border-color: #3a87ad; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); + -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); +} + +.control-group-info input:focus, +.control-group-info select:focus, +.control-group-info textarea:focus { + border-color: #2d6987; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #7ab5d3; + -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #7ab5d3; + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #7ab5d3; +} + +.control-group-info .input-prepend .add-on, +.control-group-info .input-append .add-on { + color: #3a87ad; + background-color: #d9edf7; + border-color: #3a87ad; +} + +input:focus:required:invalid, +textarea:focus:required:invalid, +select:focus:required:invalid { + color: #b94a48; + border-color: #ee5f5b; +} + +input:focus:required:invalid:focus, +textarea:focus:required:invalid:focus, +select:focus:required:invalid:focus { + border-color: #e9322d; + -webkit-box-shadow: 0 0 6px #f8b9b7; + -moz-box-shadow: 0 0 6px #f8b9b7; + box-shadow: 0 0 6px #f8b9b7; +} + +.form-actions { + padding: 19px 20px 20px; + margin-top: 20px; + margin-bottom: 20px; + background-color: #f5f5f5; + border-top: 1px solid #e5e5e5; + *zoom: 1; +} + +.form-actions:before, +.form-actions:after { + display: table; + line-height: 0; + content: ""; +} + +.form-actions:after { + clear: both; +} + +.help-block, +.help-inline { + color: #595959; +} + +.help-block { + display: block; + margin-bottom: 10px; +} + +.help-inline { + display: inline-block; + *display: inline; + padding-left: 5px; + vertical-align: middle; + *zoom: 1; +} + +.input-append, +.input-prepend { + margin-bottom: 5px; + font-size: 0; + white-space: nowrap; +} + +.input-append input, +.input-prepend input, +.input-append select, +.input-prepend select, +.input-append .uneditable-input, +.input-prepend .uneditable-input, +.input-append .dropdown-menu, +.input-prepend .dropdown-menu { + font-size: 14px; +} + +.input-append input, +.input-prepend input, +.input-append select, +.input-prepend select, +.input-append .uneditable-input, +.input-prepend .uneditable-input { + position: relative; + margin-bottom: 0; + *margin-left: 0; + vertical-align: top; + -webkit-border-radius: 0 4px 4px 0; + -moz-border-radius: 0 4px 4px 0; + border-radius: 0 4px 4px 0; +} + +.input-append input:focus, +.input-prepend input:focus, +.input-append select:focus, +.input-prepend select:focus, +.input-append .uneditable-input:focus, +.input-prepend .uneditable-input:focus { + z-index: 2; +} + +.input-append .add-on, +.input-prepend .add-on { + display: inline-block; + width: auto; + height: 20px; + min-width: 16px; + padding: 4px 5px; + font-size: 14px; + font-weight: normal; + line-height: 20px; + text-align: center; + text-shadow: 0 1px 0 #ffffff; + background-color: #eeeeee; + border: 1px solid #ccc; +} + +.input-append .add-on, +.input-prepend .add-on, +.input-append .btn, +.input-prepend .btn { + *margin-top: 1px; + vertical-align: top; + -webkit-border-radius: 0; + -moz-border-radius: 0; + border-radius: 0; +} + +.input-append .active, +.input-prepend .active { + background-color: #a9dba9; + border-color: #46a546; +} + +.input-prepend .add-on, +.input-prepend .btn { + margin-right: -1px; + *margin-right: -2px; +} + +.input-prepend .add-on:first-child, +.input-prepend .btn:first-child { + -webkit-border-radius: 4px 0 0 4px; + -moz-border-radius: 4px 0 0 4px; + border-radius: 4px 0 0 4px; +} + +.input-append input, +.input-append select, +.input-append .uneditable-input { + -webkit-border-radius: 4px 0 0 4px; + -moz-border-radius: 4px 0 0 4px; + border-radius: 4px 0 0 4px; +} + +.input-append input + .btn-group .btn, +.input-append select + .btn-group .btn, +.input-append .uneditable-input + .btn-group .btn { + -webkit-border-radius: 0 4px 4px 0; + -moz-border-radius: 0 4px 4px 0; + border-radius: 0 4px 4px 0; +} + +.input-append .add-on, +.input-append .btn, +.input-append .btn-group { + margin-left: -1px; + *margin-left: -2px; +} + +.input-append .add-on:last-child, +.input-append .btn:last-child { + -webkit-border-radius: 0 4px 4px 0; + -moz-border-radius: 0 4px 4px 0; + border-radius: 0 4px 4px 0; +} + +.input-prepend.input-append input, +.input-prepend.input-append select, +.input-prepend.input-append .uneditable-input { + -webkit-border-radius: 0; + -moz-border-radius: 0; + border-radius: 0; +} + +.input-prepend.input-append input + .btn-group .btn, +.input-prepend.input-append select + .btn-group .btn, +.input-prepend.input-append .uneditable-input + .btn-group .btn { + -webkit-border-radius: 0 4px 4px 0; + -moz-border-radius: 0 4px 4px 0; + border-radius: 0 4px 4px 0; +} + +.input-prepend.input-append .add-on:first-child, +.input-prepend.input-append .btn:first-child { + margin-right: -1px; + -webkit-border-radius: 4px 0 0 4px; + -moz-border-radius: 4px 0 0 4px; + border-radius: 4px 0 0 4px; +} + +.input-prepend.input-append .add-on:last-child, +.input-prepend.input-append .btn:last-child { + margin-left: -1px; + -webkit-border-radius: 0 4px 4px 0; + -moz-border-radius: 0 4px 4px 0; + border-radius: 0 4px 4px 0; +} + +.input-prepend.input-append .btn-group:first-child { + margin-left: 0; +} + +input.search-query { + padding-right: 14px; + padding-right: 4px \9; + padding-left: 14px; + padding-left: 4px \9; + /* IE7-8 doesn't have border-radius, so don't indent the padding */ + + margin-bottom: 0; + -webkit-border-radius: 15px; + -moz-border-radius: 15px; + border-radius: 15px; +} + +/* Allow for input prepend/append in search forms */ + +.form-search .input-append .search-query, +.form-search .input-prepend .search-query { + -webkit-border-radius: 0; + -moz-border-radius: 0; + border-radius: 0; +} + +.form-search .input-append .search-query { + -webkit-border-radius: 14px 0 0 14px; + -moz-border-radius: 14px 0 0 14px; + border-radius: 14px 0 0 14px; +} + +.form-search .input-append .btn { + -webkit-border-radius: 0 14px 14px 0; + -moz-border-radius: 0 14px 14px 0; + border-radius: 0 14px 14px 0; +} + +.form-search .input-prepend .search-query { + -webkit-border-radius: 0 14px 14px 0; + -moz-border-radius: 0 14px 14px 0; + border-radius: 0 14px 14px 0; +} + +.form-search .input-prepend .btn { + -webkit-border-radius: 14px 0 0 14px; + -moz-border-radius: 14px 0 0 14px; + border-radius: 14px 0 0 14px; +} + +.form-search input, +.form-inline input, +.form-horizontal input, +.form-search textarea, +.form-inline textarea, +.form-horizontal textarea, +.form-search select, +.form-inline select, +.form-horizontal select, +.form-search .help-inline, +.form-inline .help-inline, +.form-horizontal .help-inline, +.form-search .uneditable-input, +.form-inline .uneditable-input, +.form-horizontal .uneditable-input, +.form-search .input-prepend, +.form-inline .input-prepend, +.form-horizontal .input-prepend, +.form-search .input-append, +.form-inline .input-append, +.form-horizontal .input-append { + display: inline-block; + *display: inline; + margin-bottom: 0; + vertical-align: middle; + *zoom: 1; +} + +.form-search .hide, +.form-inline .hide, +.form-horizontal .hide { + display: none; +} + +.form-search label, +.form-inline label, +.form-search .btn-group, +.form-inline .btn-group { + display: inline-block; +} + +.form-search .input-append, +.form-inline .input-append, +.form-search .input-prepend, +.form-inline .input-prepend { + margin-bottom: 0; +} + +.form-search .radio, +.form-search .checkbox, +.form-inline .radio, +.form-inline .checkbox { + padding-left: 0; + margin-bottom: 0; + vertical-align: middle; +} + +.form-search .radio input[type="radio"], +.form-search .checkbox input[type="checkbox"], +.form-inline .radio input[type="radio"], +.form-inline .checkbox input[type="checkbox"] { + float: left; + margin-right: 3px; + margin-left: 0; +} + +.control-group { + margin-bottom: 10px; +} + +legend + .control-group { + margin-top: 20px; + -webkit-margin-top-collapse: separate; +} + +.form-horizontal .control-group { + margin-bottom: 20px; + *zoom: 1; +} + +.form-horizontal .control-group:before, +.form-horizontal .control-group:after { + display: table; + line-height: 0; + content: ""; +} + +.form-horizontal .control-group:after { + clear: both; +} + +.form-horizontal .control-label { + float: left; + width: 160px; + padding-top: 5px; + text-align: right; +} + +.form-horizontal .controls { + *display: inline-block; + *padding-left: 20px; + margin-left: 180px; + *margin-left: 0; +} + +.form-horizontal .controls-first-child { + *padding-left: 180px; +} + +.form-horizontal .help-block { + margin-bottom: 0; +} + +.form-horizontal input + .help-block, +.form-horizontal select + .help-block, +.form-horizontal textarea + .help-block { + margin-top: 10px; +} + +.form-horizontal .form-actions { + padding-left: 180px; +} + +table { + max-width: 100%; + background-color: transparent; + border-collapse: collapse; + border-spacing: 0; +} + +.table { + width: 100%; + margin-bottom: 20px; +} + +.table th, +.table td { + padding: 8px; + line-height: 20px; + text-align: left; + vertical-align: top; + border-top: 1px solid #dddddd; +} + +.table th { + font-weight: bold; +} + +.table thead th { + vertical-align: bottom; +} + +.table caption + thead tr:first-child th, +.table caption + thead tr:first-child td, +.table colgroup + thead tr:first-child th, +.table colgroup + thead tr:first-child td, +.table thead:first-child tr:first-child th, +.table thead:first-child tr:first-child td { + border-top: 0; +} + +.table tbody + tbody { + border-top: 2px solid #dddddd; +} + +.table-condensed th, +.table-condensed td { + padding: 4px 5px; +} + +.table-bordered { + border: 1px solid #dddddd; + border-collapse: separate; + *border-collapse: collapse; + border-left: 0; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; +} + +.table-bordered th, +.table-bordered td { + border-left: 1px solid #dddddd; +} + +.table-bordered caption + thead tr:first-child th, +.table-bordered caption + tbody tr:first-child th, +.table-bordered caption + tbody tr:first-child td, +.table-bordered colgroup + thead tr:first-child th, +.table-bordered colgroup + tbody tr:first-child th, +.table-bordered colgroup + tbody tr:first-child td, +.table-bordered thead:first-child tr:first-child th, +.table-bordered tbody:first-child tr:first-child th, +.table-bordered tbody:first-child tr:first-child td { + border-top: 0; +} + +.table-bordered thead:first-child tr:first-child th:first-child, +.table-bordered tbody:first-child tr:first-child td:first-child { + -webkit-border-top-left-radius: 4px; + border-top-left-radius: 4px; + -moz-border-radius-topleft: 4px; +} + +.table-bordered thead:first-child tr:first-child th:last-child, +.table-bordered tbody:first-child tr:first-child td:last-child { + -webkit-border-top-right-radius: 4px; + border-top-right-radius: 4px; + -moz-border-radius-topright: 4px; +} + +.table-bordered thead:last-child tr:last-child th:first-child, +.table-bordered tbody:last-child tr:last-child td:first-child, +.table-bordered tfoot:last-child tr:last-child td:first-child { + -webkit-border-radius: 0 0 0 4px; + -moz-border-radius: 0 0 0 4px; + border-radius: 0 0 0 4px; + -webkit-border-bottom-left-radius: 4px; + border-bottom-left-radius: 4px; + -moz-border-radius-bottomleft: 4px; +} + +.table-bordered thead:last-child tr:last-child th:last-child, +.table-bordered tbody:last-child tr:last-child td:last-child, +.table-bordered tfoot:last-child tr:last-child td:last-child { + -webkit-border-bottom-right-radius: 4px; + border-bottom-right-radius: 4px; + -moz-border-radius-bottomright: 4px; +} + +.table-bordered caption + thead tr:first-child th:first-child, +.table-bordered caption + tbody tr:first-child td:first-child, +.table-bordered colgroup + thead tr:first-child th:first-child, +.table-bordered colgroup + tbody tr:first-child td:first-child { + -webkit-border-top-left-radius: 4px; + border-top-left-radius: 4px; + -moz-border-radius-topleft: 4px; +} + +.table-bordered caption + thead tr:first-child th:last-child, +.table-bordered caption + tbody tr:first-child td:last-child, +.table-bordered colgroup + thead tr:first-child th:last-child, +.table-bordered colgroup + tbody tr:first-child td:last-child { + -webkit-border-top-right-radius: 4px; + border-top-right-radius: 4px; + -moz-border-radius-topright: 4px; +} + +.table-striped tbody tr:nth-child(odd) td, +.table-striped tbody tr:nth-child(odd) th { + background-color: #f9f9f9; +} + +.table-hover tbody .tr-hover td, +.table-hover tbody .tr-hover th { + background-color: #f5f5f5; +} + +table td[class*="span"], +table th[class*="span"], +.row-fluid table td[class*="span"], +.row-fluid table th[class*="span"] { + display: table-cell; + float: none; + margin-left: 0; +} + +.table td.span1, +.table th.span1 { + float: none; + width: 44px; + margin-left: 0; +} + +.table td.span2, +.table th.span2 { + float: none; + width: 124px; + margin-left: 0; +} + +.table td.span3, +.table th.span3 { + float: none; + width: 204px; + margin-left: 0; +} + +.table td.span4, +.table th.span4 { + float: none; + width: 284px; + margin-left: 0; +} + +.table td.span5, +.table th.span5 { + float: none; + width: 364px; + margin-left: 0; +} + +.table td.span6, +.table th.span6 { + float: none; + width: 444px; + margin-left: 0; +} + +.table td.span7, +.table th.span7 { + float: none; + width: 524px; + margin-left: 0; +} + +.table td.span8, +.table th.span8 { + float: none; + width: 604px; + margin-left: 0; +} + +.table td.span9, +.table th.span9 { + float: none; + width: 684px; + margin-left: 0; +} + +.table td.span10, +.table th.span10 { + float: none; + width: 764px; + margin-left: 0; +} + +.table td.span11, +.table th.span11 { + float: none; + width: 844px; + margin-left: 0; +} + +.table td.span12, +.table th.span12 { + float: none; + width: 924px; + margin-left: 0; +} + +.table tbody tr.success td { + background-color: #dff0d8; +} + +.table tbody tr.error td { + background-color: #f2dede; +} + +.table tbody tr.warning td { + background-color: #fcf8e3; +} + +.table tbody tr.info td { + background-color: #d9edf7; +} + +.table-hover tbody tr.success:hover td { + background-color: #d0e9c6; +} + +.table-hover tbody tr.error:hover td { + background-color: #ebcccc; +} + +.table-hover tbody tr.warning:hover td { + background-color: #faf2cc; +} + +.table-hover tbody tr.info:hover td { + background-color: #c4e3f3; +} + +.icon-xxx { + display: inline-block; + _display: inline; + width: 14px; + _width: 14px; + height: 14px; + _height: 19px; + margin-top: 1px; + *margin-right: .3em; + line-height: 14px; + vertical-align: text-top; + _vertical-align: middle; + background-image: url("../img/glyphicons-halflings.png"); + _background-image: url("../img/glyphicons-halflings-8.png"); + background-position: 14px 14px; + background-repeat: no-repeat; + _zoom: 1; +} + +/* White icons with optional class, or on hover/active states of certain elements */ + +.icon-white { + background-image: url("../img/glyphicons-halflings-white.png"); +} + +.nav-pills > .active > a > [class^="icon-"], +.nav-pills > .active > a > [class*=" icon-"], +.nav-list > .active > a > [class^="icon-"], +.nav-list > .active > a > [class*=" icon-"], +.navbar-inverse .nav > .active > a > [class^="icon-"], +.navbar-inverse .nav > .active > a > [class*=" icon-"], +.dropdown-menu > li > a:hover > [class^="icon-"], +.dropdown-menu > li > a:hover > [class*=" icon-"], +.dropdown-menu > .active > a > [class^="icon-"], +.dropdown-menu > .active > a > [class*=" icon-"], +.dropdown-submenu:hover > a > [class^="icon-"], +.dropdown-submenu:hover > a > [class*=" icon-"] { + background-image: url("../img/glyphicons-halflings-white.png"); +} + +.icon-glass { + background-position: 0 0; +} + +.icon-music { + background-position: -24px 0; +} + +.icon-search { + background-position: -48px 0; +} + +.icon-envelope { + background-position: -72px 0; +} + +.icon-heart { + background-position: -96px 0; +} + +.icon-star { + background-position: -120px 0; +} + +.icon-star-empty { + background-position: -144px 0; +} + +.icon-user { + background-position: -168px 0; +} + +.icon-film { + background-position: -192px 0; +} + +.icon-th-large { + background-position: -216px 0; +} + +.icon-th { + background-position: -240px 0; +} + +.icon-th-list { + background-position: -264px 0; +} + +.icon-ok { + background-position: -288px 0; +} + +.icon-remove { + background-position: -312px 0; +} + +.icon-zoom-in { + background-position: -336px 0; +} + +.icon-zoom-out { + background-position: -360px 0; +} + +.icon-off { + background-position: -384px 0; +} + +.icon-signal { + background-position: -408px 0; +} + +.icon-cog { + background-position: -432px 0; +} + +.icon-trash { + background-position: -456px 0; +} + +.icon-home { + background-position: 0 -24px; +} + +.icon-file { + background-position: -24px -24px; +} + +.icon-time { + background-position: -48px -24px; +} + +.icon-road { + background-position: -72px -24px; +} + +.icon-download-alt { + background-position: -96px -24px; +} + +.icon-download { + background-position: -120px -24px; +} + +.icon-upload { + background-position: -144px -24px; +} + +.icon-inbox { + background-position: -168px -24px; +} + +.icon-play-circle { + background-position: -192px -24px; +} + +.icon-repeat { + background-position: -216px -24px; +} + +.icon-refresh { + background-position: -240px -24px; +} + +.icon-list-alt { + background-position: -264px -24px; +} + +.icon-lock { + background-position: -287px -24px; +} + +.icon-flag { + background-position: -312px -24px; +} + +.icon-headphones { + background-position: -336px -24px; +} + +.icon-volume-off { + background-position: -360px -24px; +} + +.icon-volume-down { + background-position: -384px -24px; +} + +.icon-volume-up { + background-position: -408px -24px; +} + +.icon-qrcode { + background-position: -432px -24px; +} + +.icon-barcode { + background-position: -456px -24px; +} + +.icon-tag { + background-position: 0 -48px; +} + +.icon-tags { + background-position: -25px -48px; +} + +.icon-book { + background-position: -48px -48px; +} + +.icon-bookmark { + background-position: -72px -48px; +} + +.icon-print { + background-position: -96px -48px; +} + +.icon-camera { + background-position: -120px -48px; +} + +.icon-font { + background-position: -144px -48px; +} + +.icon-bold { + background-position: -167px -48px; +} + +.icon-italic { + background-position: -192px -48px; +} + +.icon-text-height { + background-position: -216px -48px; +} + +.icon-text-width { + background-position: -240px -48px; +} + +.icon-align-left { + background-position: -264px -48px; +} + +.icon-align-center { + background-position: -288px -48px; +} + +.icon-align-right { + background-position: -312px -48px; +} + +.icon-align-justify { + background-position: -336px -48px; +} + +.icon-list { + background-position: -360px -48px; +} + +.icon-indent-left { + background-position: -384px -48px; +} + +.icon-indent-right { + background-position: -408px -48px; +} + +.icon-facetime-video { + background-position: -432px -48px; +} + +.icon-picture { + background-position: -456px -48px; +} + +.icon-pencil { + background-position: 0 -72px; +} + +.icon-map-marker { + background-position: -24px -72px; +} + +.icon-adjust { + background-position: -48px -72px; +} + +.icon-tint { + background-position: -72px -72px; +} + +.icon-edit { + background-position: -96px -72px; +} + +.icon-share { + background-position: -120px -72px; +} + +.icon-check { + background-position: -144px -72px; +} + +.icon-move { + background-position: -168px -72px; +} + +.icon-step-backward { + background-position: -192px -72px; +} + +.icon-fast-backward { + background-position: -216px -72px; +} + +.icon-backward { + background-position: -240px -72px; +} + +.icon-play { + background-position: -264px -72px; +} + +.icon-pause { + background-position: -288px -72px; +} + +.icon-stop { + background-position: -312px -72px; +} + +.icon-forward { + background-position: -336px -72px; +} + +.icon-fast-forward { + background-position: -360px -72px; +} + +.icon-step-forward { + background-position: -384px -72px; +} + +.icon-eject { + background-position: -408px -72px; +} + +.icon-chevron-left { + background-position: -432px -72px; +} + +.icon-chevron-right { + background-position: -456px -72px; +} + +.icon-plus-sign { + background-position: 0 -96px; +} + +.icon-minus-sign { + background-position: -24px -96px; +} + +.icon-remove-sign { + background-position: -48px -96px; +} + +.icon-ok-sign { + background-position: -72px -96px; +} + +.icon-question-sign { + background-position: -96px -96px; +} + +.icon-info-sign { + background-position: -120px -96px; +} + +.icon-screenshot { + background-position: -144px -96px; +} + +.icon-remove-circle { + background-position: -168px -96px; +} + +.icon-ok-circle { + background-position: -192px -96px; +} + +.icon-ban-circle { + background-position: -216px -96px; +} + +.icon-arrow-left { + background-position: -240px -96px; +} + +.icon-arrow-right { + background-position: -264px -96px; +} + +.icon-arrow-up { + background-position: -289px -96px; +} + +.icon-arrow-down { + background-position: -312px -96px; +} + +.icon-share-alt { + background-position: -336px -96px; +} + +.icon-resize-full { + background-position: -360px -96px; +} + +.icon-resize-small { + background-position: -384px -96px; +} + +.icon-plus { + background-position: -408px -96px; +} + +.icon-minus { + background-position: -433px -96px; +} + +.icon-asterisk { + background-position: -456px -96px; +} + +.icon-exclamation-sign { + background-position: 0 -120px; +} + +.icon-gift { + background-position: -24px -120px; +} + +.icon-leaf { + background-position: -48px -120px; +} + +.icon-fire { + background-position: -72px -120px; +} + +.icon-eye-open { + background-position: -96px -120px; +} + +.icon-eye-close { + background-position: -120px -120px; +} + +.icon-warning-sign { + background-position: -144px -120px; +} + +.icon-plane { + background-position: -168px -120px; +} + +.icon-calendar { + background-position: -192px -120px; +} + +.icon-random { + width: 16px; + background-position: -216px -120px; +} + +.icon-comment { + background-position: -240px -120px; +} + +.icon-magnet { + background-position: -264px -120px; +} + +.icon-chevron-up { + background-position: -288px -120px; +} + +.icon-chevron-down { + background-position: -313px -119px; +} + +.icon-retweet { + background-position: -336px -120px; +} + +.icon-shopping-cart { + background-position: -360px -120px; +} + +.icon-folder-close { + background-position: -384px -120px; +} + +.icon-folder-open { + width: 16px; + background-position: -408px -120px; +} + +.icon-resize-vertical { + background-position: -432px -119px; +} + +.icon-resize-horizontal { + background-position: -456px -118px; +} + +.icon-hdd { + background-position: 0 -144px; +} + +.icon-bullhorn { + background-position: -24px -144px; +} + +.icon-bell { + background-position: -48px -144px; +} + +.icon-certificate { + background-position: -72px -144px; +} + +.icon-thumbs-up { + background-position: -96px -144px; +} + +.icon-thumbs-down { + background-position: -120px -144px; +} + +.icon-hand-right { + background-position: -144px -144px; +} + +.icon-hand-left { + background-position: -168px -144px; +} + +.icon-hand-up { + background-position: -192px -144px; +} + +.icon-hand-down { + background-position: -216px -144px; +} + +.icon-circle-arrow-right { + background-position: -240px -144px; +} + +.icon-circle-arrow-left { + background-position: -264px -144px; +} + +.icon-circle-arrow-up { + background-position: -288px -144px; +} + +.icon-circle-arrow-down { + background-position: -312px -144px; +} + +.icon-globe { + background-position: -336px -144px; +} + +.icon-wrench { + background-position: -360px -144px; +} + +.icon-tasks { + background-position: -384px -144px; +} + +.icon-filter { + background-position: -408px -144px; +} + +.icon-briefcase { + background-position: -432px -144px; +} + +.icon-fullscreen { + background-position: -456px -144px; +} + +.dropup, +.dropdown { + position: relative; + /* fix for IE6 BFC */ + + display: inline; + *zoom: 1; +} + +.dropdown-toggle { + *margin-bottom: -3px; +} + +.dropdown-toggle:active, +.open .dropdown-toggle { + outline: 0; +} + +.caret { + display: inline-block; + *display: inline; + width: 0; + height: 0; + vertical-align: top; + border-top: 4px solid #000000; + border-right: 4px solid transparent; + border-left: 4px solid transparent; + content: ""; + *zoom: 1; +} + +/* fix for IE6 */ + +.caret { + width: 0; + height: 0; + line-height: 0; + border-color: #000000 transparent transparent; + *border-color: #000000 #fffffe #fffffe; + border-style: solid; + border-width: 4px 4px 0; + *filter: progid:DXImageTransform.Microsoft.Chroma(color=#fffffe); +} + +.dropdown .caret { + margin-top: 8px; + margin-left: 2px; +} + +.dropdown-menu { + position: absolute; + top: 100%; + left: 0; + z-index: 1000; + display: none; + float: left; + *width: auto !important; + *width: 160px; + *height: 1px; + min-width: 160px; + padding: 5px 0; + margin: 2px 0 0; + list-style: none; + background-color: #ffffff; + border: 1px solid #ccc; + border: 1px solid rgba(0, 0, 0, 0.2); + *border-right-width: 2px; + *border-bottom-width: 2px; + -webkit-border-radius: 6px; + -moz-border-radius: 6px; + border-radius: 6px; + -webkit-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2); + -moz-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2); + box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2); + -webkit-background-clip: padding-box; + -moz-background-clip: padding; + background-clip: padding-box; +} + +.dropdown-menu.pull-right { + right: 0; + left: auto; +} + +.dropdown-menu .divider { + *width: 100%; + height: 1px; + margin: 9px 1px; + *margin: -5px 0 5px; + overflow: hidden; + background-color: #e5e5e5; + border-bottom: 1px solid #ffffff; +} + +.dropdown-menu li a { + display: block; + padding: 3px 20px; + clear: both; + font-weight: normal; + line-height: 20px; + color: #333333; + white-space: nowrap; + *zoom: 1; +} + +.dropdown-menu li a:hover, +.dropdown-menu li a:focus, +.dropdown-submenu:hover a, +.dropdown-submenu-hover a { + color: #ffffff; + text-decoration: none; + background-color: #0081c2; + background-image: -moz-linear-gradient(top, #0088cc, #0077b3); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#0088cc), to(#0077b3)); + background-image: -webkit-linear-gradient(top, #0088cc, #0077b3); + background-image: -o-linear-gradient(top, #0088cc, #0077b3); + background-image: linear-gradient(to bottom, #0088cc, #0077b3); + background-repeat: repeat-x; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff0088cc', endColorstr='#ff0077b3', GradientType=0); +} + +/* fix for IE6 li:hover */ + +.dropdown-submenu-hover * a { + *color: inherit; + *background-color: #fffffe; + *background-image: none; + *filter: none; + *filter: chroma(color=#fffffe); +} + +.dropdown-menu .active a, +.dropdown-menu .active a:hover { + color: #333333; + text-decoration: none; + background-color: #0081c2; + background-image: -moz-linear-gradient(top, #0088cc, #0077b3); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#0088cc), to(#0077b3)); + background-image: -webkit-linear-gradient(top, #0088cc, #0077b3); + background-image: -o-linear-gradient(top, #0088cc, #0077b3); + background-image: linear-gradient(to bottom, #0088cc, #0077b3); + background-repeat: repeat-x; + outline: 0; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff0088cc', endColorstr='#ff0077b3', GradientType=0); +} + +.dropdown-menu .disabled a, +.dropdown-menu .disabled a:hover { + color: #999999; +} + +.dropdown-menu .disabled a:hover { + text-decoration: none; + cursor: default; + background-color: transparent; + *background-color: #fffffe; + background-image: none; + *filter: chroma(color=#fffffe); +} + +.open { + *z-index: 1000; +} + +.open .dropdown-menu { + display: block; + /* fix for IE6 inline-block */ + + *zoom: 1; +} + +.pull-right .dropdown-menu { + right: 0; + left: auto; +} + +.pull-right * .dropdown-menu { + right: auto; + left: 0; +} + +.dropup .caret, +.navbar-fixed-bottom .dropdown .caret { + border-top: 0; + border-bottom: 4px solid #000000; + content: ""; +} + +.dropup .dropdown-menu, +.navbar-fixed-bottom .dropdown .dropdown-menu { + top: auto; + bottom: 100%; + margin-bottom: 1px; +} + +.dropdown-submenu { + position: relative; + /* fix for IE6 absolute element will expand parent element's height */ + + *display: inline; + *zoom: 1; +} + +.dropdown-submenu .dropdown-menu { + top: 0; + left: 100%; + margin-top: -6px; + margin-left: -1px; + -webkit-border-radius: 0 6px 6px 6px; + -moz-border-radius: 0 6px 6px 6px; + border-radius: 0 6px 6px 6px; +} + +.dropdown-submenu:hover .dropdown-menu, +.dropdown-submenu-hover .dropdown-menu { + display: block; +} + +.dropup .dropdown-submenu .dropdown-menu { + top: auto; + bottom: 0; + margin-top: 0; + margin-bottom: -2px; + -webkit-border-radius: 5px 5px 5px 0; + -moz-border-radius: 5px 5px 5px 0; + border-radius: 5px 5px 5px 0; +} + +.dropdown-submenu a:after { + display: block; + float: right; + width: 0; + height: 0; + margin-top: 5px; + margin-right: -10px; + border-color: transparent; + *border-color: #fffffe; + border-left-color: #cccccc; + border-style: solid; + border-width: 5px 0 5px 5px; + content: " "; + *filter: chroma(color=#fffffe); + *zoom: 1; +} + +/* fix for IE6 */ + +.dropdown-submenu .dropdown-tri { + display: block; + float: right; + width: 0; + height: 0; + margin: -1.3em 5px 0 0; + line-height: 0; + border-color: transparent transparent transparent #cccccc; + _border-color: #fffffe #fffffe #fffffe #cccccc; + border-style: solid; + border-width: 5px 0 5px 5px; + _filter: chroma(color=#fffffe); + zoom: 1; +} + +.dropdown-submenu:hover a:after, +.dropdown-submenu-hover .dropdown-tri { + border-left-color: #ffffff; +} + +.dropdown-submenu-pull-left { + float: none; + /* fix for IE6 */ + +} + +.dropdown-submenu-pull-left .dropdown-menu { + left: -100%; + margin-left: 10px; + *margin-left: 18px; + -webkit-border-radius: 6px 0 6px 6px; + -moz-border-radius: 6px 0 6px 6px; + border-radius: 6px 0 6px 6px; +} + +.dropdown-submenu-pull-left * .dropdown-menu { + left: 0; + margin-left: 0; + -webkit-border-radius: 0; + -moz-border-radius: 0; + border-radius: 0; +} + +.dropdown .dropdown-menu .nav-header { + padding-right: 20px; + padding-left: 20px; +} + +.typeahead { + margin-top: 2px; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; +} + +.btn { + display: inline-block; + *display: inline; + padding: 4px 12px; + _padding-top: 3px; + _padding-bottom: 3px; + margin-bottom: 0; + *margin-left: .3em; + font-size: 14px; + line-height: 20px; + *line-height: 20px; + color: #333333; + text-align: center; + text-shadow: 0 1px 1px rgba(255, 255, 255, 0.75); + vertical-align: middle; + cursor: pointer; + background-color: #f5f5f5; + *background-color: #e6e6e6; + background-image: -moz-linear-gradient(top, #ffffff, #e6e6e6); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#ffffff), to(#e6e6e6)); + background-image: -webkit-linear-gradient(top, #ffffff, #e6e6e6); + background-image: -o-linear-gradient(top, #ffffff, #e6e6e6); + background-image: linear-gradient(to bottom, #ffffff, #e6e6e6); + background-repeat: repeat-x; + border: 1px solid #bbbbbb; + *border: 0; + border-color: #e6e6e6 #e6e6e6 #bfbfbf; + border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); + border-bottom-color: #a2a2a2; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#ffe6e6e6', GradientType=0); + filter: progid:DXImageTransform.Microsoft.gradient(enabled=false); + *zoom: 1; + -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05); + -moz-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05); + box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05); +} + +.btn:hover, +.btn:active, +.btn.active, +.btn.disabled, +.btn[disabled] { + color: #333333; + background-color: #e6e6e6; + *background-color: #d9d9d9; +} + +.btn:active, +.btn.active { + background-color: #cccccc \9; +} + +.btn:first-child { + *margin-left: 0; +} + +.btn.btn-hover { + color: #333333; + text-decoration: none; + background-color: #e6e6e6; + *background-color: #d9d9d9; + /* Buttons in IE7 don't get borders, so darken on hover */ + + background-position: 0 -15px; + -webkit-transition: background-position 0.1s linear; + -moz-transition: background-position 0.1s linear; + -o-transition: background-position 0.1s linear; + transition: background-position 0.1s linear; +} + +.btn:focus { + outline: thin dotted #333; + outline: 5px auto -webkit-focus-ring-color; + outline-offset: -2px; +} + +.btn:active, +.btn.active { + background-color: #e6e6e6; + background-color: #d9d9d9 \9; + background-image: none; + outline: 0; + -webkit-box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05); + -moz-box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05); + box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05); +} + +.btn.disabled, +.btn[disabled], +.btn-disabled { + cursor: default; + background-color: #e6e6e6; + background-image: none; + opacity: 0.65; + filter: alpha(opacity=65); + -webkit-box-shadow: none; + -moz-box-shadow: none; + box-shadow: none; +} + +.btn.btn-large { + *padding-top: 7px; + *padding-bottom: 7px; +} + +.btn.btn-small { + *padding-top: 3px; + *padding-bottom: 3px; +} + +.btn.btn-mini { + *padding-top: 1px; + *padding-bottom: 1px; +} + +a.btn { + *padding-top: 5px; + *padding-bottom: 5px; +} + +a.btn.btn-large { + *padding-top: 13px; + *padding-bottom: 15px; +} + +a.btn.btn-small { + *padding-top: 4px; + *padding-bottom: 4px; +} + +a.btn.btn-mini { + *padding-top: 3px; + *padding-bottom: 3px; +} + +.btn-large { + padding: 11px 19px; + font-size: 17.5px; + -webkit-border-radius: 6px; + -moz-border-radius: 6px; + border-radius: 6px; +} + +.btn-large [class^="icon-"], +.btn-large [class*=" icon-"] { + margin-top: 2px; +} + +.btn-small { + padding: 2px 10px; + font-size: 11.9px; + -webkit-border-radius: 3px; + -moz-border-radius: 3px; + border-radius: 3px; +} + +.btn-small [class^="icon-"], +.btn-small [class*=" icon-"] { + margin-top: 0; +} + +.btn-mini { + padding: 1px 6px; + font-size: 10.5px; + -webkit-border-radius: 3px; + -moz-border-radius: 3px; + border-radius: 3px; +} + +.btn-block { + display: block; + width: 100%; + padding-right: 0; + padding-left: 0; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; +} + +.btn-block + .btn-block { + margin-top: 5px; +} + +input[type="submit"].btn-block, +input[type="reset"].btn-block, +input[type="button"].btn-block { + width: 100%; +} + +.btn-primary.active, +.btn-warning.active, +.btn-danger.active, +.btn-success.active, +.btn-info.active, +.btn-inverse.active { + color: rgba(255, 255, 255, 0.75); +} + +.btn { + border-color: #c5c5c5; + border-color: rgba(0, 0, 0, 0.15) rgba(0, 0, 0, 0.15) rgba(0, 0, 0, 0.25); +} + +.btn-primary { + color: #ffffff; + text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); + background-color: #006dcc; + *background-color: #0044cc; + background-image: -moz-linear-gradient(top, #0088cc, #0044cc); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#0088cc), to(#0044cc)); + background-image: -webkit-linear-gradient(top, #0088cc, #0044cc); + background-image: -o-linear-gradient(top, #0088cc, #0044cc); + background-image: linear-gradient(to bottom, #0088cc, #0044cc); + background-repeat: repeat-x; + border-color: #0044cc #0044cc #002a80; + border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff0088cc', endColorstr='#ff0044cc', GradientType=0); + filter: progid:DXImageTransform.Microsoft.gradient(enabled=false); +} + +.btn-primary:hover, +.btn-primary:active, +.btn-primary.active, +.btn-primary.disabled, +.btn-primary[disabled] { + color: #ffffff; + background-color: #0044cc; + *background-color: #003bb3; +} + +.btn-primary:active, +.btn-primary.active { + background-color: #003399 \9; +} + +.btn-primary.btn-primary-hover { + color: #fff; + background-color: #0044cc; + *background-color: #003bb3; +} + +a.btn-primary-hover:hover { + color: #fff; + background-color: #0044cc; + *background-color: #003bb3; +} + +.btn-warning { + color: #ffffff; + text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); + background-color: #faa732; + *background-color: #f89406; + background-image: -moz-linear-gradient(top, #fbb450, #f89406); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#fbb450), to(#f89406)); + background-image: -webkit-linear-gradient(top, #fbb450, #f89406); + background-image: -o-linear-gradient(top, #fbb450, #f89406); + background-image: linear-gradient(to bottom, #fbb450, #f89406); + background-repeat: repeat-x; + border-color: #f89406 #f89406 #ad6704; + border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffbb450', endColorstr='#fff89406', GradientType=0); + filter: progid:DXImageTransform.Microsoft.gradient(enabled=false); +} + +.btn-warning:hover, +.btn-warning:active, +.btn-warning.active, +.btn-warning.disabled, +.btn-warning[disabled] { + color: #ffffff; + background-color: #f89406; + *background-color: #df8505; +} + +.btn-warning:active, +.btn-warning.active { + background-color: #c67605 \9; +} + +.btn-warning.btn-warning-hover { + color: #fff; + background-color: #f89406; + *background-color: #df8505; +} + +a.btn-warning-hover:hover { + color: #fff; + background-color: #f89406; + *background-color: #df8505; +} + +.btn-danger { + color: #ffffff; + text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); + background-color: #da4f49; + *background-color: #bd362f; + background-image: -moz-linear-gradient(top, #ee5f5b, #bd362f); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#ee5f5b), to(#bd362f)); + background-image: -webkit-linear-gradient(top, #ee5f5b, #bd362f); + background-image: -o-linear-gradient(top, #ee5f5b, #bd362f); + background-image: linear-gradient(to bottom, #ee5f5b, #bd362f); + background-repeat: repeat-x; + border-color: #bd362f #bd362f #802420; + border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffee5f5b', endColorstr='#ffbd362f', GradientType=0); + filter: progid:DXImageTransform.Microsoft.gradient(enabled=false); +} + +.btn-danger:hover, +.btn-danger:active, +.btn-danger.active, +.btn-danger.disabled, +.btn-danger[disabled] { + color: #ffffff; + background-color: #bd362f; + *background-color: #a9302a; +} + +.btn-danger:active, +.btn-danger.active { + background-color: #942a25 \9; +} + +.btn-danger.btn-danger-hover { + color: #fff; + background-color: #bd362f; + *background-color: #a9302a; +} + +a.btn-danger-hover:hover { + color: #fff; + background-color: #bd362f; + *background-color: #a9302a; +} + +.btn-success { + color: #ffffff; + text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); + background-color: #5bb75b; + *background-color: #51a351; + background-image: -moz-linear-gradient(top, #62c462, #51a351); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#62c462), to(#51a351)); + background-image: -webkit-linear-gradient(top, #62c462, #51a351); + background-image: -o-linear-gradient(top, #62c462, #51a351); + background-image: linear-gradient(to bottom, #62c462, #51a351); + background-repeat: repeat-x; + border-color: #51a351 #51a351 #387038; + border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff62c462', endColorstr='#ff51a351', GradientType=0); + filter: progid:DXImageTransform.Microsoft.gradient(enabled=false); +} + +.btn-success:hover, +.btn-success:active, +.btn-success.active, +.btn-success.disabled, +.btn-success[disabled] { + color: #ffffff; + background-color: #51a351; + *background-color: #499249; +} + +.btn-success:active, +.btn-success.active { + background-color: #408140 \9; +} + +.btn-success.btn-success-hover { + color: #fff; + background-color: #51a351; + *background-color: #499249; +} + +a.btn-success-hover:hover { + color: #fff; + background-color: #51a351; + *background-color: #499249; +} + +.btn-info { + color: #ffffff; + text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); + background-color: #49afcd; + *background-color: #2f96b4; + background-image: -moz-linear-gradient(top, #5bc0de, #2f96b4); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#5bc0de), to(#2f96b4)); + background-image: -webkit-linear-gradient(top, #5bc0de, #2f96b4); + background-image: -o-linear-gradient(top, #5bc0de, #2f96b4); + background-image: linear-gradient(to bottom, #5bc0de, #2f96b4); + background-repeat: repeat-x; + border-color: #2f96b4 #2f96b4 #1f6377; + border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff2f96b4', GradientType=0); + filter: progid:DXImageTransform.Microsoft.gradient(enabled=false); +} + +.btn-info:hover, +.btn-info:active, +.btn-info.active, +.btn-info.disabled, +.btn-info[disabled] { + color: #ffffff; + background-color: #2f96b4; + *background-color: #2a85a0; +} + +.btn-info:active, +.btn-info.active { + background-color: #24748c \9; +} + +.btn-info.btn-info-hover { + color: #fff; + background-color: #2f96b4; + *background-color: #2a85a0; +} + +a.btn-info-hover:hover { + color: #fff; + background-color: #2f96b4; + *background-color: #2a85a0; +} + +.btn-inverse { + color: #ffffff; + text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); + background-color: #363636; + *background-color: #222222; + background-image: -moz-linear-gradient(top, #444444, #222222); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#444444), to(#222222)); + background-image: -webkit-linear-gradient(top, #444444, #222222); + background-image: -o-linear-gradient(top, #444444, #222222); + background-image: linear-gradient(to bottom, #444444, #222222); + background-repeat: repeat-x; + border-color: #222222 #222222 #000000; + border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff444444', endColorstr='#ff222222', GradientType=0); + filter: progid:DXImageTransform.Microsoft.gradient(enabled=false); +} + +.btn-inverse:hover, +.btn-inverse:active, +.btn-inverse.active, +.btn-inverse.disabled, +.btn-inverse[disabled] { + color: #ffffff; + background-color: #222222; + *background-color: #151515; +} + +.btn-inverse:active, +.btn-inverse.active { + background-color: #080808 \9; +} + +.btn-inverse.btn-inverse-hover { + color: #fff; + background-color: #222222; + *background-color: #3c3c3c; +} + +a.btn-inverse-hover:hover { + color: #fff; + background-color: #222222; + *background-color: #3c3c3c; +} + +button.btn, +input[type="submit"].btn { + *padding-top: 3px; + *padding-bottom: 3px; +} + +button.btn::-moz-focus-inner, +input[type="submit"].btn::-moz-focus-inner { + padding: 0; + border: 0; +} + +button.btn.btn-large, +input[type="submit"].btn.btn-large { + *padding-top: 7px; + *padding-bottom: 7px; +} + +button.btn.btn-small, +input[type="submit"].btn.btn-small { + *padding-top: 3px; + *padding-bottom: 3px; +} + +button.btn.btn-mini, +input[type="submit"].btn.btn-mini { + *padding-top: 1px; + *padding-bottom: 1px; +} + +.btn-link, +.btn-link:active, +.btn-link[disabled] { + background-color: transparent; + background-image: none; + -webkit-box-shadow: none; + -moz-box-shadow: none; + box-shadow: none; +} + +.btn-link { + color: #0088cc; + cursor: pointer; + border-color: transparent; + -webkit-border-radius: 0; + -moz-border-radius: 0; + border-radius: 0; +} + +.btn-link:hover { + color: #005580; + text-decoration: underline; + background-color: transparent; +} + +.btn-link[disabled]:hover { + color: #333333; + text-decoration: none; +} + +.btn-disabled { + cursor: default; + background-image: none; + opacity: 0.65; + filter: alpha(opacity=65); + filter: alpha(opacity=35); + -webkit-box-shadow: none; + -moz-box-shadow: none; + box-shadow: none; +} + +.btn-group { + position: relative; + display: inline-block; + *display: inline; + *margin-left: .3em; + font-size: 0; + white-space: nowrap; + vertical-align: middle; + *zoom: 1; +} + +.btn-group:first-child { + *margin-left: 0; +} + +.btn-group { + margin-left: 5px; +} + +.btn-group-first { + /* fix for IE6 */ + + margin-left: 0; +} + +.btn-toolbar { + margin-top: 10px; + margin-bottom: 10px; + font-size: 0; +} + +.btn-toolbar .btn .btn-group { + margin-left: 5px; +} + +.btn-toolbar .btn-first, +.btn-toolbar .btn-group-first { + margin-left: 0; +} + +.btn-group .btn { + position: relative; + -webkit-border-radius: 0; + -moz-border-radius: 0; + border-radius: 0; +} + +.btn-group .btn { + margin-left: -1px; +} + +.btn-group .btn-first { + margin-left: 0; +} + +.btn-group .btn, +.btn-group .dropdown-menu { + font-size: 14px; +} + +.btn-group .dropdown-menu .divider { + _margin: 5px 0 -5px; +} + +.btn-group .btn-mini { + font-size: 11px; +} + +.btn-group .btn-small { + font-size: 12px; +} + +.btn-group .btn-large { + font-size: 16px; +} + +.btn-group > .btn:first-child { + margin-left: 0; + -webkit-border-bottom-left-radius: 4px; + border-bottom-left-radius: 4px; + -webkit-border-top-left-radius: 4px; + border-top-left-radius: 4px; + -moz-border-radius-bottomleft: 4px; + -moz-border-radius-topleft: 4px; +} + +.btn-group > .btn:last-child, +.btn-group > .dropdown-toggle { + -webkit-border-top-right-radius: 4px; + border-top-right-radius: 4px; + -webkit-border-bottom-right-radius: 4px; + border-bottom-right-radius: 4px; + -moz-border-radius-topright: 4px; + -moz-border-radius-bottomright: 4px; +} + +.btn-group > .btn.large:first-child { + margin-left: 0; + -webkit-border-bottom-left-radius: 6px; + border-bottom-left-radius: 6px; + -webkit-border-top-left-radius: 6px; + border-top-left-radius: 6px; + -moz-border-radius-bottomleft: 6px; + -moz-border-radius-topleft: 6px; +} + +.btn-group > .btn.large:last-child, +.btn-group > .large.dropdown-toggle { + -webkit-border-top-right-radius: 6px; + border-top-right-radius: 6px; + -webkit-border-bottom-right-radius: 6px; + border-bottom-right-radius: 6px; + -moz-border-radius-topright: 6px; + -moz-border-radius-bottomright: 6px; +} + +.btn-group > .btn:hover, +.btn-group > .btn:focus, +.btn-group > .btn:active, +.btn-group > .btn.active { + z-index: 2; +} + +.btn-group .dropdown-toggle:active, +.btn-group.open .dropdown-toggle { + outline: 0; +} + +.btn-group .dropdown-toggle { + padding-right: 9px; + padding-left: 9px; + -webkit-box-shadow: inset 1px 0 0 rgba(255, 255, 255, 0.125), inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05); + -moz-box-shadow: inset 1px 0 0 rgba(255, 255, 255, 0.125), inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05); + box-shadow: inset 1px 0 0 rgba(255, 255, 255, 0.125), inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05); +} + +.btn-group .btn-mini-dropdown-toggle { + padding-right: 7px; + padding-left: 7px; +} + +.btn-group .btn-large-dropdown-toggle { + padding-right: 17px; + padding-left: 17px; +} + +.btn-group-open .dropdown-toggle { + background-image: none; + -webkit-box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05); + -moz-box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05); + box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05); +} + +.btn-group-open .btn-dropdown-toggle { + background-color: #e6e6e6; +} + +.btn-group-open .btn-primary-dropdown-toggle { + background-color: #0044cc; +} + +.btn-group-open .btn-warning-dropdown-toggle { + background-color: #f89406; +} + +.btn-group-open .btn-danger-dropdown-toggle { + background-color: #bd362f; +} + +.btn-group-open .btn-success-dropdown-toggle { + background-color: #51a351; +} + +.btn-group-open .btn-info-dropdown-toggle { + background-color: #2f96b4; +} + +.btn-group-open .btn-inverse-dropdown-toggle { + background-color: #222222; +} + +.btn .caret { + _display: inline; + _height: 8px; + margin-top: 8px; + margin-left: 0; + _vertical-align: middle; + _zoom: 1; +} + +.btn-mini .caret, +.btn-small .caret { + _height: 10px; +} + +.btn-mini .caret, +.btn-small .caret, +.btn-large .caret { + margin-top: 6px; +} + +.btn-large .caret { + _height: 9px; + border-top-width: 5px; + border-right-width: 5px; + border-left-width: 5px; +} + +.dropup .btn-large .caret { + border-bottom-width: 5px; +} + +.btn-primary .caret, +.btn-warning .caret, +.btn-danger .caret, +.btn-info .caret, +.btn-success .caret, +.btn-inverse .caret { + border-top-color: #ffffff; + border-bottom-color: #ffffff; +} + +.btn-group-vertical { + display: inline-block; + *display: inline; + /* IE7 inline-block hack */ + + *zoom: 1; +} + +.btn-group-vertical .btn { + display: block; + float: none; + width: 100%; + -webkit-border-radius: 0; + -moz-border-radius: 0; + border-radius: 0; +} + +.btn-group-vertical .btn + .btn { + margin-top: -1px; + margin-left: 0; +} + +.btn-group-vertical .btn:first-child { + -webkit-border-radius: 4px 4px 0 0; + -moz-border-radius: 4px 4px 0 0; + border-radius: 4px 4px 0 0; +} + +.btn-group-vertical .btn:last-child { + -webkit-border-radius: 0 0 4px 4px; + -moz-border-radius: 0 0 4px 4px; + border-radius: 0 0 4px 4px; +} + +.btn-group-vertical .btn-large:first-child { + -webkit-border-radius: 6px 6px 0 0; + -moz-border-radius: 6px 6px 0 0; + border-radius: 6px 6px 0 0; +} + +.btn-group-vertical .btn-large:last-child { + -webkit-border-radius: 0 0 6px 6px; + -moz-border-radius: 0 0 6px 6px; + border-radius: 0 0 6px 6px; +} + +.alert { + padding: 8px 35px 8px 14px; + margin-bottom: 20px; + color: #c09853; + text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5); + background-color: #fcf8e3; + border: 1px solid #fbeed5; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; +} + +.alert h4 { + *display: inline; + margin: 0; + *zoom: 1; +} + +.alert .close { + position: relative; + top: -2px; + right: -21px; + line-height: 20px; +} + +.alert-success { + color: #468847; + background-color: #dff0d8; + border-color: #d6e9c6; +} + +.alert-danger, +.alert-error { + color: #b94a48; + background-color: #f2dede; + border-color: #eed3d7; +} + +.alert-info { + color: #3a87ad; + background-color: #d9edf7; + border-color: #bce8f1; +} + +.alert-block { + padding-top: 14px; + padding-bottom: 14px; +} + +.alert-block p, +.alert-block ul { + margin-bottom: 0; +} + +.alert-block p + p { + margin-top: 5px; +} + +.nav { + margin-bottom: 20px; + margin-left: 0; + list-style: none; +} + +.nav li a { + display: block; + *zoom: 1; +} + +.nav * li a { + display: inline; +} + +.nav li * a { + display: inline; +} + +.nav li a:hover { + text-decoration: none; + background-color: #eeeeee; +} + +.nav * li a:hover { + text-decoration: inherit; + background-color: inherit; +} + +.nav li * a:hover { + text-decoration: inherit; + background-color: inherit; +} + +.nav > .pull-right { + float: right; +} + +.nav-header { + display: block; + padding: 3px 15px; + font-size: 11px; + font-weight: bold; + line-height: 20px; + color: #999999; + text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5); + text-transform: uppercase; + *zoom: 1; +} + +.nav .nav-header { + margin-top: 9px; +} + +.nav-list { + padding-right: 15px; + padding-left: 15px; + margin-bottom: 0; +} + +.nav-list li a, +.nav-list .nav-header { + margin-right: -15px; + margin-left: -15px; + text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5); +} + +.nav-list li a { + padding: 3px 15px; +} + +.nav-list * li a { + padding: 0; + margin-right: 0; + margin-left: 0; + text-shadow: none; +} + +.nav-list li * a { + padding: 0; + margin-right: 0; + margin-left: 0; + text-shadow: none; +} + +.nav-list .active a, +.nav-list .active a:hover { + color: #ffffff; + text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.2); + background-color: #0088cc; +} + +.nav-list * .active a, +.nav-list * .active a:hover { + color: inherit; + text-shadow: none; + background-color: inherit; +} + +.nav-list .active * a, +.nav-list .active * a:hover { + color: inherit; + text-shadow: none; + background-color: inherit; +} + +.nav-list [class^="icon-"], +.nav-list [class*=" icon-"] { + margin-right: 2px; +} + +.nav-list .divider { + *width: 100%; + height: 1px; + margin: 9px 1px; + *margin: -5px 0 5px; + overflow: hidden; + background-color: #e5e5e5; + border-bottom: 1px solid #ffffff; +} + +.nav-tabs, +.nav-pills { + *zoom: 1; +} + +.nav-tabs:before, +.nav-pills:before, +.nav-tabs:after, +.nav-pills:after { + display: table; + line-height: 0; + content: ""; +} + +.nav-tabs:after, +.nav-pills:after { + clear: both; +} + +.nav-tabs li, +.nav-pills li { + float: left; +} + +.nav-tabs * li, +.nav-pills * li { + float: none; +} + +.nav-tabs li a, +.nav-pills li a { + padding-right: 12px; + padding-left: 12px; + margin-right: 2px; + line-height: 14px; +} + +.nav-tabs * li a, +.nav-pills * li a { + padding: 0; + margin: 0; + line-height: auto; +} + +.nav-tabs { + border-bottom: 1px solid #ddd; +} + +.nav-tabs li { + margin-bottom: -1px; + *background-color: #ffffff; + *zoom: 1; +} + +.nav-tabs * li { + zoom: normal; +} + +.nav-tabs li a { + *display: inline; + padding-top: 8px; + padding-bottom: 8px; + line-height: 20px; + *background-color: #fffffe; + border: 1px solid transparent; + *border: 1px solid #fffffe; + -webkit-border-radius: 4px 4px 0 0; + -moz-border-radius: 4px 4px 0 0; + border-radius: 4px 4px 0 0; + *filter: chroma(color=#fffffe); + *zoom: 1; +} + +.nav-tabs li a:hover { + border-color: #eeeeee #eeeeee #dddddd; +} + +.nav-tabs * li a { + padding: 0; + line-height: auto; + background-color: inherit; + border: none; + filter: none; + zoom: normal; +} + +.nav-tabs .active a, +.nav-tabs .active a:hover { + color: #555555; + cursor: default; + background-color: #ffffff; + border: 1px solid #ddd; + *border: 1px solid #999; + border-bottom-color: transparent; +} + +.nav-tabs * .active a, +.nav-tabs * .active a:hover { + color: inherit; + background-color: inherit; + border: none; +} + +.nav-pills li { + *background-color: #ffffff; + *zoom: 1; +} + +.nav-pills * li { + background-color: inherit; + zoom: normal; +} + +.nav-pills li a { + *display: inline; + padding-top: 8px; + padding-bottom: 8px; + margin-top: 2px; + margin-bottom: 2px; + -webkit-border-radius: 5px; + -moz-border-radius: 5px; + border-radius: 5px; + *zoom: 1; +} + +.nav-pills * li a { + padding: 0; + margin: 0; + -webkit-border-radius: none; + -moz-border-radius: none; + border-radius: none; + zoom: normal; +} + +.nav-pills .active a, +.nav-pills .active a:hover { + color: #ffffff; + background-color: #0088cc; +} + +.nav-pills * .active a, +.nav-pills * .active a:hover { + color: inherit; + background-color: inherit; +} + +.nav-stacked > li { + float: none; +} + +.nav-stacked > li > a { + margin-right: 0; +} + +.nav-tabs.nav-stacked { + border-bottom: 0; +} + +.nav-tabs.nav-stacked > li > a { + border: 1px solid #ddd; + -webkit-border-radius: 0; + -moz-border-radius: 0; + border-radius: 0; +} + +.nav-tabs.nav-stacked > li:first-child > a { + -webkit-border-top-right-radius: 4px; + border-top-right-radius: 4px; + -webkit-border-top-left-radius: 4px; + border-top-left-radius: 4px; + -moz-border-radius-topright: 4px; + -moz-border-radius-topleft: 4px; +} + +.nav-tabs.nav-stacked > li:last-child > a { + -webkit-border-bottom-right-radius: 4px; + border-bottom-right-radius: 4px; + -webkit-border-bottom-left-radius: 4px; + border-bottom-left-radius: 4px; + -moz-border-radius-bottomright: 4px; + -moz-border-radius-bottomleft: 4px; +} + +.nav-tabs.nav-stacked > li > a:hover { + z-index: 2; + border-color: #ddd; +} + +.nav-pills.nav-stacked > li > a { + margin-bottom: 3px; +} + +.nav-pills.nav-stacked > li:last-child > a { + margin-bottom: 1px; +} + +.nav-tabs .dropdown-menu { + -webkit-border-radius: 0 0 6px 6px; + -moz-border-radius: 0 0 6px 6px; + border-radius: 0 0 6px 6px; + /* fix for IE6 li:hover */ + +} + +.nav-tabs .dropdown-menu .divider { + *width: 100%; + height: 1px; + margin: 9px 1px; + *margin: -5px 0 5px; + overflow: hidden; + background-color: #e5e5e5; + border-bottom: 1px solid #ffffff; +} + +.nav-tabs .dropdown-menu li a { + display: block; + padding: 3px 20px; + clear: both; + font-weight: normal; + line-height: 20px; + color: #333333; + white-space: nowrap; + *zoom: 1; +} + +.nav-tabs .dropdown-menu li a:hover, +.nav-tabs .dropdown-menu li a:focus, +.nav-tabs .dropdown-menu .dropdown-submenu:hover a, +.nav-tabs .dropdown-menu .dropdown-submenu-hover a { + color: #ffffff; + text-decoration: none; + background-color: #0081c2; + background-image: -moz-linear-gradient(top, #0088cc, #0077b3); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#0088cc), to(#0077b3)); + background-image: -webkit-linear-gradient(top, #0088cc, #0077b3); + background-image: -o-linear-gradient(top, #0088cc, #0077b3); + background-image: linear-gradient(to bottom, #0088cc, #0077b3); + background-repeat: repeat-x; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff0088cc', endColorstr='#ff0077b3', GradientType=0); +} + +.nav-tabs .dropdown-menu .dropdown-submenu-hover * a { + *color: inherit; + *background-color: #fffffe; + *background-image: none; + *filter: none; + *filter: chroma(color=#fffffe); +} + +.nav-pills .dropdown-menu { + -webkit-border-radius: 6px; + -moz-border-radius: 6px; + border-radius: 6px; + /* fix for IE6 li:hover */ + +} + +.nav-pills .dropdown-menu .divider { + *width: 100%; + height: 1px; + margin: 9px 1px; + *margin: -5px 0 5px; + overflow: hidden; + background-color: #e5e5e5; + border-bottom: 1px solid #ffffff; +} + +.nav-pills .dropdown-menu li a { + display: block; + padding: 3px 20px; + clear: both; + font-weight: normal; + line-height: 20px; + color: #333333; + white-space: nowrap; + *zoom: 1; +} + +.nav-pills .dropdown-menu li a:hover, +.nav-pills .dropdown-menu li a:focus, +.nav-pills .dropdown-menu .dropdown-submenu:hover a, +.nav-pills .dropdown-menu .dropdown-submenu-hover a { + color: #ffffff; + text-decoration: none; + background-color: #0081c2; + background-image: -moz-linear-gradient(top, #0088cc, #0077b3); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#0088cc), to(#0077b3)); + background-image: -webkit-linear-gradient(top, #0088cc, #0077b3); + background-image: -o-linear-gradient(top, #0088cc, #0077b3); + background-image: linear-gradient(to bottom, #0088cc, #0077b3); + background-repeat: repeat-x; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff0088cc', endColorstr='#ff0077b3', GradientType=0); +} + +.nav-pills .dropdown-menu .dropdown-submenu-hover * a { + *color: inherit; + *background-color: #fffffe; + *background-image: none; + *filter: none; + *filter: chroma(color=#fffffe); +} + +.nav-tabs .dropdown-toggle { + *padding-top: 10px; + *padding-bottom: 12px; +} + +.nav-pills .dropdown-toggle { + *padding-top: 8px; + *padding-bottom: 6px; + *margin-bottom: 3px; +} + +.nav .dropdown-toggle .caret { + width: 0; + height: 0; + margin-top: 6px; + line-height: 0; + border-color: #0088cc transparent transparent; + *border-color: #0088cc #fffffe #fffffe; + border-top-color: #0088cc; + border-bottom-color: #0088cc; + border-style: solid; + border-width: 4px 4px 0; + *filter: progid:DXImageTransform.Microsoft.Chroma(color=#fffffe); +} + +.nav .dropdown-toggle:hover .caret { + border-top-color: #005580; + border-bottom-color: #005580; +} + +/* move down carets for tabs */ + +.nav-tabs .dropdown-toggle .caret { + margin-top: 8px; +} + +.nav .active .dropdown-toggle .caret { + border-top-color: #fff; + border-bottom-color: #fff; +} + +.nav-tabs .active .dropdown-toggle .caret { + border-top-color: #555555; + border-bottom-color: #555555; +} + +.nav .dropdown.active a:hover { + cursor: pointer; +} + +.nav-tabs .open .dropdown-toggle, +.nav-pills .open .dropdown-toggle, +.nav li.dropdown-open.active a:hover { + color: #ffffff; + background-color: #999999; + border-color: #999999; +} + +.nav li.dropdown-open .caret, +.nav li.dropdown-open.active .caret, +.nav li.dropdown-open a:hover .caret { + width: 0; + height: 0; + line-height: 0; + border-color: #ffffff transparent transparent; + *border-color: #ffffff #fffffe #fffffe; + border-top-color: #ffffff; + border-bottom-color: #ffffff; + border-style: solid; + border-width: 4px 4px 0; + opacity: 1; + filter: alpha(opacity=100); + *filter: progid:DXImageTransform.Microsoft.Chroma(color=#fffffe); +} + +.tabs-stacked .open > a:hover { + border-color: #999999; +} + +.tabbable { + *zoom: 1; +} + +.tabbable:before, +.tabbable:after { + display: table; + line-height: 0; + content: ""; +} + +.tabbable:after { + clear: both; +} + +.tab-content { + overflow: auto; +} + +.tabs-below .nav-tabs, +.tabs-right .nav-tabs, +.tabs-left .nav-tabs { + border-bottom: 0; +} + +.tab-content .tab-pane, +.pill-content .pill-pane { + display: none; + *background-color: #ffffff; + *zoom: 1; +} + +.tab-content .active, +.pill-content .active { + display: block; +} + +.tabs-below .nav-tabs { + border-top: 1px solid #ddd; +} + +.tabs-below * .nav-tabs { + border-top: none; +} + +.tabs-below .nav-tabs li { + margin-top: -1px; + margin-bottom: 0; +} + +.tabs-below * .nav-tabs li { + margin-top: 0; + margin-bottom: -1px; +} + +.tabs-below .nav-tabs li a { + -webkit-border-radius: 0 0 4px 4px; + -moz-border-radius: 0 0 4px 4px; + border-radius: 0 0 4px 4px; +} + +.tabs-below .nav-tabs li a:hover { + border-top-color: #ddd; + border-bottom-color: #fff; +} + +.tabs-below * .nav-tabs li a { + -webkit-border-radius: none; + -moz-border-radius: none; + border-radius: none; +} + +.tabs-below * .nav-tabs li a:hover { + border-top-color: inherit; + border-bottom-color: inherit; +} + +.tabs-below .nav-tabs .active a, +.tabs-below .nav-tabs .active a:hover { + border-color: transparent #999 #999 #999; +} + +.tabs-below * .nav-tabs .active a, +.tabs-below * .nav-tabs .active a:hover { + border-color: inherit; +} + +.tabs-left > .nav-tabs > li, +.tabs-right > .nav-tabs > li { + float: none; +} + +.tabs-left > .nav-tabs > li > a, +.tabs-right > .nav-tabs > li > a { + min-width: 74px; + margin-right: 0; + margin-bottom: 3px; +} + +.tabs-left > .nav-tabs { + float: left; + margin-right: 19px; + border-right: 1px solid #ddd; +} + +.tabs-left > .nav-tabs > li > a { + margin-right: -1px; + -webkit-border-radius: 4px 0 0 4px; + -moz-border-radius: 4px 0 0 4px; + border-radius: 4px 0 0 4px; +} + +.tabs-left > .nav-tabs > li > a:hover { + border-color: #eeeeee #dddddd #eeeeee #eeeeee; +} + +.tabs-left > .nav-tabs .active > a, +.tabs-left > .nav-tabs .active > a:hover { + border-color: #ddd transparent #ddd #ddd; + *border-right-color: #ffffff; +} + +.tabs-right > .nav-tabs { + float: right; + margin-left: 19px; + border-left: 1px solid #ddd; +} + +.tabs-right > .nav-tabs > li > a { + margin-left: -1px; + -webkit-border-radius: 0 4px 4px 0; + -moz-border-radius: 0 4px 4px 0; + border-radius: 0 4px 4px 0; +} + +.tabs-right > .nav-tabs > li > a:hover { + border-color: #eeeeee #eeeeee #eeeeee #dddddd; +} + +.tabs-right > .nav-tabs .active > a, +.tabs-right > .nav-tabs .active > a:hover { + border-color: #ddd #ddd #ddd transparent; + *border-left-color: #ffffff; +} + +.nav .disabled a { + color: #999999; +} + +.nav * .disabled a { + color: inherit; +} + +.nav .disabled a:hover { + text-decoration: none; + cursor: default; + background-color: transparent; +} + +.nav * .disabled a:hover { + text-decoration: underline; + cursor: default; + background-color: inherit; +} + +.navbar { + *position: relative; + *z-index: 2; + margin-bottom: 20px; + overflow: visible; + color: #777777; +} + +.navbar-inner { + _height: 40px; + min-height: 40px; + padding-right: 20px; + padding-left: 20px; + background-color: #fafafa; + background-image: -moz-linear-gradient(top, #ffffff, #f2f2f2); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#ffffff), to(#f2f2f2)); + background-image: -webkit-linear-gradient(top, #ffffff, #f2f2f2); + background-image: -o-linear-gradient(top, #ffffff, #f2f2f2); + background-image: linear-gradient(to bottom, #ffffff, #f2f2f2); + background-repeat: repeat-x; + border: 1px solid #d4d4d4; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#fff2f2f2', GradientType=0); + *zoom: 1; + -webkit-box-shadow: 0 1px 4px rgba(0, 0, 0, 0.065); + -moz-box-shadow: 0 1px 4px rgba(0, 0, 0, 0.065); + box-shadow: 0 1px 4px rgba(0, 0, 0, 0.065); +} + +.navbar-inner:before, +.navbar-inner:after { + display: table; + line-height: 0; + content: ""; +} + +.navbar-inner:after { + clear: both; +} + +.navbar .container { + width: auto; +} + +.nav-collapse.collapse { + height: auto; + overflow: visible; +} + +.navbar .brand { + display: block; + float: left; + padding: 10px 20px 10px; + margin-left: -20px; + _margin-left: -10px; + font-size: 20px; + font-weight: 200; + color: #777777; + text-shadow: 0 1px 0 #ffffff; +} + +.navbar .brand:hover { + text-decoration: none; +} + +.navbar .container-fluid .brand { + _margin-left: -10px; +} + +.navbar-text { + margin-bottom: 0; + line-height: 40px; +} + +.navbar-link { + color: #777777; +} + +.navbar-link:hover { + color: #333333; +} + +.navbar .divider-vertical { + height: 40px; + margin: 0 9px; + border-right: 1px solid #ffffff; + border-left: 1px solid #f2f2f2; +} + +.navbar .btn, +.navbar .btn-group { + margin-top: 5px; +} + +.navbar .btn-group .btn, +.navbar .input-prepend .btn, +.navbar .input-append .btn { + margin-top: 0; +} + +.navbar-form { + margin-bottom: 0; + *zoom: 1; +} + +.navbar-form:before, +.navbar-form:after { + display: table; + line-height: 0; + content: ""; +} + +.navbar-form:after { + clear: both; +} + +.navbar-form input, +.navbar-form select, +.navbar-form .radio, +.navbar-form .checkbox { + margin-top: 5px; +} + +.navbar-form input, +.navbar-form select, +.navbar-form .btn { + display: inline-block; + margin-bottom: 0; +} + +.navbar-form input[type="image"], +.navbar-form input[type="checkbox"], +.navbar-form input[type="radio"] { + margin-top: 3px; +} + +.navbar-form .input-append, +.navbar-form .input-prepend { + margin-top: 6px; + white-space: nowrap; +} + +.navbar-form .input-append input, +.navbar-form .input-prepend input { + margin-top: 0; +} + +.navbar-search { + position: relative; + float: left; + margin-top: 5px; + margin-bottom: 0; +} + +.navbar-search .search-query { + padding: 4px 14px; + margin-bottom: 0; + font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; + font-size: 13px; + font-weight: normal; + line-height: 1; + -webkit-border-radius: 15px; + -moz-border-radius: 15px; + border-radius: 15px; +} + +.navbar-static-top { + position: static; + margin-bottom: 0; +} + +.navbar-static-top .navbar-inner { + -webkit-border-radius: 0; + -moz-border-radius: 0; + border-radius: 0; +} + +.navbar-fixed-top, +.navbar-fixed-bottom { + position: fixed; + right: 0; + left: 0; + z-index: 1030; + margin-bottom: 0; +} + +.navbar-fixed-top .navbar-inner, +.navbar-static-top .navbar-inner { + border-width: 0 0 1px; +} + +.navbar-fixed-bottom .navbar-inner { + border-width: 1px 0 0; +} + +.navbar-fixed-top .navbar-inner, +.navbar-fixed-bottom .navbar-inner { + padding-right: 0; + padding-left: 0; + -webkit-border-radius: 0; + -moz-border-radius: 0; + border-radius: 0; +} + +.navbar-static-top .container, +.navbar-fixed-top .container, +.navbar-fixed-bottom .container { + width: 940px; +} + +.navbar-fixed-top { + top: 0; +} + +.navbar-fixed-top .navbar-inner, +.navbar-static-top .navbar-inner { + -webkit-box-shadow: 0 1px 10px rgba(0, 0, 0, 0.1); + -moz-box-shadow: 0 1px 10px rgba(0, 0, 0, 0.1); + box-shadow: 0 1px 10px rgba(0, 0, 0, 0.1); +} + +.navbar-fixed-bottom { + bottom: 0; +} + +.navbar-fixed-bottom .navbar-inner { + -webkit-box-shadow: 0 -1px 10px rgba(0, 0, 0, 0.1); + -moz-box-shadow: 0 -1px 10px rgba(0, 0, 0, 0.1); + box-shadow: 0 -1px 10px rgba(0, 0, 0, 0.1); +} + +.navbar .nav { + position: relative; + left: 0; + display: block; + float: left; + margin: 0 10px 0 0; +} + +.navbar .nav.pull-right { + float: right; + margin-right: 0; +} + +.navbar .nav li { + float: left; +} + +.navbar .nav * li { + float: none; +} + +.navbar .nav li a { + *display: inline; + float: none; + padding: 10px 15px 10px; + color: #777777; + text-decoration: none; + text-shadow: 0 1px 0 #ffffff; + *zoom: 1; +} + +.navbar .nav * li a { + padding: 0; + color: inherit; + text-decoration: inherit; + text-shadow: inherit; +} + +.navbar .nav li * a { + padding: 0; + color: inherit; + text-decoration: inherit; + text-shadow: inherit; +} + +.navbar .nav .dropdown-toggle { + /* fix for IE6 */ + + _padding: 12px; +} + +.navbar .nav .dropdown-toggle .caret { + margin-top: 8px; +} + +.navbar .nav li a:focus, +.navbar .nav li a:hover { + color: #333333; + text-decoration: none; + background-color: transparent; +} + +.navbar .nav * li a:focus, +.navbar .nav * li a:hover { + color: inherit; + text-decoration: inherit; + background-color: inherit; +} + +.navbar .nav .active a, +.navbar .nav .active a:hover, +.navbar .nav .active a:focus { + color: #555555; + text-decoration: none; + background-color: #e5e5e5; + -webkit-box-shadow: inset 0 3px 8px rgba(0, 0, 0, 0.125); + -moz-box-shadow: inset 0 3px 8px rgba(0, 0, 0, 0.125); + box-shadow: inset 0 3px 8px rgba(0, 0, 0, 0.125); +} + +.navbar .nav * .active a, +.navbar .nav * .active a:hover, +.navbar .nav * .active a:focus { + color: inherit; + text-decoration: inherit; + background-color: inherit; + -webkit-box-shadow: none; + -moz-box-shadow: none; + box-shadow: none; +} + +.navbar .btn-navbar { + display: none; + float: right; + padding: 7px 10px; + margin-right: 5px; + margin-left: 5px; + color: #ffffff; + text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); + background-color: #ededed; + *background-color: #e5e5e5; + background-image: -moz-linear-gradient(top, #f2f2f2, #e5e5e5); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#f2f2f2), to(#e5e5e5)); + background-image: -webkit-linear-gradient(top, #f2f2f2, #e5e5e5); + background-image: -o-linear-gradient(top, #f2f2f2, #e5e5e5); + background-image: linear-gradient(to bottom, #f2f2f2, #e5e5e5); + background-repeat: repeat-x; + border-color: #e5e5e5 #e5e5e5 #bfbfbf; + border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2f2f2', endColorstr='#ffe5e5e5', GradientType=0); + filter: progid:DXImageTransform.Microsoft.gradient(enabled=false); + -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.075); + -moz-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.075); + box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.075); +} + +.navbar .btn-navbar:hover, +.navbar .btn-navbar:active, +.navbar .btn-navbar.active, +.navbar .btn-navbar.disabled, +.navbar .btn-navbar[disabled] { + color: #ffffff; + background-color: #e5e5e5; + *background-color: #d9d9d9; +} + +.navbar .btn-navbar:active, +.navbar .btn-navbar.active { + background-color: #cccccc \9; +} + +.navbar .btn-navbar .icon-bar { + display: block; + width: 18px; + height: 2px; + background-color: #f5f5f5; + -webkit-border-radius: 1px; + -moz-border-radius: 1px; + border-radius: 1px; + -webkit-box-shadow: 0 1px 0 rgba(0, 0, 0, 0.25); + -moz-box-shadow: 0 1px 0 rgba(0, 0, 0, 0.25); + box-shadow: 0 1px 0 rgba(0, 0, 0, 0.25); +} + +.btn-navbar .icon-bar + .icon-bar { + margin-top: 3px; +} + +.navbar .nav li .dropdown-menu { + /* fix for IE6 li:hover */ + +} + +.navbar .nav li .dropdown-menu:before { + position: absolute; + top: -7px; + left: 9px; + display: inline-block; + border-right: 7px solid transparent; + border-bottom: 7px solid #ccc; + border-left: 7px solid transparent; + border-bottom-color: rgba(0, 0, 0, 0.2); + content: ''; +} + +.navbar .nav li .dropdown-menu:after { + position: absolute; + top: -6px; + left: 10px; + display: inline-block; + border-right: 6px solid transparent; + border-bottom: 6px solid #ffffff; + border-left: 6px solid transparent; + content: ''; +} + +.navbar .nav li .dropdown-menu .divider { + *width: 100%; + height: 1px; + margin: 9px 1px; + *margin: -5px 0 5px; + overflow: hidden; + background-color: #e5e5e5; + border-bottom: 1px solid #ffffff; +} + +.navbar .nav li .dropdown-menu li a { + display: block; + padding: 3px 20px; + clear: both; + font-weight: normal; + line-height: 20px; + color: #333333; + white-space: nowrap; + *zoom: 1; +} + +.navbar .nav li .dropdown-menu li a:hover, +.navbar .nav li .dropdown-menu li a:focus, +.navbar .nav li .dropdown-menu .dropdown-submenu:hover a, +.navbar .nav li .dropdown-menu .dropdown-submenu-hover a { + color: #ffffff; + text-decoration: none; + background-color: #0081c2; + background-image: -moz-linear-gradient(top, #0088cc, #0077b3); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#0088cc), to(#0077b3)); + background-image: -webkit-linear-gradient(top, #0088cc, #0077b3); + background-image: -o-linear-gradient(top, #0088cc, #0077b3); + background-image: linear-gradient(to bottom, #0088cc, #0077b3); + background-repeat: repeat-x; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff0088cc', endColorstr='#ff0077b3', GradientType=0); +} + +.navbar .nav li .dropdown-menu .dropdown-submenu-hover * a { + *color: inherit; + *background-color: #fffffe; + *background-image: none; + *filter: none; + *filter: chroma(color=#fffffe); +} + +.navbar-fixed-bottom .nav li .dropdown-menu:before { + top: auto; + bottom: -7px; + border-top: 7px solid #ccc; + border-bottom: 0; + border-top-color: rgba(0, 0, 0, 0.2); +} + +.navbar-fixed-bottom .nav li .dropdown-menu:after { + top: auto; + bottom: -6px; + border-top: 6px solid #ffffff; + border-bottom: 0; +} + +.navbar .nav li.dropdown.open .dropdown-toggle, +.navbar .nav li.dropdown.active .dropdown-toggle, +.navbar .nav li.dropdown.open.active .dropdown-toggle { + color: #555555; + background-color: #e5e5e5; +} + +.navbar .nav li.dropdown .dropdown-toggle .caret { + border-top-color: #777777; + border-bottom-color: #777777; +} + +.navbar .nav li.dropdown.open .dropdown-toggle .caret, +.navbar .nav li.dropdown.active .dropdown-toggle .caret, +.navbar .nav li.dropdown.open.active .dropdown-toggle .caret { + border-top-color: #555555; + border-bottom-color: #555555; +} + +.navbar .pull-right li .dropdown-menu, +.navbar .nav li .dropdown-menu.pull-right { + right: 0; + left: auto; +} + +.navbar .pull-right li .dropdown-menu:before, +.navbar .nav li .dropdown-menu.pull-right:before { + right: 12px; + left: auto; +} + +.navbar .pull-right li .dropdown-menu:after, +.navbar .nav li .dropdown-menu.pull-right:after { + right: 13px; + left: auto; +} + +.navbar .pull-right li .dropdown-menu .dropdown-menu, +.navbar .nav li .dropdown-menu.pull-right .dropdown-menu { + right: 100%; + left: auto; + margin-right: -1px; + margin-left: 0; + -webkit-border-radius: 6px 0 6px 6px; + -moz-border-radius: 6px 0 6px 6px; + border-radius: 6px 0 6px 6px; +} + +.navbar-inverse { + color: #999999; +} + +.navbar-inverse .navbar-inner { + background-color: #1b1b1b; + background-image: -moz-linear-gradient(top, #222222, #111111); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#222222), to(#111111)); + background-image: -webkit-linear-gradient(top, #222222, #111111); + background-image: -o-linear-gradient(top, #222222, #111111); + background-image: linear-gradient(to bottom, #222222, #111111); + background-repeat: repeat-x; + border-color: #252525; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff222222', endColorstr='#ff111111', GradientType=0); +} + +.navbar-inverse .brand, +.navbar-inverse .nav li a { + color: #999999; + text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); +} + +.navbar-inverse .brand:hover, +.navbar-inverse .nav li a:hover { + color: #ffffff; +} + +.navbar-inverse .nav li a:focus, +.navbar-inverse .nav li a:hover { + color: #ffffff; + background-color: transparent; +} + +.navbar-inverse .nav .active a, +.navbar-inverse .nav .active a:hover, +.navbar-inverse .nav .active a:focus { + color: #ffffff; + background-color: #111111; +} + +.navbar-inverse .navbar-link { + color: #999999; +} + +.navbar-inverse .navbar-link:hover { + color: #ffffff; +} + +.navbar-inverse .divider-vertical { + border-right-color: #222222; + border-left-color: #111111; +} + +.navbar-inverse .nav li.dropdown.open > .dropdown-toggle, +.navbar-inverse .nav li.dropdown.active > .dropdown-toggle, +.navbar-inverse .nav li.dropdown.open.active > .dropdown-toggle { + color: #ffffff; + background-color: #111111; +} + +.navbar-inverse .nav li.dropdown > .dropdown-toggle .caret { + border-top-color: #999999; + border-bottom-color: #999999; +} + +.navbar-inverse .nav li.dropdown.open > .dropdown-toggle .caret, +.navbar-inverse .nav li.dropdown.active > .dropdown-toggle .caret, +.navbar-inverse .nav li.dropdown.open.active > .dropdown-toggle .caret { + border-top-color: #ffffff; + border-bottom-color: #ffffff; +} + +.navbar-inverse .navbar-search .search-query { + color: #ffffff; + background-color: #515151; + border-color: #111111; + -webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1), 0 1px 0 rgba(255, 255, 255, 0.15); + -moz-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1), 0 1px 0 rgba(255, 255, 255, 0.15); + box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1), 0 1px 0 rgba(255, 255, 255, 0.15); + -webkit-transition: none; + -moz-transition: none; + -o-transition: none; + transition: none; +} + +.navbar-inverse .navbar-search .search-query:-moz-placeholder { + color: #cccccc; +} + +.navbar-inverse .navbar-search .search-query:-ms-input-placeholder { + color: #cccccc; +} + +.navbar-inverse .navbar-search .search-query::-webkit-input-placeholder { + color: #cccccc; +} + +.navbar-inverse .navbar-search .search-query:focus, +.navbar-inverse .navbar-search .search-query.focused { + padding: 5px 15px; + color: #333333; + text-shadow: 0 1px 0 #ffffff; + background-color: #ffffff; + border: 0; + outline: 0; + -webkit-box-shadow: 0 0 3px rgba(0, 0, 0, 0.15); + -moz-box-shadow: 0 0 3px rgba(0, 0, 0, 0.15); + box-shadow: 0 0 3px rgba(0, 0, 0, 0.15); +} + +.navbar-inverse .btn-navbar { + color: #ffffff; + text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); + background-color: #0e0e0e; + *background-color: #040404; + background-image: -moz-linear-gradient(top, #151515, #040404); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#151515), to(#040404)); + background-image: -webkit-linear-gradient(top, #151515, #040404); + background-image: -o-linear-gradient(top, #151515, #040404); + background-image: linear-gradient(to bottom, #151515, #040404); + background-repeat: repeat-x; + border-color: #040404 #040404 #000000; + border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff151515', endColorstr='#ff040404', GradientType=0); + filter: progid:DXImageTransform.Microsoft.gradient(enabled=false); +} + +.navbar-inverse .btn-navbar:hover, +.navbar-inverse .btn-navbar:active, +.navbar-inverse .btn-navbar.active, +.navbar-inverse .btn-navbar.disabled, +.navbar-inverse .btn-navbar[disabled] { + color: #ffffff; + background-color: #040404; + *background-color: #000000; +} + +.navbar-inverse .btn-navbar:active, +.navbar-inverse .btn-navbar.active { + background-color: #000000 \9; +} + +.breadcrumb { + padding: 8px 15px; + margin: 0 0 20px; + list-style: none; + background-color: #f5f5f5; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; +} + +.breadcrumb li { + display: inline-block; + *display: inline; + text-shadow: 0 1px 0 #ffffff; + *zoom: 1; +} + +.breadcrumb .divider { + padding: 0 5px; + color: #ccc; +} + +.breadcrumb .active { + color: #999999; + background-color: #f5f5f5; +} + +.pagination { + margin: 20px 0; +} + +.pagination ul { + display: inline-block; + *display: inline; + margin-bottom: 0; + margin-left: 0; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; + *zoom: 1; + -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05); + -moz-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05); + box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05); +} + +.pagination ul li { + display: inline; +} + +.pagination ul li a, +.pagination ul li span { + float: left; + padding: 4px 12px; + line-height: 20px; + text-decoration: none; + background-color: #ffffff; + border: 1px solid #dddddd; + border-left-width: 0; +} + +.pagination ul li a:hover, +.pagination ul .active a, +.pagination ul .active span { + background-color: #f5f5f5; +} + +.pagination ul .active a, +.pagination ul .active span { + color: #999999; + cursor: default; +} + +.pagination ul .disabled span, +.pagination ul .disabled a, +.pagination ul .disabled a:hover { + color: #999999; + cursor: default; + background-color: transparent; +} + +.pagination ul li.first-child a, +.pagination ul li.first-child span { + border-left-width: 1px; + -webkit-border-bottom-left-radius: 4px; + border-bottom-left-radius: 4px; + -webkit-border-top-left-radius: 4px; + border-top-left-radius: 4px; + -moz-border-radius-bottomleft: 4px; + -moz-border-radius-topleft: 4px; +} + +.pagination ul li:last-child a, +.pagination ul li:last-child span { + -webkit-border-top-right-radius: 4px; + border-top-right-radius: 4px; + -webkit-border-bottom-right-radius: 4px; + border-bottom-right-radius: 4px; + -moz-border-radius-topright: 4px; + -moz-border-radius-bottomright: 4px; +} + +.pagination-centered { + text-align: center; +} + +.pagination-right { + text-align: right; +} + +.pagination-large ul li a, +.pagination-large ul li span { + padding: 11px 19px; + font-size: 17.5px; +} + +.pagination-large ul li:first-child a, +.pagination-large ul li:first-child span { + -webkit-border-bottom-left-radius: 6px; + border-bottom-left-radius: 6px; + -webkit-border-top-left-radius: 6px; + border-top-left-radius: 6px; + -moz-border-radius-bottomleft: 6px; + -moz-border-radius-topleft: 6px; +} + +.pagination-large ul li:last-child a, +.pagination-large ul li:last-child span { + -webkit-border-top-right-radius: 6px; + border-top-right-radius: 6px; + -webkit-border-bottom-right-radius: 6px; + border-bottom-right-radius: 6px; + -moz-border-radius-topright: 6px; + -moz-border-radius-bottomright: 6px; +} + +.pagination-mini ul li:first-child a, +.pagination-small ul li:first-child a, +.pagination-mini ul li:first-child span, +.pagination-small ul li:first-child span { + -webkit-border-bottom-left-radius: 3px; + border-bottom-left-radius: 3px; + -webkit-border-top-left-radius: 3px; + border-top-left-radius: 3px; + -moz-border-radius-bottomleft: 3px; + -moz-border-radius-topleft: 3px; +} + +.pagination-mini ul li:last-child a, +.pagination-small ul li:last-child a, +.pagination-mini ul li:last-child span, +.pagination-small ul li:last-child span { + -webkit-border-top-right-radius: 3px; + border-top-right-radius: 3px; + -webkit-border-bottom-right-radius: 3px; + border-bottom-right-radius: 3px; + -moz-border-radius-topright: 3px; + -moz-border-radius-bottomright: 3px; +} + +.pagination-small ul li a, +.pagination-small ul li span { + padding: 2px 10px; + font-size: 11.9px; +} + +.pagination-mini ul li a, +.pagination-mini ul li span { + padding: 1px 6px; + font-size: 10.5px; +} + +.pager { + margin: 20px 0; + text-align: center; + list-style: none; + *zoom: 1; +} + +.pager:before, +.pager:after { + display: table; + line-height: 0; + content: ""; +} + +.pager:after { + clear: both; +} + +.pager li { + display: inline; +} + +.pager li a, +.pager li span { + display: inline-block; + padding: 5px 14px; + background-color: #fff; + border: 1px solid #ddd; + -webkit-border-radius: 15px; + -moz-border-radius: 15px; + border-radius: 15px; +} + +.pager li a:hover { + text-decoration: none; + background-color: #f5f5f5; +} + +.pager .next a, +.pager .next span { + float: right; +} + +.pager .previous a, +.pager .previous span { + float: left; +} + +.pager .disabled a, +.pager .disabled a:hover, +.pager .disabled span { + color: #999999; + cursor: default; + background-color: #fff; +} + +.tooltip { + position: absolute; + z-index: 1030; + display: block; + padding: 5px; + font-size: 11px; + opacity: 0; + filter: alpha(opacity=0); + visibility: visible; +} + +.tooltip.in { + opacity: 0.8; + filter: alpha(opacity=80); +} + +.tooltip.top { + margin-top: -3px; +} + +.tooltip.right { + margin-left: 3px; +} + +.tooltip.bottom { + margin-top: 3px; +} + +.tooltip.left { + margin-left: -3px; +} + +.tooltip-inner { + max-width: 200px; + padding: 3px 8px; + color: #ffffff; + text-align: center; + text-decoration: none; + background-color: #000000; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; +} + +.tooltip-arrow { + position: absolute; + width: 0; + height: 0; + border-color: transparent; + border-style: solid; +} + +.tooltip.top .tooltip-arrow { + bottom: 0; + left: 50%; + width: 0; + height: 0; + margin-left: -5px; + line-height: 0; + border-color: #000000 transparent transparent; + *border-color: #000000 #fffffe #fffffe; + border-top-color: #000000; + border-style: solid; + border-width: 5px 5px 0; + *filter: progid:DXImageTransform.Microsoft.Chroma(color=#fffffe); +} + +.tooltip.right .tooltip-arrow { + top: 50%; + left: 0; + width: 0; + height: 0; + margin-top: -5px; + line-height: 0; + border-color: transparent #000000 transparent transparent; + *border-color: #fffffe #000000 #fffffe #fffffe; + border-right-color: #000000; + border-style: solid; + border-width: 5px 5px 5px 0; + *filter: progid:DXImageTransform.Microsoft.Chroma(color=#fffffe); +} + +.tooltip.left .tooltip-arrow { + top: 50%; + right: 0; + width: 0; + height: 0; + margin-top: -5px; + line-height: 0; + border-color: transparent transparent transparent #000000; + *border-color: #fffffe #fffffe #fffffe #000000; + border-left-color: #000000; + border-style: solid; + border-width: 5px 0 5px 5px; + *filter: progid:DXImageTransform.Microsoft.Chroma(color=#fffffe); +} + +.tooltip.bottom .tooltip-arrow { + top: 0; + left: 50%; + width: 0; + height: 0; + margin-left: -5px; + line-height: 0; + border-color: transparent transparent #000000; + *border-color: #fffffe #fffffe #000000; + border-bottom-color: #000000; + border-style: solid; + border-width: 0 5px 5px; + *filter: progid:DXImageTransform.Microsoft.Chroma(color=#fffffe); +} + +.popover { + position: absolute; + top: 0; + left: 0; + z-index: 1010; + display: none; + width: 236px; + padding: 1px; + background-color: #ffffff; + border: 1px solid #ccc; + border: 1px solid rgba(0, 0, 0, 0.2); + -webkit-border-radius: 6px; + -moz-border-radius: 6px; + border-radius: 6px; + -webkit-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2); + -moz-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2); + box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2); + -webkit-background-clip: padding-box; + -moz-background-clip: padding; + background-clip: padding-box; +} + +.popover.top { + margin-top: -10px; +} + +.popover.right { + margin-left: 10px; +} + +.popover.bottom { + margin-top: 10px; +} + +.popover.left { + margin-left: -10px; +} + +.popover-title { + padding: 8px 14px; + margin: 0; + font-size: 14px; + font-weight: normal; + line-height: 18px; + background-color: #f7f7f7; + border-bottom: 1px solid #ebebeb; + -webkit-border-radius: 5px 5px 0 0; + -moz-border-radius: 5px 5px 0 0; + border-radius: 5px 5px 0 0; +} + +.popover-content { + padding: 9px 14px; +} + +.popover-content p, +.popover-content ul, +.popover-content ol { + margin-bottom: 0; +} + +.popover .arrow, +.popover .arrow-after { + position: absolute; + display: inline-block; + *display: inline; + width: 0; + height: 0; + border-color: transparent; + border-style: solid; + *zoom: 1; +} + +.popover .arrow-after { + z-index: -1; + content: ""; +} + +.popover.top .arrow { + *bottom: -12px; + left: 50%; + width: 0; + height: 0; + margin-left: -10px; + line-height: 0; + border-color: #cccccc transparent transparent; + *border-color: #cccccc #fffffe #fffffe; + border-top-color: #ffffff; + border-style: solid; + border-width: 10px 10px 0; + *filter: progid:DXImageTransform.Microsoft.Chroma(color=#fffffe); +} + +.popover.top .arrow.arrow-after { + bottom: -1px; + left: -11px; + border-top-color: rgba(0, 0, 0, 0.25); + border-width: 11px 11px 0; +} + +.popover.right .arrow { + top: 50%; + *left: -10px; + width: 0; + height: 0; + margin-top: -10px; + line-height: 0; + border-color: transparent #cccccc transparent transparent; + *border-color: #fffffe #cccccc #fffffe #fffffe; + border-right-color: #ffffff; + border-style: solid; + border-width: 10px 10px 10px 0; + *filter: progid:DXImageTransform.Microsoft.Chroma(color=#fffffe); +} + +.popover.right .arrow-after { + bottom: -11px; + left: -1px; + border-right-color: rgba(0, 0, 0, 0.25); + border-width: 11px 11px 11px 0; +} + +.popover.bottom .arrow { + *top: -10px; + left: 50%; + width: 0; + height: 0; + margin-left: -10px; + line-height: 0; + border-color: transparent transparent #cccccc; + *border-color: #fffffe #fffffe #cccccc; + border-bottom-color: #ffffff; + border-style: solid; + border-width: 0 10px 10px; + *filter: progid:DXImageTransform.Microsoft.Chroma(color=#fffffe); +} + +.popover.bottom .arrow-after { + top: -1px; + left: -11px; + border-bottom-color: rgba(0, 0, 0, 0.25); + border-width: 0 11px 11px; +} + +.popover.left .arrow { + top: 50%; + *right: -10px; + width: 0; + height: 0; + margin-top: -10px; + line-height: 0; + border-color: transparent transparent transparent #cccccc; + *border-color: #fffffe #fffffe #fffffe #cccccc; + border-left-color: #ffffff; + border-style: solid; + border-width: 10px 0 10px 10px; + *filter: progid:DXImageTransform.Microsoft.Chroma(color=#fffffe); +} + +.popover.left .arrow-after { + right: -1px; + bottom: -11px; + border-left-color: rgba(0, 0, 0, 0.25); + border-width: 11px 0 11px 11px; +} + +.carousel { + position: relative; + margin-bottom: 20px; + line-height: 1; +} + +.carousel-inner { + position: relative; + width: 100%; + overflow: hidden; +} + +.carousel .item { + position: relative; + display: none; + -webkit-transition: 0.6s ease-in-out left; + -moz-transition: 0.6s ease-in-out left; + -o-transition: 0.6s ease-in-out left; + transition: 0.6s ease-in-out left; +} + +.carousel .item img { + display: block; + *width: 100%; + *height: auto; + line-height: 1; +} + +.carousel .active, +.carousel .next, +.carousel .prev { + display: block; +} + +.carousel .active { + left: 0; +} + +.carousel .next, +.carousel .prev { + position: absolute; + top: 0; + width: 100%; +} + +.carousel .next { + left: 100%; +} + +.carousel .prev { + left: -100%; +} + +.carousel .next.left, +.carousel .prev.right { + left: 0; +} + +.carousel .active.left { + left: -100%; +} + +.carousel .active.right { + left: 100%; +} + +.carousel-control { + position: absolute; + top: 40%; + left: 15px; + width: 40px; + height: 40px; + margin-top: -20px; + font-size: 60px; + font-weight: 100; + line-height: 30px; + color: #ffffff; + text-align: center; + background: #222222; + border: 3px solid #ffffff; + -webkit-border-radius: 23px; + -moz-border-radius: 23px; + border-radius: 23px; + opacity: 0.5; + filter: alpha(opacity=50); +} + +.carousel-control-right { + right: 15px; + left: auto; +} + +.carousel-control-left { + left: 15px; +} + +.carousel-control:hover { + color: #ffffff; + text-decoration: none; + opacity: 0.9; + filter: alpha(opacity=90); +} + +.carousel-caption { + position: absolute; + right: 0; + bottom: 0; + left: 0; + padding: 15px; + background: #333333; + background: rgba(0, 0, 0, 0.75); + _filter: alpha(opacity=70); +} + +.carousel-caption h4, +.carousel-caption p { + line-height: 20px; + color: #ffffff; +} + +.carousel-caption h4 { + margin: 0 0 5px; +} + +.carousel-caption p { + margin-bottom: 0; +} + +.pull-right { + float: right; +} + +.pull-left { + float: left; +} + +.hide { + display: none; +} + +.show { + display: block; +} + +.invisible { + visibility: hidden; +} + +.affix { + position: fixed; +} diff --git a/OpenAuth.Identity/wwwroot/lib/bootstrap/css/bootstrap-ie6.min.css b/OpenAuth.Identity/wwwroot/lib/bootstrap/css/bootstrap-ie6.min.css new file mode 100644 index 0000000..04b8c35 --- /dev/null +++ b/OpenAuth.Identity/wwwroot/lib/bootstrap/css/bootstrap-ie6.min.css @@ -0,0 +1 @@ +article,aside,details,figcaption,figure,footer,header,hgroup,nav,section{display:block}audio,canvas,video{display:inline-block;*display:inline;*zoom:1}audio:not([controls]){display:none}html{font-size:100%;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%}a:focus{outline:thin dotted #333;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}a:hover,a:active{outline:0}sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:baseline}sup{top:-0.5em}sub{bottom:-0.25em}img{width:auto\9;height:auto;max-width:100%;vertical-align:middle;border:0;-ms-interpolation-mode:bicubic}#map_canvas img,.google-maps img{max-width:none}button,input,select,textarea{margin:0;font-size:100%;vertical-align:middle}button,input{*overflow:visible;line-height:normal}button::-moz-focus-inner,input::-moz-focus-inner{padding:0;border:0}button,html input[type="button"],input[type="reset"],input[type="submit"]{cursor:pointer;-webkit-appearance:button}input[type="search"]{-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;-webkit-appearance:textfield}input[type="search"]::-webkit-search-decoration,input[type="search"]::-webkit-search-cancel-button{-webkit-appearance:none}textarea{overflow:auto;vertical-align:top}.clearfix{*zoom:1}.clearfix:before,.clearfix:after{display:table;line-height:0;content:""}.clearfix:after{clear:both}.hide-text{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.input-block-level{display:block;width:100%;min-height:30px;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}body{margin:0;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:14px;line-height:20px;color:#333;background-color:#fff}a{color:#08c;text-decoration:none}a:hover{color:#005580;text-decoration:underline}.img-rounded{-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px}.img-polaroid{padding:4px;background-color:#fff;border:1px solid #ccc;border:1px solid rgba(0,0,0,0.2);-webkit-box-shadow:0 1px 3px rgba(0,0,0,0.1);-moz-box-shadow:0 1px 3px rgba(0,0,0,0.1);box-shadow:0 1px 3px rgba(0,0,0,0.1)}.img-circle{-webkit-border-radius:500px;-moz-border-radius:500px;border-radius:500px}.row{margin-left:-20px;*zoom:1}.row:before,.row:after{display:table;line-height:0;content:""}.row:after{clear:both}[class*="span"]{float:left;min-height:1px;margin-left:20px}.container,.navbar-static-top .container,.navbar-fixed-top .container,.navbar-fixed-bottom .container{width:940px}.span12{display:inline;float:left;width:940px;min-height:1px;margin-left:20px}.span11{display:inline;float:left;width:860px;min-height:1px;margin-left:20px}.span10{display:inline;float:left;width:780px;min-height:1px;margin-left:20px}.span9{display:inline;float:left;width:700px;min-height:1px;margin-left:20px}.span8{display:inline;float:left;width:620px;min-height:1px;margin-left:20px}.span7{display:inline;float:left;width:540px;min-height:1px;margin-left:20px}.span6{display:inline;float:left;width:460px;min-height:1px;margin-left:20px}.span5{display:inline;float:left;width:380px;min-height:1px;margin-left:20px}.span4{display:inline;float:left;width:300px;min-height:1px;margin-left:20px}.span3{display:inline;float:left;width:220px;min-height:1px;margin-left:20px}.span2{display:inline;float:left;width:140px;min-height:1px;margin-left:20px}.span1{display:inline;float:left;width:60px;min-height:1px;margin-left:20px}.offset12{margin-left:980px}.offset11{margin-left:900px}.offset10{margin-left:820px}.offset9{margin-left:740px}.offset8{margin-left:660px}.offset7{margin-left:580px}.offset6{margin-left:500px}.offset5{margin-left:420px}.offset4{margin-left:340px}.offset3{margin-left:260px}.offset2{margin-left:180px}.offset1{margin-left:100px}.span-first-child{margin-left:10px}.row-fluid{width:100%;*zoom:1}.row-fluid:before,.row-fluid:after{display:table;line-height:0;content:""}.row-fluid:after{clear:both}.row-fluid [class*="span"]{display:block;float:left;width:100%;min-height:30px;margin-left:2.127659574468085%;*margin-left:2.074468085106383%;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.row-fluid [class*="span"]:first-child{margin-left:0}.row-fluid .controls-row [class*="span"]+[class*="span"]{margin-left:2.127659574468085%}.row-fluid .span12{display:block;float:left;width:100%;*width:99.94680851063829%;min-height:30px;margin-left:2.127659574468085%;*margin-left:2.074468085106383%;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.row-fluid .span11{display:block;float:left;width:100%;width:91.48936170212765%;*width:91.43617021276594%;min-height:30px;margin-left:2.127659574468085%;*margin-left:2.074468085106383%;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.row-fluid .span10{display:block;float:left;width:100%;width:82.97872340425532%;*width:82.92553191489361%;min-height:30px;margin-left:2.127659574468085%;*margin-left:2.074468085106383%;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.row-fluid .span9{display:block;float:left;width:100%;width:74.46808510638297%;*width:74.41489361702126%;min-height:30px;margin-left:2.127659574468085%;*margin-left:2.074468085106383%;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.row-fluid .span8{display:block;float:left;width:100%;width:65.95744680851064%;*width:65.90425531914893%;min-height:30px;margin-left:2.127659574468085%;*margin-left:2.074468085106383%;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.row-fluid .span7{display:block;float:left;width:100%;width:57.44680851063829%;*width:57.39361702127659%;min-height:30px;margin-left:2.127659574468085%;*margin-left:2.074468085106383%;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.row-fluid .span6{display:block;float:left;width:100%;width:48.93617021276595%;*width:48.88297872340425%;min-height:30px;margin-left:2.127659574468085%;*margin-left:2.074468085106383%;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.row-fluid .span5{display:block;float:left;width:100%;width:40.42553191489362%;*width:40.37234042553192%;min-height:30px;margin-left:2.127659574468085%;*margin-left:2.074468085106383%;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.row-fluid .span4{display:block;float:left;width:100%;width:31.914893617021278%;*width:31.861702127659576%;min-height:30px;margin-left:2.127659574468085%;*margin-left:2.074468085106383%;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.row-fluid .span3{display:block;float:left;width:100%;width:23.404255319148934%;*width:23.351063829787233%;min-height:30px;margin-left:2.127659574468085%;*margin-left:2.074468085106383%;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.row-fluid .span2{display:block;float:left;width:100%;width:14.893617021276595%;*width:14.840425531914894%;min-height:30px;margin-left:2.127659574468085%;*margin-left:2.074468085106383%;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.row-fluid .span1{display:block;float:left;width:100%;width:6.382978723404255%;*width:6.329787234042553%;min-height:30px;margin-left:2.127659574468085%;*margin-left:2.074468085106383%;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.row-fluid .offset12{margin-left:104.25531914893617%;*margin-left:104.14893617021275%}.row-fluid .offset12:first-child{margin-left:102.12765957446808%;*margin-left:102.02127659574467%}.row-fluid .offset11{margin-left:95.74468085106382%;*margin-left:95.6382978723404%}.row-fluid .offset11:first-child{margin-left:93.61702127659574%;*margin-left:93.51063829787232%}.row-fluid .offset10{margin-left:87.23404255319149%;*margin-left:87.12765957446807%}.row-fluid .offset10:first-child{margin-left:85.1063829787234%;*margin-left:84.99999999999999%}.row-fluid .offset9{margin-left:78.72340425531914%;*margin-left:78.61702127659572%}.row-fluid .offset9:first-child{margin-left:76.59574468085106%;*margin-left:76.48936170212764%}.row-fluid .offset8{margin-left:70.2127659574468%;*margin-left:70.10638297872339%}.row-fluid .offset8:first-child{margin-left:68.08510638297872%;*margin-left:67.9787234042553%}.row-fluid .offset7{margin-left:61.70212765957446%;*margin-left:61.59574468085106%}.row-fluid .offset7:first-child{margin-left:59.574468085106375%;*margin-left:59.46808510638297%}.row-fluid .offset6{margin-left:53.191489361702125%;*margin-left:53.085106382978715%}.row-fluid .offset6:first-child{margin-left:51.063829787234035%;*margin-left:50.95744680851063%}.row-fluid .offset5{margin-left:44.68085106382979%;*margin-left:44.57446808510638%}.row-fluid .offset5:first-child{margin-left:42.5531914893617%;*margin-left:42.4468085106383%}.row-fluid .offset4{margin-left:36.170212765957444%;*margin-left:36.06382978723405%}.row-fluid .offset4:first-child{margin-left:34.04255319148936%;*margin-left:33.93617021276596%}.row-fluid .offset3{margin-left:27.659574468085104%;*margin-left:27.5531914893617%}.row-fluid .offset3:first-child{margin-left:25.53191489361702%;*margin-left:25.425531914893618%}.row-fluid .offset2{margin-left:19.148936170212764%;*margin-left:19.04255319148936%}.row-fluid .offset2:first-child{margin-left:17.02127659574468%;*margin-left:16.914893617021278%}.row-fluid .offset1{margin-left:10.638297872340425%;*margin-left:10.53191489361702%}.row-fluid .offset1:first-child{margin-left:8.51063829787234%;*margin-left:8.404255319148938%}.row-fluid .span-first-child{margin-left:0}[class*="span"].hide,.row-fluid [class*="span"].hide{display:none}[class*="span"].pull-right,.row-fluid [class*="span"].pull-right{float:right}form{margin:0 0 20px}fieldset{padding:0;margin:0;border:0}legend{display:block;width:100%;padding:0;margin-bottom:20px;*margin-left:-7px;font-size:21px;line-height:40px;color:#333;border:0;border-bottom:1px solid #e5e5e5}legend small{font-size:15px;color:#999}label,input,button,select,textarea{font-size:14px;font-weight:normal;line-height:20px}input,button,select,textarea{font-family:"Helvetica Neue",Helvetica,Arial,sans-serif}label{display:block;margin-bottom:5px}select,textarea,.input-text,.input-password,.input-datetime,.input-datetime-local,.input-date,.input-month,.input-time,.input-week,.input-number,.input-email,.input-url,.input-search,.input-tel,.input-color,.uneditable-input{display:inline-block;height:20px;padding:4px 6px;margin-bottom:10px;font-size:14px;line-height:20px;color:#555;vertical-align:middle;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}input,textarea,.uneditable-input{width:206px}textarea{height:auto}textarea,.input-text,.input-password,.input-datetime,.input-datetime-local,.input-date,.input-month,.input-time,.input-week,.input-number,.input-email,.input-url,.input-search,.input-tel,.input-color,.uneditable-input{background-color:#fff;border:1px solid #ccc;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);-webkit-transition:border linear .2s,box-shadow linear .2s;-moz-transition:border linear .2s,box-shadow linear .2s;-o-transition:border linear .2s,box-shadow linear .2s;transition:border linear .2s,box-shadow linear .2s}textarea:focus,.input-text:focus,.input-password:focus,.input-datetime:focus,.input-datetime-local:focus,.input-date:focus,.input-month:focus,.input-time:focus,.input-week:focus,.input-number:focus,.input-email:focus,.input-url:focus,.input-search:focus,.input-tel:focus,.input-color:focus,.uneditable-input:focus{border-color:rgba(82,168,236,0.8);outline:0;outline:thin dotted \9;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 8px rgba(82,168,236,0.6);-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 8px rgba(82,168,236,0.6);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 8px rgba(82,168,236,0.6)}input[type="radio"],input[type="checkbox"]{margin:4px 0 0;margin-top:1px \9;*margin-top:0;line-height:normal;cursor:pointer}.input-file,.input-image,.input-submit,.input-reset,.input-button,.input-radio,.input-checkbox{width:auto}select,input[type="file"]{height:30px;*margin-top:4px;line-height:30px}select{width:220px;background-color:#fff;border:1px solid #ccc}.select-multiple,.select-size{height:auto}select:focus,input[type="file"]:focus,input[type="radio"]:focus,input[type="checkbox"]:focus{outline:thin dotted #333;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}.uneditable-input,.uneditable-textarea{color:#999;cursor:not-allowed;background-color:#fcfcfc;border-color:#ccc;-webkit-box-shadow:inset 0 1px 2px rgba(0,0,0,0.025);-moz-box-shadow:inset 0 1px 2px rgba(0,0,0,0.025);box-shadow:inset 0 1px 2px rgba(0,0,0,0.025)}.uneditable-input{overflow:hidden;white-space:nowrap}.uneditable-textarea{width:auto;height:auto}input:-moz-placeholder,textarea:-moz-placeholder{color:#999}input:-ms-input-placeholder,textarea:-ms-input-placeholder{color:#999}input::-webkit-input-placeholder,textarea::-webkit-input-placeholder{color:#999}.radio,.checkbox{min-height:20px;padding-left:20px;_padding-left:0}.radio input[type="radio"],.checkbox input[type="checkbox"]{float:left;margin-left:-20px}.controls>.radio:first-child,.controls>.checkbox:first-child{padding-top:5px}.radio.inline,.checkbox.inline,.radio-inline,.checkbox-inline{display:inline-block;padding-top:5px;margin-bottom:0;vertical-align:middle}.radio.inline+.radio.inline,.checkbox.inline+.checkbox.inline,.radio-inline .checkbox-inline{margin-left:10px}.input-mini{width:60px}.input-small{width:90px}.input-medium{width:150px}.input-large{width:210px}.input-xlarge{width:270px}.input-xxlarge{width:530px}input[class*="span"],select[class*="span"],textarea[class*="span"],.uneditable-input[class*="span"],.row-fluid input[class*="span"],.row-fluid select[class*="span"],.row-fluid textarea[class*="span"],.row-fluid .uneditable-input[class*="span"]{float:none;margin-left:0}.input-append input[class*="span"],.input-append .uneditable-input[class*="span"],.input-prepend input[class*="span"],.input-prepend .uneditable-input[class*="span"],.row-fluid input[class*="span"],.row-fluid select[class*="span"],.row-fluid textarea[class*="span"],.row-fluid .uneditable-input[class*="span"],.row-fluid .input-prepend [class*="span"],.row-fluid .input-append [class*="span"]{display:inline-block}input,textarea,.uneditable-input{margin-left:0}.controls-row [class*="span"]+[class*="span"]{margin-left:20px}input.span12,textarea.span12{float:none;width:926px;margin-left:0}.uneditable-input-span12{width:926px}input.span11,textarea.span11{float:none;width:846px;margin-left:0}.uneditable-input-span11{width:846px}input.span10,textarea.span10{float:none;width:766px;margin-left:0}.uneditable-input-span10{width:766px}input.span9,textarea.span9{float:none;width:686px;margin-left:0}.uneditable-input-span9{width:686px}input.span8,textarea.span8{float:none;width:606px;margin-left:0}.uneditable-input-span8{width:606px}input.span7,textarea.span7{float:none;width:526px;margin-left:0}.uneditable-input-span7{width:526px}input.span6,textarea.span6{float:none;width:446px;margin-left:0}.uneditable-input-span6{width:446px}input.span5,textarea.span5{float:none;width:366px;margin-left:0}.uneditable-input-span5{width:366px}input.span4,textarea.span4{float:none;width:286px;margin-left:0}.uneditable-input-span4{width:286px}input.span3,textarea.span3{float:none;width:206px;margin-left:0}.uneditable-input-span3{width:206px}input.span2,textarea.span2{float:none;width:126px;margin-left:0}.uneditable-input-span2{width:126px}input.span1,textarea.span1{float:none;width:46px;margin-left:0}.uneditable-input-span1{width:46px}.controls-row{*zoom:1}.controls-row:before,.controls-row:after{display:table;line-height:0;content:""}.controls-row:after{clear:both}.controls-row [class*="span"],.row-fluid .controls-row [class*="span"]{float:left}.controls-row .checkbox[class*="span"],.controls-row .radio[class*="span"]{padding-top:5px}.input-disabled,.select-disabled,.textarea-disabled,.input-readonly,.select-readonly,.textarea-readonly{cursor:not-allowed;background-color:#eee}.radio-disabled,.checkbox-disabled,.radio-readonly,.checkbox-readonly{background-color:transparent}.control-group-warning label,.control-group-warning .help-block,.control-group-warning .help-inline{color:#c09853}.control-group-warning * label{color:inherit}.control-group-warning .checkbox,.control-group-warning .radio,.control-group-warning input,.control-group-warning select,.control-group-warning textarea{color:#c09853}.control-group-warning input,.control-group-warning select,.control-group-warning textarea{border-color:#c09853;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075)}.control-group-warning input:focus,.control-group-warning select:focus,.control-group-warning textarea:focus{border-color:#a47e3c;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #dbc59e;-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #dbc59e;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #dbc59e}.control-group-warning .input-prepend .add-on,.control-group-warning .input-append .add-on{color:#c09853;background-color:#fcf8e3;border-color:#c09853}.control-group-error label,.control-group-error .help-block,.control-group-error .help-inline{color:#b94a48}.control-group-error * label{color:inherit}.control-group-error .checkbox,.control-group-error .radio,.control-group-error input,.control-group-error select,.control-group-error textarea{color:#b94a48}.control-group-error input,.control-group-error select,.control-group-error textarea{border-color:#b94a48;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075)}.control-group-error input:focus,.control-group-error select:focus,.control-group-error textarea:focus{border-color:#953b39;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #d59392;-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #d59392;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #d59392}.control-group-error .input-prepend .add-on,.control-group-error .input-append .add-on{color:#b94a48;background-color:#f2dede;border-color:#b94a48}.control-group-success label,.control-group-success .help-block,.control-group-success .help-inline{color:#468847}.control-group-success * label{color:inherit}.control-group-success .checkbox,.control-group-success .radio,.control-group-success input,.control-group-success select,.control-group-success textarea{color:#468847}.control-group-success input,.control-group-success select,.control-group-success textarea{border-color:#468847;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075)}.control-group-success input:focus,.control-group-success select:focus,.control-group-success textarea:focus{border-color:#356635;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #7aba7b;-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #7aba7b;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #7aba7b}.control-group-success .input-prepend .add-on,.control-group-success .input-append .add-on{color:#468847;background-color:#dff0d8;border-color:#468847}.control-group-info label,.control-group-info .help-block,.control-group-info .help-inline{color:#3a87ad}.control-group-info * label{color:inherit}.control-group-info .checkbox,.control-group-info .radio,.control-group-info input,.control-group-info select,.control-group-info textarea{color:#3a87ad}.control-group-info input,.control-group-info select,.control-group-info textarea{border-color:#3a87ad;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075)}.control-group-info input:focus,.control-group-info select:focus,.control-group-info textarea:focus{border-color:#2d6987;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #7ab5d3;-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #7ab5d3;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #7ab5d3}.control-group-info .input-prepend .add-on,.control-group-info .input-append .add-on{color:#3a87ad;background-color:#d9edf7;border-color:#3a87ad}input:focus:required:invalid,textarea:focus:required:invalid,select:focus:required:invalid{color:#b94a48;border-color:#ee5f5b}input:focus:required:invalid:focus,textarea:focus:required:invalid:focus,select:focus:required:invalid:focus{border-color:#e9322d;-webkit-box-shadow:0 0 6px #f8b9b7;-moz-box-shadow:0 0 6px #f8b9b7;box-shadow:0 0 6px #f8b9b7}.form-actions{padding:19px 20px 20px;margin-top:20px;margin-bottom:20px;background-color:#f5f5f5;border-top:1px solid #e5e5e5;*zoom:1}.form-actions:before,.form-actions:after{display:table;line-height:0;content:""}.form-actions:after{clear:both}.help-block,.help-inline{color:#595959}.help-block{display:block;margin-bottom:10px}.help-inline{display:inline-block;*display:inline;padding-left:5px;vertical-align:middle;*zoom:1}.input-append,.input-prepend{margin-bottom:5px;font-size:0;white-space:nowrap}.input-append input,.input-prepend input,.input-append select,.input-prepend select,.input-append .uneditable-input,.input-prepend .uneditable-input,.input-append .dropdown-menu,.input-prepend .dropdown-menu{font-size:14px}.input-append input,.input-prepend input,.input-append select,.input-prepend select,.input-append .uneditable-input,.input-prepend .uneditable-input{position:relative;margin-bottom:0;*margin-left:0;vertical-align:top;-webkit-border-radius:0 4px 4px 0;-moz-border-radius:0 4px 4px 0;border-radius:0 4px 4px 0}.input-append input:focus,.input-prepend input:focus,.input-append select:focus,.input-prepend select:focus,.input-append .uneditable-input:focus,.input-prepend .uneditable-input:focus{z-index:2}.input-append .add-on,.input-prepend .add-on{display:inline-block;width:auto;height:20px;min-width:16px;padding:4px 5px;font-size:14px;font-weight:normal;line-height:20px;text-align:center;text-shadow:0 1px 0 #fff;background-color:#eee;border:1px solid #ccc}.input-append .add-on,.input-prepend .add-on,.input-append .btn,.input-prepend .btn{*margin-top:1px;vertical-align:top;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.input-append .active,.input-prepend .active{background-color:#a9dba9;border-color:#46a546}.input-prepend .add-on,.input-prepend .btn{margin-right:-1px;*margin-right:-2px}.input-prepend .add-on:first-child,.input-prepend .btn:first-child{-webkit-border-radius:4px 0 0 4px;-moz-border-radius:4px 0 0 4px;border-radius:4px 0 0 4px}.input-append input,.input-append select,.input-append .uneditable-input{-webkit-border-radius:4px 0 0 4px;-moz-border-radius:4px 0 0 4px;border-radius:4px 0 0 4px}.input-append input+.btn-group .btn,.input-append select+.btn-group .btn,.input-append .uneditable-input+.btn-group .btn{-webkit-border-radius:0 4px 4px 0;-moz-border-radius:0 4px 4px 0;border-radius:0 4px 4px 0}.input-append .add-on,.input-append .btn,.input-append .btn-group{margin-left:-1px;*margin-left:-2px}.input-append .add-on:last-child,.input-append .btn:last-child{-webkit-border-radius:0 4px 4px 0;-moz-border-radius:0 4px 4px 0;border-radius:0 4px 4px 0}.input-prepend.input-append input,.input-prepend.input-append select,.input-prepend.input-append .uneditable-input{-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.input-prepend.input-append input+.btn-group .btn,.input-prepend.input-append select+.btn-group .btn,.input-prepend.input-append .uneditable-input+.btn-group .btn{-webkit-border-radius:0 4px 4px 0;-moz-border-radius:0 4px 4px 0;border-radius:0 4px 4px 0}.input-prepend.input-append .add-on:first-child,.input-prepend.input-append .btn:first-child{margin-right:-1px;-webkit-border-radius:4px 0 0 4px;-moz-border-radius:4px 0 0 4px;border-radius:4px 0 0 4px}.input-prepend.input-append .add-on:last-child,.input-prepend.input-append .btn:last-child{margin-left:-1px;-webkit-border-radius:0 4px 4px 0;-moz-border-radius:0 4px 4px 0;border-radius:0 4px 4px 0}.input-prepend.input-append .btn-group:first-child{margin-left:0}input.search-query{padding-right:14px;padding-right:4px \9;padding-left:14px;padding-left:4px \9;margin-bottom:0;-webkit-border-radius:15px;-moz-border-radius:15px;border-radius:15px}.form-search .input-append .search-query,.form-search .input-prepend .search-query{-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.form-search .input-append .search-query{-webkit-border-radius:14px 0 0 14px;-moz-border-radius:14px 0 0 14px;border-radius:14px 0 0 14px}.form-search .input-append .btn{-webkit-border-radius:0 14px 14px 0;-moz-border-radius:0 14px 14px 0;border-radius:0 14px 14px 0}.form-search .input-prepend .search-query{-webkit-border-radius:0 14px 14px 0;-moz-border-radius:0 14px 14px 0;border-radius:0 14px 14px 0}.form-search .input-prepend .btn{-webkit-border-radius:14px 0 0 14px;-moz-border-radius:14px 0 0 14px;border-radius:14px 0 0 14px}.form-search input,.form-inline input,.form-horizontal input,.form-search textarea,.form-inline textarea,.form-horizontal textarea,.form-search select,.form-inline select,.form-horizontal select,.form-search .help-inline,.form-inline .help-inline,.form-horizontal .help-inline,.form-search .uneditable-input,.form-inline .uneditable-input,.form-horizontal .uneditable-input,.form-search .input-prepend,.form-inline .input-prepend,.form-horizontal .input-prepend,.form-search .input-append,.form-inline .input-append,.form-horizontal .input-append{display:inline-block;*display:inline;margin-bottom:0;vertical-align:middle;*zoom:1}.form-search .hide,.form-inline .hide,.form-horizontal .hide{display:none}.form-search label,.form-inline label,.form-search .btn-group,.form-inline .btn-group{display:inline-block}.form-search .input-append,.form-inline .input-append,.form-search .input-prepend,.form-inline .input-prepend{margin-bottom:0}.form-search .radio,.form-search .checkbox,.form-inline .radio,.form-inline .checkbox{padding-left:0;margin-bottom:0;vertical-align:middle}.form-search .radio input[type="radio"],.form-search .checkbox input[type="checkbox"],.form-inline .radio input[type="radio"],.form-inline .checkbox input[type="checkbox"]{float:left;margin-right:3px;margin-left:0}.control-group{margin-bottom:10px}legend+.control-group{margin-top:20px;-webkit-margin-top-collapse:separate}.form-horizontal .control-group{margin-bottom:20px;*zoom:1}.form-horizontal .control-group:before,.form-horizontal .control-group:after{display:table;line-height:0;content:""}.form-horizontal .control-group:after{clear:both}.form-horizontal .control-label{float:left;width:160px;padding-top:5px;text-align:right}.form-horizontal .controls{*display:inline-block;*padding-left:20px;margin-left:180px;*margin-left:0}.form-horizontal .controls-first-child{*padding-left:180px}.form-horizontal .help-block{margin-bottom:0}.form-horizontal input+.help-block,.form-horizontal select+.help-block,.form-horizontal textarea+.help-block{margin-top:10px}.form-horizontal .form-actions{padding-left:180px}table{max-width:100%;background-color:transparent;border-collapse:collapse;border-spacing:0}.table{width:100%;margin-bottom:20px}.table th,.table td{padding:8px;line-height:20px;text-align:left;vertical-align:top;border-top:1px solid #ddd}.table th{font-weight:bold}.table thead th{vertical-align:bottom}.table caption+thead tr:first-child th,.table caption+thead tr:first-child td,.table colgroup+thead tr:first-child th,.table colgroup+thead tr:first-child td,.table thead:first-child tr:first-child th,.table thead:first-child tr:first-child td{border-top:0}.table tbody+tbody{border-top:2px solid #ddd}.table-condensed th,.table-condensed td{padding:4px 5px}.table-bordered{border:1px solid #ddd;border-collapse:separate;*border-collapse:collapse;border-left:0;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}.table-bordered th,.table-bordered td{border-left:1px solid #ddd}.table-bordered caption+thead tr:first-child th,.table-bordered caption+tbody tr:first-child th,.table-bordered caption+tbody tr:first-child td,.table-bordered colgroup+thead tr:first-child th,.table-bordered colgroup+tbody tr:first-child th,.table-bordered colgroup+tbody tr:first-child td,.table-bordered thead:first-child tr:first-child th,.table-bordered tbody:first-child tr:first-child th,.table-bordered tbody:first-child tr:first-child td{border-top:0}.table-bordered thead:first-child tr:first-child th:first-child,.table-bordered tbody:first-child tr:first-child td:first-child{-webkit-border-top-left-radius:4px;border-top-left-radius:4px;-moz-border-radius-topleft:4px}.table-bordered thead:first-child tr:first-child th:last-child,.table-bordered tbody:first-child tr:first-child td:last-child{-webkit-border-top-right-radius:4px;border-top-right-radius:4px;-moz-border-radius-topright:4px}.table-bordered thead:last-child tr:last-child th:first-child,.table-bordered tbody:last-child tr:last-child td:first-child,.table-bordered tfoot:last-child tr:last-child td:first-child{-webkit-border-radius:0 0 0 4px;-moz-border-radius:0 0 0 4px;border-radius:0 0 0 4px;-webkit-border-bottom-left-radius:4px;border-bottom-left-radius:4px;-moz-border-radius-bottomleft:4px}.table-bordered thead:last-child tr:last-child th:last-child,.table-bordered tbody:last-child tr:last-child td:last-child,.table-bordered tfoot:last-child tr:last-child td:last-child{-webkit-border-bottom-right-radius:4px;border-bottom-right-radius:4px;-moz-border-radius-bottomright:4px}.table-bordered caption+thead tr:first-child th:first-child,.table-bordered caption+tbody tr:first-child td:first-child,.table-bordered colgroup+thead tr:first-child th:first-child,.table-bordered colgroup+tbody tr:first-child td:first-child{-webkit-border-top-left-radius:4px;border-top-left-radius:4px;-moz-border-radius-topleft:4px}.table-bordered caption+thead tr:first-child th:last-child,.table-bordered caption+tbody tr:first-child td:last-child,.table-bordered colgroup+thead tr:first-child th:last-child,.table-bordered colgroup+tbody tr:first-child td:last-child{-webkit-border-top-right-radius:4px;border-top-right-radius:4px;-moz-border-radius-topright:4px}.table-striped tbody tr:nth-child(odd) td,.table-striped tbody tr:nth-child(odd) th{background-color:#f9f9f9}.table-hover tbody .tr-hover td,.table-hover tbody .tr-hover th{background-color:#f5f5f5}table td[class*="span"],table th[class*="span"],.row-fluid table td[class*="span"],.row-fluid table th[class*="span"]{display:table-cell;float:none;margin-left:0}.table td.span1,.table th.span1{float:none;width:44px;margin-left:0}.table td.span2,.table th.span2{float:none;width:124px;margin-left:0}.table td.span3,.table th.span3{float:none;width:204px;margin-left:0}.table td.span4,.table th.span4{float:none;width:284px;margin-left:0}.table td.span5,.table th.span5{float:none;width:364px;margin-left:0}.table td.span6,.table th.span6{float:none;width:444px;margin-left:0}.table td.span7,.table th.span7{float:none;width:524px;margin-left:0}.table td.span8,.table th.span8{float:none;width:604px;margin-left:0}.table td.span9,.table th.span9{float:none;width:684px;margin-left:0}.table td.span10,.table th.span10{float:none;width:764px;margin-left:0}.table td.span11,.table th.span11{float:none;width:844px;margin-left:0}.table td.span12,.table th.span12{float:none;width:924px;margin-left:0}.table tbody tr.success td{background-color:#dff0d8}.table tbody tr.error td{background-color:#f2dede}.table tbody tr.warning td{background-color:#fcf8e3}.table tbody tr.info td{background-color:#d9edf7}.table-hover tbody tr.success:hover td{background-color:#d0e9c6}.table-hover tbody tr.error:hover td{background-color:#ebcccc}.table-hover tbody tr.warning:hover td{background-color:#faf2cc}.table-hover tbody tr.info:hover td{background-color:#c4e3f3}.icon-xxx{display:inline-block;_display:inline;width:14px;_width:14px;height:14px;_height:19px;margin-top:1px;*margin-right:.3em;line-height:14px;vertical-align:text-top;_vertical-align:middle;background-image:url("../img/glyphicons-halflings.png");_background-image:url("../img/glyphicons-halflings-8.png");background-position:14px 14px;background-repeat:no-repeat;_zoom:1}.icon-white{background-image:url("../img/glyphicons-halflings-white.png")}.nav-pills>.active>a>[class^="icon-"],.nav-pills>.active>a>[class*=" icon-"],.nav-list>.active>a>[class^="icon-"],.nav-list>.active>a>[class*=" icon-"],.navbar-inverse .nav>.active>a>[class^="icon-"],.navbar-inverse .nav>.active>a>[class*=" icon-"],.dropdown-menu>li>a:hover>[class^="icon-"],.dropdown-menu>li>a:hover>[class*=" icon-"],.dropdown-menu>.active>a>[class^="icon-"],.dropdown-menu>.active>a>[class*=" icon-"],.dropdown-submenu:hover>a>[class^="icon-"],.dropdown-submenu:hover>a>[class*=" icon-"]{background-image:url("../img/glyphicons-halflings-white.png")}.icon-glass{background-position:0 0}.icon-music{background-position:-24px 0}.icon-search{background-position:-48px 0}.icon-envelope{background-position:-72px 0}.icon-heart{background-position:-96px 0}.icon-star{background-position:-120px 0}.icon-star-empty{background-position:-144px 0}.icon-user{background-position:-168px 0}.icon-film{background-position:-192px 0}.icon-th-large{background-position:-216px 0}.icon-th{background-position:-240px 0}.icon-th-list{background-position:-264px 0}.icon-ok{background-position:-288px 0}.icon-remove{background-position:-312px 0}.icon-zoom-in{background-position:-336px 0}.icon-zoom-out{background-position:-360px 0}.icon-off{background-position:-384px 0}.icon-signal{background-position:-408px 0}.icon-cog{background-position:-432px 0}.icon-trash{background-position:-456px 0}.icon-home{background-position:0 -24px}.icon-file{background-position:-24px -24px}.icon-time{background-position:-48px -24px}.icon-road{background-position:-72px -24px}.icon-download-alt{background-position:-96px -24px}.icon-download{background-position:-120px -24px}.icon-upload{background-position:-144px -24px}.icon-inbox{background-position:-168px -24px}.icon-play-circle{background-position:-192px -24px}.icon-repeat{background-position:-216px -24px}.icon-refresh{background-position:-240px -24px}.icon-list-alt{background-position:-264px -24px}.icon-lock{background-position:-287px -24px}.icon-flag{background-position:-312px -24px}.icon-headphones{background-position:-336px -24px}.icon-volume-off{background-position:-360px -24px}.icon-volume-down{background-position:-384px -24px}.icon-volume-up{background-position:-408px -24px}.icon-qrcode{background-position:-432px -24px}.icon-barcode{background-position:-456px -24px}.icon-tag{background-position:0 -48px}.icon-tags{background-position:-25px -48px}.icon-book{background-position:-48px -48px}.icon-bookmark{background-position:-72px -48px}.icon-print{background-position:-96px -48px}.icon-camera{background-position:-120px -48px}.icon-font{background-position:-144px -48px}.icon-bold{background-position:-167px -48px}.icon-italic{background-position:-192px -48px}.icon-text-height{background-position:-216px -48px}.icon-text-width{background-position:-240px -48px}.icon-align-left{background-position:-264px -48px}.icon-align-center{background-position:-288px -48px}.icon-align-right{background-position:-312px -48px}.icon-align-justify{background-position:-336px -48px}.icon-list{background-position:-360px -48px}.icon-indent-left{background-position:-384px -48px}.icon-indent-right{background-position:-408px -48px}.icon-facetime-video{background-position:-432px -48px}.icon-picture{background-position:-456px -48px}.icon-pencil{background-position:0 -72px}.icon-map-marker{background-position:-24px -72px}.icon-adjust{background-position:-48px -72px}.icon-tint{background-position:-72px -72px}.icon-edit{background-position:-96px -72px}.icon-share{background-position:-120px -72px}.icon-check{background-position:-144px -72px}.icon-move{background-position:-168px -72px}.icon-step-backward{background-position:-192px -72px}.icon-fast-backward{background-position:-216px -72px}.icon-backward{background-position:-240px -72px}.icon-play{background-position:-264px -72px}.icon-pause{background-position:-288px -72px}.icon-stop{background-position:-312px -72px}.icon-forward{background-position:-336px -72px}.icon-fast-forward{background-position:-360px -72px}.icon-step-forward{background-position:-384px -72px}.icon-eject{background-position:-408px -72px}.icon-chevron-left{background-position:-432px -72px}.icon-chevron-right{background-position:-456px -72px}.icon-plus-sign{background-position:0 -96px}.icon-minus-sign{background-position:-24px -96px}.icon-remove-sign{background-position:-48px -96px}.icon-ok-sign{background-position:-72px -96px}.icon-question-sign{background-position:-96px -96px}.icon-info-sign{background-position:-120px -96px}.icon-screenshot{background-position:-144px -96px}.icon-remove-circle{background-position:-168px -96px}.icon-ok-circle{background-position:-192px -96px}.icon-ban-circle{background-position:-216px -96px}.icon-arrow-left{background-position:-240px -96px}.icon-arrow-right{background-position:-264px -96px}.icon-arrow-up{background-position:-289px -96px}.icon-arrow-down{background-position:-312px -96px}.icon-share-alt{background-position:-336px -96px}.icon-resize-full{background-position:-360px -96px}.icon-resize-small{background-position:-384px -96px}.icon-plus{background-position:-408px -96px}.icon-minus{background-position:-433px -96px}.icon-asterisk{background-position:-456px -96px}.icon-exclamation-sign{background-position:0 -120px}.icon-gift{background-position:-24px -120px}.icon-leaf{background-position:-48px -120px}.icon-fire{background-position:-72px -120px}.icon-eye-open{background-position:-96px -120px}.icon-eye-close{background-position:-120px -120px}.icon-warning-sign{background-position:-144px -120px}.icon-plane{background-position:-168px -120px}.icon-calendar{background-position:-192px -120px}.icon-random{width:16px;background-position:-216px -120px}.icon-comment{background-position:-240px -120px}.icon-magnet{background-position:-264px -120px}.icon-chevron-up{background-position:-288px -120px}.icon-chevron-down{background-position:-313px -119px}.icon-retweet{background-position:-336px -120px}.icon-shopping-cart{background-position:-360px -120px}.icon-folder-close{background-position:-384px -120px}.icon-folder-open{width:16px;background-position:-408px -120px}.icon-resize-vertical{background-position:-432px -119px}.icon-resize-horizontal{background-position:-456px -118px}.icon-hdd{background-position:0 -144px}.icon-bullhorn{background-position:-24px -144px}.icon-bell{background-position:-48px -144px}.icon-certificate{background-position:-72px -144px}.icon-thumbs-up{background-position:-96px -144px}.icon-thumbs-down{background-position:-120px -144px}.icon-hand-right{background-position:-144px -144px}.icon-hand-left{background-position:-168px -144px}.icon-hand-up{background-position:-192px -144px}.icon-hand-down{background-position:-216px -144px}.icon-circle-arrow-right{background-position:-240px -144px}.icon-circle-arrow-left{background-position:-264px -144px}.icon-circle-arrow-up{background-position:-288px -144px}.icon-circle-arrow-down{background-position:-312px -144px}.icon-globe{background-position:-336px -144px}.icon-wrench{background-position:-360px -144px}.icon-tasks{background-position:-384px -144px}.icon-filter{background-position:-408px -144px}.icon-briefcase{background-position:-432px -144px}.icon-fullscreen{background-position:-456px -144px}.dropup,.dropdown{position:relative;display:inline;*zoom:1}.dropdown-toggle{*margin-bottom:-3px}.dropdown-toggle:active,.open .dropdown-toggle{outline:0}.caret{display:inline-block;*display:inline;width:0;height:0;vertical-align:top;border-top:4px solid #000;border-right:4px solid transparent;border-left:4px solid transparent;content:"";*zoom:1}.caret{width:0;height:0;line-height:0;border-color:#000 transparent transparent;*border-color:#000 #fffffe #fffffe;border-style:solid;border-width:4px 4px 0;*filter:progid:DXImageTransform.Microsoft.Chroma(color=#fffffe)}.dropdown .caret{margin-top:8px;margin-left:2px}.dropdown-menu{position:absolute;top:100%;left:0;z-index:1000;display:none;float:left;*width:auto!important;*width:160px;*height:1px;min-width:160px;padding:5px 0;margin:2px 0 0;list-style:none;background-color:#fff;border:1px solid #ccc;border:1px solid rgba(0,0,0,0.2);*border-right-width:2px;*border-bottom-width:2px;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px;-webkit-box-shadow:0 5px 10px rgba(0,0,0,0.2);-moz-box-shadow:0 5px 10px rgba(0,0,0,0.2);box-shadow:0 5px 10px rgba(0,0,0,0.2);-webkit-background-clip:padding-box;-moz-background-clip:padding;background-clip:padding-box}.dropdown-menu.pull-right{right:0;left:auto}.dropdown-menu .divider{*width:100%;height:1px;margin:9px 1px;*margin:-5px 0 5px;overflow:hidden;background-color:#e5e5e5;border-bottom:1px solid #fff}.dropdown-menu li a{display:block;padding:3px 20px;clear:both;font-weight:normal;line-height:20px;color:#333;white-space:nowrap;*zoom:1}.dropdown-menu li a:hover,.dropdown-menu li a:focus,.dropdown-submenu:hover a,.dropdown-submenu-hover a{color:#fff;text-decoration:none;background-color:#0081c2;background-image:-moz-linear-gradient(top,#08c,#0077b3);background-image:-webkit-gradient(linear,0 0,0 100%,from(#08c),to(#0077b3));background-image:-webkit-linear-gradient(top,#08c,#0077b3);background-image:-o-linear-gradient(top,#08c,#0077b3);background-image:linear-gradient(to bottom,#08c,#0077b3);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff0088cc',endColorstr='#ff0077b3',GradientType=0)}.dropdown-submenu-hover * a{*color:inherit;*background-color:#fffffe;*background-image:none;*filter:none;*filter:chroma(color=#fffffe)}.dropdown-menu .active a,.dropdown-menu .active a:hover{color:#333;text-decoration:none;background-color:#0081c2;background-image:-moz-linear-gradient(top,#08c,#0077b3);background-image:-webkit-gradient(linear,0 0,0 100%,from(#08c),to(#0077b3));background-image:-webkit-linear-gradient(top,#08c,#0077b3);background-image:-o-linear-gradient(top,#08c,#0077b3);background-image:linear-gradient(to bottom,#08c,#0077b3);background-repeat:repeat-x;outline:0;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff0088cc',endColorstr='#ff0077b3',GradientType=0)}.dropdown-menu .disabled a,.dropdown-menu .disabled a:hover{color:#999}.dropdown-menu .disabled a:hover{text-decoration:none;cursor:default;background-color:transparent;*background-color:#fffffe;background-image:none;*filter:chroma(color=#fffffe)}.open{*z-index:1000}.open .dropdown-menu{display:block;*zoom:1}.pull-right .dropdown-menu{right:0;left:auto}.pull-right * .dropdown-menu{right:auto;left:0}.dropup .caret,.navbar-fixed-bottom .dropdown .caret{border-top:0;border-bottom:4px solid #000;content:""}.dropup .dropdown-menu,.navbar-fixed-bottom .dropdown .dropdown-menu{top:auto;bottom:100%;margin-bottom:1px}.dropdown-submenu{position:relative;*display:inline;*zoom:1}.dropdown-submenu .dropdown-menu{top:0;left:100%;margin-top:-6px;margin-left:-1px;-webkit-border-radius:0 6px 6px 6px;-moz-border-radius:0 6px 6px 6px;border-radius:0 6px 6px 6px}.dropdown-submenu:hover .dropdown-menu,.dropdown-submenu-hover .dropdown-menu{display:block}.dropup .dropdown-submenu .dropdown-menu{top:auto;bottom:0;margin-top:0;margin-bottom:-2px;-webkit-border-radius:5px 5px 5px 0;-moz-border-radius:5px 5px 5px 0;border-radius:5px 5px 5px 0}.dropdown-submenu a:after{display:block;float:right;width:0;height:0;margin-top:5px;margin-right:-10px;border-color:transparent;*border-color:#fffffe;border-left-color:#ccc;border-style:solid;border-width:5px 0 5px 5px;content:" ";*filter:chroma(color=#fffffe);*zoom:1}.dropdown-submenu .dropdown-tri{display:block;float:right;width:0;height:0;margin:-1.3em 5px 0 0;line-height:0;border-color:transparent transparent transparent #ccc;_border-color:#fffffe #fffffe #fffffe #ccc;border-style:solid;border-width:5px 0 5px 5px;_filter:chroma(color=#fffffe);zoom:1}.dropdown-submenu:hover a:after,.dropdown-submenu-hover .dropdown-tri{border-left-color:#fff}.dropdown-submenu-pull-left{float:none}.dropdown-submenu-pull-left .dropdown-menu{left:-100%;margin-left:10px;*margin-left:18px;-webkit-border-radius:6px 0 6px 6px;-moz-border-radius:6px 0 6px 6px;border-radius:6px 0 6px 6px}.dropdown-submenu-pull-left * .dropdown-menu{left:0;margin-left:0;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.dropdown .dropdown-menu .nav-header{padding-right:20px;padding-left:20px}.typeahead{margin-top:2px;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}.btn{display:inline-block;*display:inline;padding:4px 12px;_padding-top:3px;_padding-bottom:3px;margin-bottom:0;*margin-left:.3em;font-size:14px;line-height:20px;*line-height:20px;color:#333;text-align:center;text-shadow:0 1px 1px rgba(255,255,255,0.75);vertical-align:middle;cursor:pointer;background-color:#f5f5f5;*background-color:#e6e6e6;background-image:-moz-linear-gradient(top,#fff,#e6e6e6);background-image:-webkit-gradient(linear,0 0,0 100%,from(#fff),to(#e6e6e6));background-image:-webkit-linear-gradient(top,#fff,#e6e6e6);background-image:-o-linear-gradient(top,#fff,#e6e6e6);background-image:linear-gradient(to bottom,#fff,#e6e6e6);background-repeat:repeat-x;border:1px solid #bbb;*border:0;border-color:#e6e6e6 #e6e6e6 #bfbfbf;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);border-bottom-color:#a2a2a2;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff',endColorstr='#ffe6e6e6',GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);*zoom:1;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.05);-moz-box-shadow:inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.05);box-shadow:inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.05)}.btn:hover,.btn:active,.btn.active,.btn.disabled,.btn[disabled]{color:#333;background-color:#e6e6e6;*background-color:#d9d9d9}.btn:active,.btn.active{background-color:#ccc \9}.btn:first-child{*margin-left:0}.btn.btn-hover{color:#333;text-decoration:none;background-color:#e6e6e6;*background-color:#d9d9d9;background-position:0 -15px;-webkit-transition:background-position .1s linear;-moz-transition:background-position .1s linear;-o-transition:background-position .1s linear;transition:background-position .1s linear}.btn:focus{outline:thin dotted #333;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}.btn:active,.btn.active{background-color:#e6e6e6;background-color:#d9d9d9 \9;background-image:none;outline:0;-webkit-box-shadow:inset 0 2px 4px rgba(0,0,0,0.15),0 1px 2px rgba(0,0,0,0.05);-moz-box-shadow:inset 0 2px 4px rgba(0,0,0,0.15),0 1px 2px rgba(0,0,0,0.05);box-shadow:inset 0 2px 4px rgba(0,0,0,0.15),0 1px 2px rgba(0,0,0,0.05)}.btn.disabled,.btn[disabled],.btn-disabled{cursor:default;background-color:#e6e6e6;background-image:none;opacity:.65;filter:alpha(opacity=65);-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none}.btn.btn-large{*padding-top:7px;*padding-bottom:7px}.btn.btn-small{*padding-top:3px;*padding-bottom:3px}.btn.btn-mini{*padding-top:1px;*padding-bottom:1px}a.btn{*padding-top:5px;*padding-bottom:5px}a.btn.btn-large{*padding-top:13px;*padding-bottom:15px}a.btn.btn-small{*padding-top:4px;*padding-bottom:4px}a.btn.btn-mini{*padding-top:3px;*padding-bottom:3px}.btn-large{padding:11px 19px;font-size:17.5px;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px}.btn-large [class^="icon-"],.btn-large [class*=" icon-"]{margin-top:2px}.btn-small{padding:2px 10px;font-size:11.9px;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px}.btn-small [class^="icon-"],.btn-small [class*=" icon-"]{margin-top:0}.btn-mini{padding:1px 6px;font-size:10.5px;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px}.btn-block{display:block;width:100%;padding-right:0;padding-left:0;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.btn-block+.btn-block{margin-top:5px}input[type="submit"].btn-block,input[type="reset"].btn-block,input[type="button"].btn-block{width:100%}.btn-primary.active,.btn-warning.active,.btn-danger.active,.btn-success.active,.btn-info.active,.btn-inverse.active{color:rgba(255,255,255,0.75)}.btn{border-color:#c5c5c5;border-color:rgba(0,0,0,0.15) rgba(0,0,0,0.15) rgba(0,0,0,0.25)}.btn-primary{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.25);background-color:#006dcc;*background-color:#04c;background-image:-moz-linear-gradient(top,#08c,#04c);background-image:-webkit-gradient(linear,0 0,0 100%,from(#08c),to(#04c));background-image:-webkit-linear-gradient(top,#08c,#04c);background-image:-o-linear-gradient(top,#08c,#04c);background-image:linear-gradient(to bottom,#08c,#04c);background-repeat:repeat-x;border-color:#04c #04c #002a80;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff0088cc',endColorstr='#ff0044cc',GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.btn-primary:hover,.btn-primary:active,.btn-primary.active,.btn-primary.disabled,.btn-primary[disabled]{color:#fff;background-color:#04c;*background-color:#003bb3}.btn-primary:active,.btn-primary.active{background-color:#039 \9}.btn-primary.btn-primary-hover{color:#fff;background-color:#04c;*background-color:#003bb3}a.btn-primary-hover:hover{color:#fff;background-color:#04c;*background-color:#003bb3}.btn-warning{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.25);background-color:#faa732;*background-color:#f89406;background-image:-moz-linear-gradient(top,#fbb450,#f89406);background-image:-webkit-gradient(linear,0 0,0 100%,from(#fbb450),to(#f89406));background-image:-webkit-linear-gradient(top,#fbb450,#f89406);background-image:-o-linear-gradient(top,#fbb450,#f89406);background-image:linear-gradient(to bottom,#fbb450,#f89406);background-repeat:repeat-x;border-color:#f89406 #f89406 #ad6704;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffbb450',endColorstr='#fff89406',GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.btn-warning:hover,.btn-warning:active,.btn-warning.active,.btn-warning.disabled,.btn-warning[disabled]{color:#fff;background-color:#f89406;*background-color:#df8505}.btn-warning:active,.btn-warning.active{background-color:#c67605 \9}.btn-warning.btn-warning-hover{color:#fff;background-color:#f89406;*background-color:#df8505}a.btn-warning-hover:hover{color:#fff;background-color:#f89406;*background-color:#df8505}.btn-danger{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.25);background-color:#da4f49;*background-color:#bd362f;background-image:-moz-linear-gradient(top,#ee5f5b,#bd362f);background-image:-webkit-gradient(linear,0 0,0 100%,from(#ee5f5b),to(#bd362f));background-image:-webkit-linear-gradient(top,#ee5f5b,#bd362f);background-image:-o-linear-gradient(top,#ee5f5b,#bd362f);background-image:linear-gradient(to bottom,#ee5f5b,#bd362f);background-repeat:repeat-x;border-color:#bd362f #bd362f #802420;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffee5f5b',endColorstr='#ffbd362f',GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.btn-danger:hover,.btn-danger:active,.btn-danger.active,.btn-danger.disabled,.btn-danger[disabled]{color:#fff;background-color:#bd362f;*background-color:#a9302a}.btn-danger:active,.btn-danger.active{background-color:#942a25 \9}.btn-danger.btn-danger-hover{color:#fff;background-color:#bd362f;*background-color:#a9302a}a.btn-danger-hover:hover{color:#fff;background-color:#bd362f;*background-color:#a9302a}.btn-success{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.25);background-color:#5bb75b;*background-color:#51a351;background-image:-moz-linear-gradient(top,#62c462,#51a351);background-image:-webkit-gradient(linear,0 0,0 100%,from(#62c462),to(#51a351));background-image:-webkit-linear-gradient(top,#62c462,#51a351);background-image:-o-linear-gradient(top,#62c462,#51a351);background-image:linear-gradient(to bottom,#62c462,#51a351);background-repeat:repeat-x;border-color:#51a351 #51a351 #387038;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff62c462',endColorstr='#ff51a351',GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.btn-success:hover,.btn-success:active,.btn-success.active,.btn-success.disabled,.btn-success[disabled]{color:#fff;background-color:#51a351;*background-color:#499249}.btn-success:active,.btn-success.active{background-color:#408140 \9}.btn-success.btn-success-hover{color:#fff;background-color:#51a351;*background-color:#499249}a.btn-success-hover:hover{color:#fff;background-color:#51a351;*background-color:#499249}.btn-info{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.25);background-color:#49afcd;*background-color:#2f96b4;background-image:-moz-linear-gradient(top,#5bc0de,#2f96b4);background-image:-webkit-gradient(linear,0 0,0 100%,from(#5bc0de),to(#2f96b4));background-image:-webkit-linear-gradient(top,#5bc0de,#2f96b4);background-image:-o-linear-gradient(top,#5bc0de,#2f96b4);background-image:linear-gradient(to bottom,#5bc0de,#2f96b4);background-repeat:repeat-x;border-color:#2f96b4 #2f96b4 #1f6377;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de',endColorstr='#ff2f96b4',GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.btn-info:hover,.btn-info:active,.btn-info.active,.btn-info.disabled,.btn-info[disabled]{color:#fff;background-color:#2f96b4;*background-color:#2a85a0}.btn-info:active,.btn-info.active{background-color:#24748c \9}.btn-info.btn-info-hover{color:#fff;background-color:#2f96b4;*background-color:#2a85a0}a.btn-info-hover:hover{color:#fff;background-color:#2f96b4;*background-color:#2a85a0}.btn-inverse{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.25);background-color:#363636;*background-color:#222;background-image:-moz-linear-gradient(top,#444,#222);background-image:-webkit-gradient(linear,0 0,0 100%,from(#444),to(#222));background-image:-webkit-linear-gradient(top,#444,#222);background-image:-o-linear-gradient(top,#444,#222);background-image:linear-gradient(to bottom,#444,#222);background-repeat:repeat-x;border-color:#222 #222 #000;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff444444',endColorstr='#ff222222',GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.btn-inverse:hover,.btn-inverse:active,.btn-inverse.active,.btn-inverse.disabled,.btn-inverse[disabled]{color:#fff;background-color:#222;*background-color:#151515}.btn-inverse:active,.btn-inverse.active{background-color:#080808 \9}.btn-inverse.btn-inverse-hover{color:#fff;background-color:#222;*background-color:#3c3c3c}a.btn-inverse-hover:hover{color:#fff;background-color:#222;*background-color:#3c3c3c}button.btn,input[type="submit"].btn{*padding-top:3px;*padding-bottom:3px}button.btn::-moz-focus-inner,input[type="submit"].btn::-moz-focus-inner{padding:0;border:0}button.btn.btn-large,input[type="submit"].btn.btn-large{*padding-top:7px;*padding-bottom:7px}button.btn.btn-small,input[type="submit"].btn.btn-small{*padding-top:3px;*padding-bottom:3px}button.btn.btn-mini,input[type="submit"].btn.btn-mini{*padding-top:1px;*padding-bottom:1px}.btn-link,.btn-link:active,.btn-link[disabled]{background-color:transparent;background-image:none;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none}.btn-link{color:#08c;cursor:pointer;border-color:transparent;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.btn-link:hover{color:#005580;text-decoration:underline;background-color:transparent}.btn-link[disabled]:hover{color:#333;text-decoration:none}.btn-disabled{cursor:default;background-image:none;opacity:.65;filter:alpha(opacity=65);filter:alpha(opacity=35);-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none}.btn-group{position:relative;display:inline-block;*display:inline;*margin-left:.3em;font-size:0;white-space:nowrap;vertical-align:middle;*zoom:1}.btn-group:first-child{*margin-left:0}.btn-group{margin-left:5px}.btn-group-first{margin-left:0}.btn-toolbar{margin-top:10px;margin-bottom:10px;font-size:0}.btn-toolbar .btn .btn-group{margin-left:5px}.btn-toolbar .btn-first,.btn-toolbar .btn-group-first{margin-left:0}.btn-group .btn{position:relative;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.btn-group .btn{margin-left:-1px}.btn-group .btn-first{margin-left:0}.btn-group .btn,.btn-group .dropdown-menu{font-size:14px}.btn-group .dropdown-menu .divider{_margin:5px 0 -5px}.btn-group .btn-mini{font-size:11px}.btn-group .btn-small{font-size:12px}.btn-group .btn-large{font-size:16px}.btn-group>.btn:first-child{margin-left:0;-webkit-border-bottom-left-radius:4px;border-bottom-left-radius:4px;-webkit-border-top-left-radius:4px;border-top-left-radius:4px;-moz-border-radius-bottomleft:4px;-moz-border-radius-topleft:4px}.btn-group>.btn:last-child,.btn-group>.dropdown-toggle{-webkit-border-top-right-radius:4px;border-top-right-radius:4px;-webkit-border-bottom-right-radius:4px;border-bottom-right-radius:4px;-moz-border-radius-topright:4px;-moz-border-radius-bottomright:4px}.btn-group>.btn.large:first-child{margin-left:0;-webkit-border-bottom-left-radius:6px;border-bottom-left-radius:6px;-webkit-border-top-left-radius:6px;border-top-left-radius:6px;-moz-border-radius-bottomleft:6px;-moz-border-radius-topleft:6px}.btn-group>.btn.large:last-child,.btn-group>.large.dropdown-toggle{-webkit-border-top-right-radius:6px;border-top-right-radius:6px;-webkit-border-bottom-right-radius:6px;border-bottom-right-radius:6px;-moz-border-radius-topright:6px;-moz-border-radius-bottomright:6px}.btn-group>.btn:hover,.btn-group>.btn:focus,.btn-group>.btn:active,.btn-group>.btn.active{z-index:2}.btn-group .dropdown-toggle:active,.btn-group.open .dropdown-toggle{outline:0}.btn-group .dropdown-toggle{padding-right:9px;padding-left:9px;-webkit-box-shadow:inset 1px 0 0 rgba(255,255,255,0.125),inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.05);-moz-box-shadow:inset 1px 0 0 rgba(255,255,255,0.125),inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.05);box-shadow:inset 1px 0 0 rgba(255,255,255,0.125),inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.05)}.btn-group .btn-mini-dropdown-toggle{padding-right:7px;padding-left:7px}.btn-group .btn-large-dropdown-toggle{padding-right:17px;padding-left:17px}.btn-group-open .dropdown-toggle{background-image:none;-webkit-box-shadow:inset 0 2px 4px rgba(0,0,0,0.15),0 1px 2px rgba(0,0,0,0.05);-moz-box-shadow:inset 0 2px 4px rgba(0,0,0,0.15),0 1px 2px rgba(0,0,0,0.05);box-shadow:inset 0 2px 4px rgba(0,0,0,0.15),0 1px 2px rgba(0,0,0,0.05)}.btn-group-open .btn-dropdown-toggle{background-color:#e6e6e6}.btn-group-open .btn-primary-dropdown-toggle{background-color:#04c}.btn-group-open .btn-warning-dropdown-toggle{background-color:#f89406}.btn-group-open .btn-danger-dropdown-toggle{background-color:#bd362f}.btn-group-open .btn-success-dropdown-toggle{background-color:#51a351}.btn-group-open .btn-info-dropdown-toggle{background-color:#2f96b4}.btn-group-open .btn-inverse-dropdown-toggle{background-color:#222}.btn .caret{_display:inline;_height:8px;margin-top:8px;margin-left:0;_vertical-align:middle;_zoom:1}.btn-mini .caret,.btn-small .caret{_height:10px}.btn-mini .caret,.btn-small .caret,.btn-large .caret{margin-top:6px}.btn-large .caret{_height:9px;border-top-width:5px;border-right-width:5px;border-left-width:5px}.dropup .btn-large .caret{border-bottom-width:5px}.btn-primary .caret,.btn-warning .caret,.btn-danger .caret,.btn-info .caret,.btn-success .caret,.btn-inverse .caret{border-top-color:#fff;border-bottom-color:#fff}.btn-group-vertical{display:inline-block;*display:inline;*zoom:1}.btn-group-vertical .btn{display:block;float:none;width:100%;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.btn-group-vertical .btn+.btn{margin-top:-1px;margin-left:0}.btn-group-vertical .btn:first-child{-webkit-border-radius:4px 4px 0 0;-moz-border-radius:4px 4px 0 0;border-radius:4px 4px 0 0}.btn-group-vertical .btn:last-child{-webkit-border-radius:0 0 4px 4px;-moz-border-radius:0 0 4px 4px;border-radius:0 0 4px 4px}.btn-group-vertical .btn-large:first-child{-webkit-border-radius:6px 6px 0 0;-moz-border-radius:6px 6px 0 0;border-radius:6px 6px 0 0}.btn-group-vertical .btn-large:last-child{-webkit-border-radius:0 0 6px 6px;-moz-border-radius:0 0 6px 6px;border-radius:0 0 6px 6px}.alert{padding:8px 35px 8px 14px;margin-bottom:20px;color:#c09853;text-shadow:0 1px 0 rgba(255,255,255,0.5);background-color:#fcf8e3;border:1px solid #fbeed5;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}.alert h4{*display:inline;margin:0;*zoom:1}.alert .close{position:relative;top:-2px;right:-21px;line-height:20px}.alert-success{color:#468847;background-color:#dff0d8;border-color:#d6e9c6}.alert-danger,.alert-error{color:#b94a48;background-color:#f2dede;border-color:#eed3d7}.alert-info{color:#3a87ad;background-color:#d9edf7;border-color:#bce8f1}.alert-block{padding-top:14px;padding-bottom:14px}.alert-block p,.alert-block ul{margin-bottom:0}.alert-block p+p{margin-top:5px}.nav{margin-bottom:20px;margin-left:0;list-style:none}.nav li a{display:block;*zoom:1}.nav * li a{display:inline}.nav li * a{display:inline}.nav li a:hover{text-decoration:none;background-color:#eee}.nav * li a:hover{text-decoration:inherit;background-color:inherit}.nav li * a:hover{text-decoration:inherit;background-color:inherit}.nav>.pull-right{float:right}.nav-header{display:block;padding:3px 15px;font-size:11px;font-weight:bold;line-height:20px;color:#999;text-shadow:0 1px 0 rgba(255,255,255,0.5);text-transform:uppercase;*zoom:1}.nav .nav-header{margin-top:9px}.nav-list{padding-right:15px;padding-left:15px;margin-bottom:0}.nav-list li a,.nav-list .nav-header{margin-right:-15px;margin-left:-15px;text-shadow:0 1px 0 rgba(255,255,255,0.5)}.nav-list li a{padding:3px 15px}.nav-list * li a{padding:0;margin-right:0;margin-left:0;text-shadow:none}.nav-list li * a{padding:0;margin-right:0;margin-left:0;text-shadow:none}.nav-list .active a,.nav-list .active a:hover{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.2);background-color:#08c}.nav-list * .active a,.nav-list * .active a:hover{color:inherit;text-shadow:none;background-color:inherit}.nav-list .active * a,.nav-list .active * a:hover{color:inherit;text-shadow:none;background-color:inherit}.nav-list [class^="icon-"],.nav-list [class*=" icon-"]{margin-right:2px}.nav-list .divider{*width:100%;height:1px;margin:9px 1px;*margin:-5px 0 5px;overflow:hidden;background-color:#e5e5e5;border-bottom:1px solid #fff}.nav-tabs,.nav-pills{*zoom:1}.nav-tabs:before,.nav-pills:before,.nav-tabs:after,.nav-pills:after{display:table;line-height:0;content:""}.nav-tabs:after,.nav-pills:after{clear:both}.nav-tabs li,.nav-pills li{float:left}.nav-tabs * li,.nav-pills * li{float:none}.nav-tabs li a,.nav-pills li a{padding-right:12px;padding-left:12px;margin-right:2px;line-height:14px}.nav-tabs * li a,.nav-pills * li a{padding:0;margin:0;line-height:auto}.nav-tabs{border-bottom:1px solid #ddd}.nav-tabs li{margin-bottom:-1px;*background-color:#fff;*zoom:1}.nav-tabs * li{zoom:normal}.nav-tabs li a{*display:inline;padding-top:8px;padding-bottom:8px;line-height:20px;*background-color:#fffffe;border:1px solid transparent;*border:1px solid #fffffe;-webkit-border-radius:4px 4px 0 0;-moz-border-radius:4px 4px 0 0;border-radius:4px 4px 0 0;*filter:chroma(color=#fffffe);*zoom:1}.nav-tabs li a:hover{border-color:#eee #eee #ddd}.nav-tabs * li a{padding:0;line-height:auto;background-color:inherit;border:0;filter:none;zoom:normal}.nav-tabs .active a,.nav-tabs .active a:hover{color:#555;cursor:default;background-color:#fff;border:1px solid #ddd;*border:1px solid #999;border-bottom-color:transparent}.nav-tabs * .active a,.nav-tabs * .active a:hover{color:inherit;background-color:inherit;border:0}.nav-pills li{*background-color:#fff;*zoom:1}.nav-pills * li{background-color:inherit;zoom:normal}.nav-pills li a{*display:inline;padding-top:8px;padding-bottom:8px;margin-top:2px;margin-bottom:2px;-webkit-border-radius:5px;-moz-border-radius:5px;border-radius:5px;*zoom:1}.nav-pills * li a{padding:0;margin:0;-webkit-border-radius:none;-moz-border-radius:none;border-radius:none;zoom:normal}.nav-pills .active a,.nav-pills .active a:hover{color:#fff;background-color:#08c}.nav-pills * .active a,.nav-pills * .active a:hover{color:inherit;background-color:inherit}.nav-stacked>li{float:none}.nav-stacked>li>a{margin-right:0}.nav-tabs.nav-stacked{border-bottom:0}.nav-tabs.nav-stacked>li>a{border:1px solid #ddd;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.nav-tabs.nav-stacked>li:first-child>a{-webkit-border-top-right-radius:4px;border-top-right-radius:4px;-webkit-border-top-left-radius:4px;border-top-left-radius:4px;-moz-border-radius-topright:4px;-moz-border-radius-topleft:4px}.nav-tabs.nav-stacked>li:last-child>a{-webkit-border-bottom-right-radius:4px;border-bottom-right-radius:4px;-webkit-border-bottom-left-radius:4px;border-bottom-left-radius:4px;-moz-border-radius-bottomright:4px;-moz-border-radius-bottomleft:4px}.nav-tabs.nav-stacked>li>a:hover{z-index:2;border-color:#ddd}.nav-pills.nav-stacked>li>a{margin-bottom:3px}.nav-pills.nav-stacked>li:last-child>a{margin-bottom:1px}.nav-tabs .dropdown-menu{-webkit-border-radius:0 0 6px 6px;-moz-border-radius:0 0 6px 6px;border-radius:0 0 6px 6px}.nav-tabs .dropdown-menu .divider{*width:100%;height:1px;margin:9px 1px;*margin:-5px 0 5px;overflow:hidden;background-color:#e5e5e5;border-bottom:1px solid #fff}.nav-tabs .dropdown-menu li a{display:block;padding:3px 20px;clear:both;font-weight:normal;line-height:20px;color:#333;white-space:nowrap;*zoom:1}.nav-tabs .dropdown-menu li a:hover,.nav-tabs .dropdown-menu li a:focus,.nav-tabs .dropdown-menu .dropdown-submenu:hover a,.nav-tabs .dropdown-menu .dropdown-submenu-hover a{color:#fff;text-decoration:none;background-color:#0081c2;background-image:-moz-linear-gradient(top,#08c,#0077b3);background-image:-webkit-gradient(linear,0 0,0 100%,from(#08c),to(#0077b3));background-image:-webkit-linear-gradient(top,#08c,#0077b3);background-image:-o-linear-gradient(top,#08c,#0077b3);background-image:linear-gradient(to bottom,#08c,#0077b3);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff0088cc',endColorstr='#ff0077b3',GradientType=0)}.nav-tabs .dropdown-menu .dropdown-submenu-hover * a{*color:inherit;*background-color:#fffffe;*background-image:none;*filter:none;*filter:chroma(color=#fffffe)}.nav-pills .dropdown-menu{-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px}.nav-pills .dropdown-menu .divider{*width:100%;height:1px;margin:9px 1px;*margin:-5px 0 5px;overflow:hidden;background-color:#e5e5e5;border-bottom:1px solid #fff}.nav-pills .dropdown-menu li a{display:block;padding:3px 20px;clear:both;font-weight:normal;line-height:20px;color:#333;white-space:nowrap;*zoom:1}.nav-pills .dropdown-menu li a:hover,.nav-pills .dropdown-menu li a:focus,.nav-pills .dropdown-menu .dropdown-submenu:hover a,.nav-pills .dropdown-menu .dropdown-submenu-hover a{color:#fff;text-decoration:none;background-color:#0081c2;background-image:-moz-linear-gradient(top,#08c,#0077b3);background-image:-webkit-gradient(linear,0 0,0 100%,from(#08c),to(#0077b3));background-image:-webkit-linear-gradient(top,#08c,#0077b3);background-image:-o-linear-gradient(top,#08c,#0077b3);background-image:linear-gradient(to bottom,#08c,#0077b3);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff0088cc',endColorstr='#ff0077b3',GradientType=0)}.nav-pills .dropdown-menu .dropdown-submenu-hover * a{*color:inherit;*background-color:#fffffe;*background-image:none;*filter:none;*filter:chroma(color=#fffffe)}.nav-tabs .dropdown-toggle{*padding-top:10px;*padding-bottom:12px}.nav-pills .dropdown-toggle{*padding-top:8px;*padding-bottom:6px;*margin-bottom:3px}.nav .dropdown-toggle .caret{width:0;height:0;margin-top:6px;line-height:0;border-color:#08c transparent transparent;*border-color:#08c #fffffe #fffffe;border-top-color:#08c;border-bottom-color:#08c;border-style:solid;border-width:4px 4px 0;*filter:progid:DXImageTransform.Microsoft.Chroma(color=#fffffe)}.nav .dropdown-toggle:hover .caret{border-top-color:#005580;border-bottom-color:#005580}.nav-tabs .dropdown-toggle .caret{margin-top:8px}.nav .active .dropdown-toggle .caret{border-top-color:#fff;border-bottom-color:#fff}.nav-tabs .active .dropdown-toggle .caret{border-top-color:#555;border-bottom-color:#555}.nav .dropdown.active a:hover{cursor:pointer}.nav-tabs .open .dropdown-toggle,.nav-pills .open .dropdown-toggle,.nav li.dropdown-open.active a:hover{color:#fff;background-color:#999;border-color:#999}.nav li.dropdown-open .caret,.nav li.dropdown-open.active .caret,.nav li.dropdown-open a:hover .caret{width:0;height:0;line-height:0;border-color:#fff transparent transparent;*border-color:#fff #fffffe #fffffe;border-top-color:#fff;border-bottom-color:#fff;border-style:solid;border-width:4px 4px 0;opacity:1;filter:alpha(opacity=100);*filter:progid:DXImageTransform.Microsoft.Chroma(color=#fffffe)}.tabs-stacked .open>a:hover{border-color:#999}.tabbable{*zoom:1}.tabbable:before,.tabbable:after{display:table;line-height:0;content:""}.tabbable:after{clear:both}.tab-content{overflow:auto}.tabs-below .nav-tabs,.tabs-right .nav-tabs,.tabs-left .nav-tabs{border-bottom:0}.tab-content .tab-pane,.pill-content .pill-pane{display:none;*background-color:#fff;*zoom:1}.tab-content .active,.pill-content .active{display:block}.tabs-below .nav-tabs{border-top:1px solid #ddd}.tabs-below * .nav-tabs{border-top:0}.tabs-below .nav-tabs li{margin-top:-1px;margin-bottom:0}.tabs-below * .nav-tabs li{margin-top:0;margin-bottom:-1px}.tabs-below .nav-tabs li a{-webkit-border-radius:0 0 4px 4px;-moz-border-radius:0 0 4px 4px;border-radius:0 0 4px 4px}.tabs-below .nav-tabs li a:hover{border-top-color:#ddd;border-bottom-color:#fff}.tabs-below * .nav-tabs li a{-webkit-border-radius:none;-moz-border-radius:none;border-radius:none}.tabs-below * .nav-tabs li a:hover{border-top-color:inherit;border-bottom-color:inherit}.tabs-below .nav-tabs .active a,.tabs-below .nav-tabs .active a:hover{border-color:transparent #999 #999 #999}.tabs-below * .nav-tabs .active a,.tabs-below * .nav-tabs .active a:hover{border-color:inherit}.tabs-left>.nav-tabs>li,.tabs-right>.nav-tabs>li{float:none}.tabs-left>.nav-tabs>li>a,.tabs-right>.nav-tabs>li>a{min-width:74px;margin-right:0;margin-bottom:3px}.tabs-left>.nav-tabs{float:left;margin-right:19px;border-right:1px solid #ddd}.tabs-left>.nav-tabs>li>a{margin-right:-1px;-webkit-border-radius:4px 0 0 4px;-moz-border-radius:4px 0 0 4px;border-radius:4px 0 0 4px}.tabs-left>.nav-tabs>li>a:hover{border-color:#eee #ddd #eee #eee}.tabs-left>.nav-tabs .active>a,.tabs-left>.nav-tabs .active>a:hover{border-color:#ddd transparent #ddd #ddd;*border-right-color:#fff}.tabs-right>.nav-tabs{float:right;margin-left:19px;border-left:1px solid #ddd}.tabs-right>.nav-tabs>li>a{margin-left:-1px;-webkit-border-radius:0 4px 4px 0;-moz-border-radius:0 4px 4px 0;border-radius:0 4px 4px 0}.tabs-right>.nav-tabs>li>a:hover{border-color:#eee #eee #eee #ddd}.tabs-right>.nav-tabs .active>a,.tabs-right>.nav-tabs .active>a:hover{border-color:#ddd #ddd #ddd transparent;*border-left-color:#fff}.nav .disabled a{color:#999}.nav * .disabled a{color:inherit}.nav .disabled a:hover{text-decoration:none;cursor:default;background-color:transparent}.nav * .disabled a:hover{text-decoration:underline;cursor:default;background-color:inherit}.navbar{*position:relative;*z-index:2;margin-bottom:20px;overflow:visible;color:#777}.navbar-inner{_height:40px;min-height:40px;padding-right:20px;padding-left:20px;background-color:#fafafa;background-image:-moz-linear-gradient(top,#fff,#f2f2f2);background-image:-webkit-gradient(linear,0 0,0 100%,from(#fff),to(#f2f2f2));background-image:-webkit-linear-gradient(top,#fff,#f2f2f2);background-image:-o-linear-gradient(top,#fff,#f2f2f2);background-image:linear-gradient(to bottom,#fff,#f2f2f2);background-repeat:repeat-x;border:1px solid #d4d4d4;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff',endColorstr='#fff2f2f2',GradientType=0);*zoom:1;-webkit-box-shadow:0 1px 4px rgba(0,0,0,0.065);-moz-box-shadow:0 1px 4px rgba(0,0,0,0.065);box-shadow:0 1px 4px rgba(0,0,0,0.065)}.navbar-inner:before,.navbar-inner:after{display:table;line-height:0;content:""}.navbar-inner:after{clear:both}.navbar .container{width:auto}.nav-collapse.collapse{height:auto;overflow:visible}.navbar .brand{display:block;float:left;padding:10px 20px 10px;margin-left:-20px;_margin-left:-10px;font-size:20px;font-weight:200;color:#777;text-shadow:0 1px 0 #fff}.navbar .brand:hover{text-decoration:none}.navbar .container-fluid .brand{_margin-left:-10px}.navbar-text{margin-bottom:0;line-height:40px}.navbar-link{color:#777}.navbar-link:hover{color:#333}.navbar .divider-vertical{height:40px;margin:0 9px;border-right:1px solid #fff;border-left:1px solid #f2f2f2}.navbar .btn,.navbar .btn-group{margin-top:5px}.navbar .btn-group .btn,.navbar .input-prepend .btn,.navbar .input-append .btn{margin-top:0}.navbar-form{margin-bottom:0;*zoom:1}.navbar-form:before,.navbar-form:after{display:table;line-height:0;content:""}.navbar-form:after{clear:both}.navbar-form input,.navbar-form select,.navbar-form .radio,.navbar-form .checkbox{margin-top:5px}.navbar-form input,.navbar-form select,.navbar-form .btn{display:inline-block;margin-bottom:0}.navbar-form input[type="image"],.navbar-form input[type="checkbox"],.navbar-form input[type="radio"]{margin-top:3px}.navbar-form .input-append,.navbar-form .input-prepend{margin-top:6px;white-space:nowrap}.navbar-form .input-append input,.navbar-form .input-prepend input{margin-top:0}.navbar-search{position:relative;float:left;margin-top:5px;margin-bottom:0}.navbar-search .search-query{padding:4px 14px;margin-bottom:0;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:13px;font-weight:normal;line-height:1;-webkit-border-radius:15px;-moz-border-radius:15px;border-radius:15px}.navbar-static-top{position:static;margin-bottom:0}.navbar-static-top .navbar-inner{-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.navbar-fixed-top,.navbar-fixed-bottom{position:fixed;right:0;left:0;z-index:1030;margin-bottom:0}.navbar-fixed-top .navbar-inner,.navbar-static-top .navbar-inner{border-width:0 0 1px}.navbar-fixed-bottom .navbar-inner{border-width:1px 0 0}.navbar-fixed-top .navbar-inner,.navbar-fixed-bottom .navbar-inner{padding-right:0;padding-left:0;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.navbar-static-top .container,.navbar-fixed-top .container,.navbar-fixed-bottom .container{width:940px}.navbar-fixed-top{top:0}.navbar-fixed-top .navbar-inner,.navbar-static-top .navbar-inner{-webkit-box-shadow:0 1px 10px rgba(0,0,0,0.1);-moz-box-shadow:0 1px 10px rgba(0,0,0,0.1);box-shadow:0 1px 10px rgba(0,0,0,0.1)}.navbar-fixed-bottom{bottom:0}.navbar-fixed-bottom .navbar-inner{-webkit-box-shadow:0 -1px 10px rgba(0,0,0,0.1);-moz-box-shadow:0 -1px 10px rgba(0,0,0,0.1);box-shadow:0 -1px 10px rgba(0,0,0,0.1)}.navbar .nav{position:relative;left:0;display:block;float:left;margin:0 10px 0 0}.navbar .nav.pull-right{float:right;margin-right:0}.navbar .nav li{float:left}.navbar .nav * li{float:none}.navbar .nav li a{*display:inline;float:none;padding:10px 15px 10px;color:#777;text-decoration:none;text-shadow:0 1px 0 #fff;*zoom:1}.navbar .nav * li a{padding:0;color:inherit;text-decoration:inherit;text-shadow:inherit}.navbar .nav li * a{padding:0;color:inherit;text-decoration:inherit;text-shadow:inherit}.navbar .nav .dropdown-toggle{_padding:12px}.navbar .nav .dropdown-toggle .caret{margin-top:8px}.navbar .nav li a:focus,.navbar .nav li a:hover{color:#333;text-decoration:none;background-color:transparent}.navbar .nav * li a:focus,.navbar .nav * li a:hover{color:inherit;text-decoration:inherit;background-color:inherit}.navbar .nav .active a,.navbar .nav .active a:hover,.navbar .nav .active a:focus{color:#555;text-decoration:none;background-color:#e5e5e5;-webkit-box-shadow:inset 0 3px 8px rgba(0,0,0,0.125);-moz-box-shadow:inset 0 3px 8px rgba(0,0,0,0.125);box-shadow:inset 0 3px 8px rgba(0,0,0,0.125)}.navbar .nav * .active a,.navbar .nav * .active a:hover,.navbar .nav * .active a:focus{color:inherit;text-decoration:inherit;background-color:inherit;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none}.navbar .btn-navbar{display:none;float:right;padding:7px 10px;margin-right:5px;margin-left:5px;color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.25);background-color:#ededed;*background-color:#e5e5e5;background-image:-moz-linear-gradient(top,#f2f2f2,#e5e5e5);background-image:-webkit-gradient(linear,0 0,0 100%,from(#f2f2f2),to(#e5e5e5));background-image:-webkit-linear-gradient(top,#f2f2f2,#e5e5e5);background-image:-o-linear-gradient(top,#f2f2f2,#e5e5e5);background-image:linear-gradient(to bottom,#f2f2f2,#e5e5e5);background-repeat:repeat-x;border-color:#e5e5e5 #e5e5e5 #bfbfbf;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2f2f2',endColorstr='#ffe5e5e5',GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,0.1),0 1px 0 rgba(255,255,255,0.075);-moz-box-shadow:inset 0 1px 0 rgba(255,255,255,0.1),0 1px 0 rgba(255,255,255,0.075);box-shadow:inset 0 1px 0 rgba(255,255,255,0.1),0 1px 0 rgba(255,255,255,0.075)}.navbar .btn-navbar:hover,.navbar .btn-navbar:active,.navbar .btn-navbar.active,.navbar .btn-navbar.disabled,.navbar .btn-navbar[disabled]{color:#fff;background-color:#e5e5e5;*background-color:#d9d9d9}.navbar .btn-navbar:active,.navbar .btn-navbar.active{background-color:#ccc \9}.navbar .btn-navbar .icon-bar{display:block;width:18px;height:2px;background-color:#f5f5f5;-webkit-border-radius:1px;-moz-border-radius:1px;border-radius:1px;-webkit-box-shadow:0 1px 0 rgba(0,0,0,0.25);-moz-box-shadow:0 1px 0 rgba(0,0,0,0.25);box-shadow:0 1px 0 rgba(0,0,0,0.25)}.btn-navbar .icon-bar+.icon-bar{margin-top:3px}.navbar .nav li .dropdown-menu:before{position:absolute;top:-7px;left:9px;display:inline-block;border-right:7px solid transparent;border-bottom:7px solid #ccc;border-left:7px solid transparent;border-bottom-color:rgba(0,0,0,0.2);content:''}.navbar .nav li .dropdown-menu:after{position:absolute;top:-6px;left:10px;display:inline-block;border-right:6px solid transparent;border-bottom:6px solid #fff;border-left:6px solid transparent;content:''}.navbar .nav li .dropdown-menu .divider{*width:100%;height:1px;margin:9px 1px;*margin:-5px 0 5px;overflow:hidden;background-color:#e5e5e5;border-bottom:1px solid #fff}.navbar .nav li .dropdown-menu li a{display:block;padding:3px 20px;clear:both;font-weight:normal;line-height:20px;color:#333;white-space:nowrap;*zoom:1}.navbar .nav li .dropdown-menu li a:hover,.navbar .nav li .dropdown-menu li a:focus,.navbar .nav li .dropdown-menu .dropdown-submenu:hover a,.navbar .nav li .dropdown-menu .dropdown-submenu-hover a{color:#fff;text-decoration:none;background-color:#0081c2;background-image:-moz-linear-gradient(top,#08c,#0077b3);background-image:-webkit-gradient(linear,0 0,0 100%,from(#08c),to(#0077b3));background-image:-webkit-linear-gradient(top,#08c,#0077b3);background-image:-o-linear-gradient(top,#08c,#0077b3);background-image:linear-gradient(to bottom,#08c,#0077b3);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff0088cc',endColorstr='#ff0077b3',GradientType=0)}.navbar .nav li .dropdown-menu .dropdown-submenu-hover * a{*color:inherit;*background-color:#fffffe;*background-image:none;*filter:none;*filter:chroma(color=#fffffe)}.navbar-fixed-bottom .nav li .dropdown-menu:before{top:auto;bottom:-7px;border-top:7px solid #ccc;border-bottom:0;border-top-color:rgba(0,0,0,0.2)}.navbar-fixed-bottom .nav li .dropdown-menu:after{top:auto;bottom:-6px;border-top:6px solid #fff;border-bottom:0}.navbar .nav li.dropdown.open .dropdown-toggle,.navbar .nav li.dropdown.active .dropdown-toggle,.navbar .nav li.dropdown.open.active .dropdown-toggle{color:#555;background-color:#e5e5e5}.navbar .nav li.dropdown .dropdown-toggle .caret{border-top-color:#777;border-bottom-color:#777}.navbar .nav li.dropdown.open .dropdown-toggle .caret,.navbar .nav li.dropdown.active .dropdown-toggle .caret,.navbar .nav li.dropdown.open.active .dropdown-toggle .caret{border-top-color:#555;border-bottom-color:#555}.navbar .pull-right li .dropdown-menu,.navbar .nav li .dropdown-menu.pull-right{right:0;left:auto}.navbar .pull-right li .dropdown-menu:before,.navbar .nav li .dropdown-menu.pull-right:before{right:12px;left:auto}.navbar .pull-right li .dropdown-menu:after,.navbar .nav li .dropdown-menu.pull-right:after{right:13px;left:auto}.navbar .pull-right li .dropdown-menu .dropdown-menu,.navbar .nav li .dropdown-menu.pull-right .dropdown-menu{right:100%;left:auto;margin-right:-1px;margin-left:0;-webkit-border-radius:6px 0 6px 6px;-moz-border-radius:6px 0 6px 6px;border-radius:6px 0 6px 6px}.navbar-inverse{color:#999}.navbar-inverse .navbar-inner{background-color:#1b1b1b;background-image:-moz-linear-gradient(top,#222,#111);background-image:-webkit-gradient(linear,0 0,0 100%,from(#222),to(#111));background-image:-webkit-linear-gradient(top,#222,#111);background-image:-o-linear-gradient(top,#222,#111);background-image:linear-gradient(to bottom,#222,#111);background-repeat:repeat-x;border-color:#252525;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff222222',endColorstr='#ff111111',GradientType=0)}.navbar-inverse .brand,.navbar-inverse .nav li a{color:#999;text-shadow:0 -1px 0 rgba(0,0,0,0.25)}.navbar-inverse .brand:hover,.navbar-inverse .nav li a:hover{color:#fff}.navbar-inverse .nav li a:focus,.navbar-inverse .nav li a:hover{color:#fff;background-color:transparent}.navbar-inverse .nav .active a,.navbar-inverse .nav .active a:hover,.navbar-inverse .nav .active a:focus{color:#fff;background-color:#111}.navbar-inverse .navbar-link{color:#999}.navbar-inverse .navbar-link:hover{color:#fff}.navbar-inverse .divider-vertical{border-right-color:#222;border-left-color:#111}.navbar-inverse .nav li.dropdown.open>.dropdown-toggle,.navbar-inverse .nav li.dropdown.active>.dropdown-toggle,.navbar-inverse .nav li.dropdown.open.active>.dropdown-toggle{color:#fff;background-color:#111}.navbar-inverse .nav li.dropdown>.dropdown-toggle .caret{border-top-color:#999;border-bottom-color:#999}.navbar-inverse .nav li.dropdown.open>.dropdown-toggle .caret,.navbar-inverse .nav li.dropdown.active>.dropdown-toggle .caret,.navbar-inverse .nav li.dropdown.open.active>.dropdown-toggle .caret{border-top-color:#fff;border-bottom-color:#fff}.navbar-inverse .navbar-search .search-query{color:#fff;background-color:#515151;border-color:#111;-webkit-box-shadow:inset 0 1px 2px rgba(0,0,0,0.1),0 1px 0 rgba(255,255,255,0.15);-moz-box-shadow:inset 0 1px 2px rgba(0,0,0,0.1),0 1px 0 rgba(255,255,255,0.15);box-shadow:inset 0 1px 2px rgba(0,0,0,0.1),0 1px 0 rgba(255,255,255,0.15);-webkit-transition:none;-moz-transition:none;-o-transition:none;transition:none}.navbar-inverse .navbar-search .search-query:-moz-placeholder{color:#ccc}.navbar-inverse .navbar-search .search-query:-ms-input-placeholder{color:#ccc}.navbar-inverse .navbar-search .search-query::-webkit-input-placeholder{color:#ccc}.navbar-inverse .navbar-search .search-query:focus,.navbar-inverse .navbar-search .search-query.focused{padding:5px 15px;color:#333;text-shadow:0 1px 0 #fff;background-color:#fff;border:0;outline:0;-webkit-box-shadow:0 0 3px rgba(0,0,0,0.15);-moz-box-shadow:0 0 3px rgba(0,0,0,0.15);box-shadow:0 0 3px rgba(0,0,0,0.15)}.navbar-inverse .btn-navbar{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.25);background-color:#0e0e0e;*background-color:#040404;background-image:-moz-linear-gradient(top,#151515,#040404);background-image:-webkit-gradient(linear,0 0,0 100%,from(#151515),to(#040404));background-image:-webkit-linear-gradient(top,#151515,#040404);background-image:-o-linear-gradient(top,#151515,#040404);background-image:linear-gradient(to bottom,#151515,#040404);background-repeat:repeat-x;border-color:#040404 #040404 #000;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff151515',endColorstr='#ff040404',GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.navbar-inverse .btn-navbar:hover,.navbar-inverse .btn-navbar:active,.navbar-inverse .btn-navbar.active,.navbar-inverse .btn-navbar.disabled,.navbar-inverse .btn-navbar[disabled]{color:#fff;background-color:#040404;*background-color:#000}.navbar-inverse .btn-navbar:active,.navbar-inverse .btn-navbar.active{background-color:#000 \9}.breadcrumb{padding:8px 15px;margin:0 0 20px;list-style:none;background-color:#f5f5f5;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}.breadcrumb li{display:inline-block;*display:inline;text-shadow:0 1px 0 #fff;*zoom:1}.breadcrumb .divider{padding:0 5px;color:#ccc}.breadcrumb .active{color:#999;background-color:#f5f5f5}.pagination{margin:20px 0}.pagination ul{display:inline-block;*display:inline;margin-bottom:0;margin-left:0;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;*zoom:1;-webkit-box-shadow:0 1px 2px rgba(0,0,0,0.05);-moz-box-shadow:0 1px 2px rgba(0,0,0,0.05);box-shadow:0 1px 2px rgba(0,0,0,0.05)}.pagination ul li{display:inline}.pagination ul li a,.pagination ul li span{float:left;padding:4px 12px;line-height:20px;text-decoration:none;background-color:#fff;border:1px solid #ddd;border-left-width:0}.pagination ul li a:hover,.pagination ul .active a,.pagination ul .active span{background-color:#f5f5f5}.pagination ul .active a,.pagination ul .active span{color:#999;cursor:default}.pagination ul .disabled span,.pagination ul .disabled a,.pagination ul .disabled a:hover{color:#999;cursor:default;background-color:transparent}.pagination ul li.first-child a,.pagination ul li.first-child span{border-left-width:1px;-webkit-border-bottom-left-radius:4px;border-bottom-left-radius:4px;-webkit-border-top-left-radius:4px;border-top-left-radius:4px;-moz-border-radius-bottomleft:4px;-moz-border-radius-topleft:4px}.pagination ul li:last-child a,.pagination ul li:last-child span{-webkit-border-top-right-radius:4px;border-top-right-radius:4px;-webkit-border-bottom-right-radius:4px;border-bottom-right-radius:4px;-moz-border-radius-topright:4px;-moz-border-radius-bottomright:4px}.pagination-centered{text-align:center}.pagination-right{text-align:right}.pagination-large ul li a,.pagination-large ul li span{padding:11px 19px;font-size:17.5px}.pagination-large ul li:first-child a,.pagination-large ul li:first-child span{-webkit-border-bottom-left-radius:6px;border-bottom-left-radius:6px;-webkit-border-top-left-radius:6px;border-top-left-radius:6px;-moz-border-radius-bottomleft:6px;-moz-border-radius-topleft:6px}.pagination-large ul li:last-child a,.pagination-large ul li:last-child span{-webkit-border-top-right-radius:6px;border-top-right-radius:6px;-webkit-border-bottom-right-radius:6px;border-bottom-right-radius:6px;-moz-border-radius-topright:6px;-moz-border-radius-bottomright:6px}.pagination-mini ul li:first-child a,.pagination-small ul li:first-child a,.pagination-mini ul li:first-child span,.pagination-small ul li:first-child span{-webkit-border-bottom-left-radius:3px;border-bottom-left-radius:3px;-webkit-border-top-left-radius:3px;border-top-left-radius:3px;-moz-border-radius-bottomleft:3px;-moz-border-radius-topleft:3px}.pagination-mini ul li:last-child a,.pagination-small ul li:last-child a,.pagination-mini ul li:last-child span,.pagination-small ul li:last-child span{-webkit-border-top-right-radius:3px;border-top-right-radius:3px;-webkit-border-bottom-right-radius:3px;border-bottom-right-radius:3px;-moz-border-radius-topright:3px;-moz-border-radius-bottomright:3px}.pagination-small ul li a,.pagination-small ul li span{padding:2px 10px;font-size:11.9px}.pagination-mini ul li a,.pagination-mini ul li span{padding:1px 6px;font-size:10.5px}.pager{margin:20px 0;text-align:center;list-style:none;*zoom:1}.pager:before,.pager:after{display:table;line-height:0;content:""}.pager:after{clear:both}.pager li{display:inline}.pager li a,.pager li span{display:inline-block;padding:5px 14px;background-color:#fff;border:1px solid #ddd;-webkit-border-radius:15px;-moz-border-radius:15px;border-radius:15px}.pager li a:hover{text-decoration:none;background-color:#f5f5f5}.pager .next a,.pager .next span{float:right}.pager .previous a,.pager .previous span{float:left}.pager .disabled a,.pager .disabled a:hover,.pager .disabled span{color:#999;cursor:default;background-color:#fff}.tooltip{position:absolute;z-index:1030;display:block;padding:5px;font-size:11px;opacity:0;filter:alpha(opacity=0);visibility:visible}.tooltip.in{opacity:.8;filter:alpha(opacity=80)}.tooltip.top{margin-top:-3px}.tooltip.right{margin-left:3px}.tooltip.bottom{margin-top:3px}.tooltip.left{margin-left:-3px}.tooltip-inner{max-width:200px;padding:3px 8px;color:#fff;text-align:center;text-decoration:none;background-color:#000;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}.tooltip-arrow{position:absolute;width:0;height:0;border-color:transparent;border-style:solid}.tooltip.top .tooltip-arrow{bottom:0;left:50%;width:0;height:0;margin-left:-5px;line-height:0;border-color:#000 transparent transparent;*border-color:#000 #fffffe #fffffe;border-top-color:#000;border-style:solid;border-width:5px 5px 0;*filter:progid:DXImageTransform.Microsoft.Chroma(color=#fffffe)}.tooltip.right .tooltip-arrow{top:50%;left:0;width:0;height:0;margin-top:-5px;line-height:0;border-color:transparent #000 transparent transparent;*border-color:#fffffe #000 #fffffe #fffffe;border-right-color:#000;border-style:solid;border-width:5px 5px 5px 0;*filter:progid:DXImageTransform.Microsoft.Chroma(color=#fffffe)}.tooltip.left .tooltip-arrow{top:50%;right:0;width:0;height:0;margin-top:-5px;line-height:0;border-color:transparent transparent transparent #000;*border-color:#fffffe #fffffe #fffffe #000;border-left-color:#000;border-style:solid;border-width:5px 0 5px 5px;*filter:progid:DXImageTransform.Microsoft.Chroma(color=#fffffe)}.tooltip.bottom .tooltip-arrow{top:0;left:50%;width:0;height:0;margin-left:-5px;line-height:0;border-color:transparent transparent #000;*border-color:#fffffe #fffffe #000;border-bottom-color:#000;border-style:solid;border-width:0 5px 5px;*filter:progid:DXImageTransform.Microsoft.Chroma(color=#fffffe)}.popover{position:absolute;top:0;left:0;z-index:1010;display:none;width:236px;padding:1px;background-color:#fff;border:1px solid #ccc;border:1px solid rgba(0,0,0,0.2);-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px;-webkit-box-shadow:0 5px 10px rgba(0,0,0,0.2);-moz-box-shadow:0 5px 10px rgba(0,0,0,0.2);box-shadow:0 5px 10px rgba(0,0,0,0.2);-webkit-background-clip:padding-box;-moz-background-clip:padding;background-clip:padding-box}.popover.top{margin-top:-10px}.popover.right{margin-left:10px}.popover.bottom{margin-top:10px}.popover.left{margin-left:-10px}.popover-title{padding:8px 14px;margin:0;font-size:14px;font-weight:normal;line-height:18px;background-color:#f7f7f7;border-bottom:1px solid #ebebeb;-webkit-border-radius:5px 5px 0 0;-moz-border-radius:5px 5px 0 0;border-radius:5px 5px 0 0}.popover-content{padding:9px 14px}.popover-content p,.popover-content ul,.popover-content ol{margin-bottom:0}.popover .arrow,.popover .arrow-after{position:absolute;display:inline-block;*display:inline;width:0;height:0;border-color:transparent;border-style:solid;*zoom:1}.popover .arrow-after{z-index:-1;content:""}.popover.top .arrow{*bottom:-12px;left:50%;width:0;height:0;margin-left:-10px;line-height:0;border-color:#ccc transparent transparent;*border-color:#ccc #fffffe #fffffe;border-top-color:#fff;border-style:solid;border-width:10px 10px 0;*filter:progid:DXImageTransform.Microsoft.Chroma(color=#fffffe)}.popover.top .arrow.arrow-after{bottom:-1px;left:-11px;border-top-color:rgba(0,0,0,0.25);border-width:11px 11px 0}.popover.right .arrow{top:50%;*left:-10px;width:0;height:0;margin-top:-10px;line-height:0;border-color:transparent #ccc transparent transparent;*border-color:#fffffe #ccc #fffffe #fffffe;border-right-color:#fff;border-style:solid;border-width:10px 10px 10px 0;*filter:progid:DXImageTransform.Microsoft.Chroma(color=#fffffe)}.popover.right .arrow-after{bottom:-11px;left:-1px;border-right-color:rgba(0,0,0,0.25);border-width:11px 11px 11px 0}.popover.bottom .arrow{*top:-10px;left:50%;width:0;height:0;margin-left:-10px;line-height:0;border-color:transparent transparent #ccc;*border-color:#fffffe #fffffe #ccc;border-bottom-color:#fff;border-style:solid;border-width:0 10px 10px;*filter:progid:DXImageTransform.Microsoft.Chroma(color=#fffffe)}.popover.bottom .arrow-after{top:-1px;left:-11px;border-bottom-color:rgba(0,0,0,0.25);border-width:0 11px 11px}.popover.left .arrow{top:50%;*right:-10px;width:0;height:0;margin-top:-10px;line-height:0;border-color:transparent transparent transparent #ccc;*border-color:#fffffe #fffffe #fffffe #ccc;border-left-color:#fff;border-style:solid;border-width:10px 0 10px 10px;*filter:progid:DXImageTransform.Microsoft.Chroma(color=#fffffe)}.popover.left .arrow-after{right:-1px;bottom:-11px;border-left-color:rgba(0,0,0,0.25);border-width:11px 0 11px 11px}.carousel{position:relative;margin-bottom:20px;line-height:1}.carousel-inner{position:relative;width:100%;overflow:hidden}.carousel .item{position:relative;display:none;-webkit-transition:.6s ease-in-out left;-moz-transition:.6s ease-in-out left;-o-transition:.6s ease-in-out left;transition:.6s ease-in-out left}.carousel .item img{display:block;*width:100%;*height:auto;line-height:1}.carousel .active,.carousel .next,.carousel .prev{display:block}.carousel .active{left:0}.carousel .next,.carousel .prev{position:absolute;top:0;width:100%}.carousel .next{left:100%}.carousel .prev{left:-100%}.carousel .next.left,.carousel .prev.right{left:0}.carousel .active.left{left:-100%}.carousel .active.right{left:100%}.carousel-control{position:absolute;top:40%;left:15px;width:40px;height:40px;margin-top:-20px;font-size:60px;font-weight:100;line-height:30px;color:#fff;text-align:center;background:#222;border:3px solid #fff;-webkit-border-radius:23px;-moz-border-radius:23px;border-radius:23px;opacity:.5;filter:alpha(opacity=50)}.carousel-control-right{right:15px;left:auto}.carousel-control-left{left:15px}.carousel-control:hover{color:#fff;text-decoration:none;opacity:.9;filter:alpha(opacity=90)}.carousel-caption{position:absolute;right:0;bottom:0;left:0;padding:15px;background:#333;background:rgba(0,0,0,0.75);_filter:alpha(opacity=70)}.carousel-caption h4,.carousel-caption p{line-height:20px;color:#fff}.carousel-caption h4{margin:0 0 5px}.carousel-caption p{margin-bottom:0}.pull-right{float:right}.pull-left{float:left}.hide{display:none}.show{display:block}.invisible{visibility:hidden}.affix{position:fixed} diff --git a/OpenAuth.Identity/wwwroot/lib/bootstrap/css/bootstrap-responsive.css b/OpenAuth.Identity/wwwroot/lib/bootstrap/css/bootstrap-responsive.css new file mode 100644 index 0000000..c0bba15 --- /dev/null +++ b/OpenAuth.Identity/wwwroot/lib/bootstrap/css/bootstrap-responsive.css @@ -0,0 +1,1109 @@ +/*! + * Bootstrap Responsive v2.3.2 + * + * Copyright 2013 Twitter, Inc + * Licensed under the Apache License v2.0 + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Designed and built with all the love in the world by @mdo and @fat. + */ + +.clearfix { + *zoom: 1; +} + +.clearfix:before, +.clearfix:after { + display: table; + line-height: 0; + content: ""; +} + +.clearfix:after { + clear: both; +} + +.hide-text { + font: 0/0 a; + color: transparent; + text-shadow: none; + background-color: transparent; + border: 0; +} + +.input-block-level { + display: block; + width: 100%; + min-height: 30px; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; +} + +@-ms-viewport { + width: device-width; +} + +.hidden { + display: none; + visibility: hidden; +} + +.visible-phone { + display: none !important; +} + +.visible-tablet { + display: none !important; +} + +.hidden-desktop { + display: none !important; +} + +.visible-desktop { + display: inherit !important; +} + +@media (min-width: 768px) and (max-width: 979px) { + .hidden-desktop { + display: inherit !important; + } + .visible-desktop { + display: none !important ; + } + .visible-tablet { + display: inherit !important; + } + .hidden-tablet { + display: none !important; + } +} + +@media (max-width: 767px) { + .hidden-desktop { + display: inherit !important; + } + .visible-desktop { + display: none !important; + } + .visible-phone { + display: inherit !important; + } + .hidden-phone { + display: none !important; + } +} + +.visible-print { + display: none !important; +} + +@media print { + .visible-print { + display: inherit !important; + } + .hidden-print { + display: none !important; + } +} + +@media (min-width: 1200px) { + .row { + margin-left: -30px; + *zoom: 1; + } + .row:before, + .row:after { + display: table; + line-height: 0; + content: ""; + } + .row:after { + clear: both; + } + [class*="span"] { + float: left; + min-height: 1px; + margin-left: 30px; + } + .container, + .navbar-static-top .container, + .navbar-fixed-top .container, + .navbar-fixed-bottom .container { + width: 1170px; + } + .span12 { + width: 1170px; + } + .span11 { + width: 1070px; + } + .span10 { + width: 970px; + } + .span9 { + width: 870px; + } + .span8 { + width: 770px; + } + .span7 { + width: 670px; + } + .span6 { + width: 570px; + } + .span5 { + width: 470px; + } + .span4 { + width: 370px; + } + .span3 { + width: 270px; + } + .span2 { + width: 170px; + } + .span1 { + width: 70px; + } + .offset12 { + margin-left: 1230px; + } + .offset11 { + margin-left: 1130px; + } + .offset10 { + margin-left: 1030px; + } + .offset9 { + margin-left: 930px; + } + .offset8 { + margin-left: 830px; + } + .offset7 { + margin-left: 730px; + } + .offset6 { + margin-left: 630px; + } + .offset5 { + margin-left: 530px; + } + .offset4 { + margin-left: 430px; + } + .offset3 { + margin-left: 330px; + } + .offset2 { + margin-left: 230px; + } + .offset1 { + margin-left: 130px; + } + .row-fluid { + width: 100%; + *zoom: 1; + } + .row-fluid:before, + .row-fluid:after { + display: table; + line-height: 0; + content: ""; + } + .row-fluid:after { + clear: both; + } + .row-fluid [class*="span"] { + display: block; + float: left; + width: 100%; + min-height: 30px; + margin-left: 2.564102564102564%; + *margin-left: 2.5109110747408616%; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + } + .row-fluid [class*="span"]:first-child { + margin-left: 0; + } + .row-fluid .controls-row [class*="span"] + [class*="span"] { + margin-left: 2.564102564102564%; + } + .row-fluid .span12 { + width: 100%; + *width: 99.94680851063829%; + } + .row-fluid .span11 { + width: 91.45299145299145%; + *width: 91.39979996362975%; + } + .row-fluid .span10 { + width: 82.90598290598291%; + *width: 82.8527914166212%; + } + .row-fluid .span9 { + width: 74.35897435897436%; + *width: 74.30578286961266%; + } + .row-fluid .span8 { + width: 65.81196581196582%; + *width: 65.75877432260411%; + } + .row-fluid .span7 { + width: 57.26495726495726%; + *width: 57.21176577559556%; + } + .row-fluid .span6 { + width: 48.717948717948715%; + *width: 48.664757228587014%; + } + .row-fluid .span5 { + width: 40.17094017094017%; + *width: 40.11774868157847%; + } + .row-fluid .span4 { + width: 31.623931623931625%; + *width: 31.570740134569924%; + } + .row-fluid .span3 { + width: 23.076923076923077%; + *width: 23.023731587561375%; + } + .row-fluid .span2 { + width: 14.52991452991453%; + *width: 14.476723040552828%; + } + .row-fluid .span1 { + width: 5.982905982905983%; + *width: 5.929714493544281%; + } + .row-fluid .offset12 { + margin-left: 105.12820512820512%; + *margin-left: 105.02182214948171%; + } + .row-fluid .offset12:first-child { + margin-left: 102.56410256410257%; + *margin-left: 102.45771958537915%; + } + .row-fluid .offset11 { + margin-left: 96.58119658119658%; + *margin-left: 96.47481360247316%; + } + .row-fluid .offset11:first-child { + margin-left: 94.01709401709402%; + *margin-left: 93.91071103837061%; + } + .row-fluid .offset10 { + margin-left: 88.03418803418803%; + *margin-left: 87.92780505546462%; + } + .row-fluid .offset10:first-child { + margin-left: 85.47008547008548%; + *margin-left: 85.36370249136206%; + } + .row-fluid .offset9 { + margin-left: 79.48717948717949%; + *margin-left: 79.38079650845607%; + } + .row-fluid .offset9:first-child { + margin-left: 76.92307692307693%; + *margin-left: 76.81669394435352%; + } + .row-fluid .offset8 { + margin-left: 70.94017094017094%; + *margin-left: 70.83378796144753%; + } + .row-fluid .offset8:first-child { + margin-left: 68.37606837606839%; + *margin-left: 68.26968539734497%; + } + .row-fluid .offset7 { + margin-left: 62.393162393162385%; + *margin-left: 62.28677941443899%; + } + .row-fluid .offset7:first-child { + margin-left: 59.82905982905982%; + *margin-left: 59.72267685033642%; + } + .row-fluid .offset6 { + margin-left: 53.84615384615384%; + *margin-left: 53.739770867430444%; + } + .row-fluid .offset6:first-child { + margin-left: 51.28205128205128%; + *margin-left: 51.175668303327875%; + } + .row-fluid .offset5 { + margin-left: 45.299145299145295%; + *margin-left: 45.1927623204219%; + } + .row-fluid .offset5:first-child { + margin-left: 42.73504273504273%; + *margin-left: 42.62865975631933%; + } + .row-fluid .offset4 { + margin-left: 36.75213675213675%; + *margin-left: 36.645753773413354%; + } + .row-fluid .offset4:first-child { + margin-left: 34.18803418803419%; + *margin-left: 34.081651209310785%; + } + .row-fluid .offset3 { + margin-left: 28.205128205128204%; + *margin-left: 28.0987452264048%; + } + .row-fluid .offset3:first-child { + margin-left: 25.641025641025642%; + *margin-left: 25.53464266230224%; + } + .row-fluid .offset2 { + margin-left: 19.65811965811966%; + *margin-left: 19.551736679396257%; + } + .row-fluid .offset2:first-child { + margin-left: 17.094017094017094%; + *margin-left: 16.98763411529369%; + } + .row-fluid .offset1 { + margin-left: 11.11111111111111%; + *margin-left: 11.004728132387708%; + } + .row-fluid .offset1:first-child { + margin-left: 8.547008547008547%; + *margin-left: 8.440625568285142%; + } + input, + textarea, + .uneditable-input { + margin-left: 0; + } + .controls-row [class*="span"] + [class*="span"] { + margin-left: 30px; + } + input.span12, + textarea.span12, + .uneditable-input.span12 { + width: 1156px; + } + input.span11, + textarea.span11, + .uneditable-input.span11 { + width: 1056px; + } + input.span10, + textarea.span10, + .uneditable-input.span10 { + width: 956px; + } + input.span9, + textarea.span9, + .uneditable-input.span9 { + width: 856px; + } + input.span8, + textarea.span8, + .uneditable-input.span8 { + width: 756px; + } + input.span7, + textarea.span7, + .uneditable-input.span7 { + width: 656px; + } + input.span6, + textarea.span6, + .uneditable-input.span6 { + width: 556px; + } + input.span5, + textarea.span5, + .uneditable-input.span5 { + width: 456px; + } + input.span4, + textarea.span4, + .uneditable-input.span4 { + width: 356px; + } + input.span3, + textarea.span3, + .uneditable-input.span3 { + width: 256px; + } + input.span2, + textarea.span2, + .uneditable-input.span2 { + width: 156px; + } + input.span1, + textarea.span1, + .uneditable-input.span1 { + width: 56px; + } + .thumbnails { + margin-left: -30px; + } + .thumbnails > li { + margin-left: 30px; + } + .row-fluid .thumbnails { + margin-left: 0; + } +} + +@media (min-width: 768px) and (max-width: 979px) { + .row { + margin-left: -20px; + *zoom: 1; + } + .row:before, + .row:after { + display: table; + line-height: 0; + content: ""; + } + .row:after { + clear: both; + } + [class*="span"] { + float: left; + min-height: 1px; + margin-left: 20px; + } + .container, + .navbar-static-top .container, + .navbar-fixed-top .container, + .navbar-fixed-bottom .container { + width: 724px; + } + .span12 { + width: 724px; + } + .span11 { + width: 662px; + } + .span10 { + width: 600px; + } + .span9 { + width: 538px; + } + .span8 { + width: 476px; + } + .span7 { + width: 414px; + } + .span6 { + width: 352px; + } + .span5 { + width: 290px; + } + .span4 { + width: 228px; + } + .span3 { + width: 166px; + } + .span2 { + width: 104px; + } + .span1 { + width: 42px; + } + .offset12 { + margin-left: 764px; + } + .offset11 { + margin-left: 702px; + } + .offset10 { + margin-left: 640px; + } + .offset9 { + margin-left: 578px; + } + .offset8 { + margin-left: 516px; + } + .offset7 { + margin-left: 454px; + } + .offset6 { + margin-left: 392px; + } + .offset5 { + margin-left: 330px; + } + .offset4 { + margin-left: 268px; + } + .offset3 { + margin-left: 206px; + } + .offset2 { + margin-left: 144px; + } + .offset1 { + margin-left: 82px; + } + .row-fluid { + width: 100%; + *zoom: 1; + } + .row-fluid:before, + .row-fluid:after { + display: table; + line-height: 0; + content: ""; + } + .row-fluid:after { + clear: both; + } + .row-fluid [class*="span"] { + display: block; + float: left; + width: 100%; + min-height: 30px; + margin-left: 2.7624309392265194%; + *margin-left: 2.709239449864817%; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + } + .row-fluid [class*="span"]:first-child { + margin-left: 0; + } + .row-fluid .controls-row [class*="span"] + [class*="span"] { + margin-left: 2.7624309392265194%; + } + .row-fluid .span12 { + width: 100%; + *width: 99.94680851063829%; + } + .row-fluid .span11 { + width: 91.43646408839778%; + *width: 91.38327259903608%; + } + .row-fluid .span10 { + width: 82.87292817679558%; + *width: 82.81973668743387%; + } + .row-fluid .span9 { + width: 74.30939226519337%; + *width: 74.25620077583166%; + } + .row-fluid .span8 { + width: 65.74585635359117%; + *width: 65.69266486422946%; + } + .row-fluid .span7 { + width: 57.18232044198895%; + *width: 57.12912895262725%; + } + .row-fluid .span6 { + width: 48.61878453038674%; + *width: 48.56559304102504%; + } + .row-fluid .span5 { + width: 40.05524861878453%; + *width: 40.00205712942283%; + } + .row-fluid .span4 { + width: 31.491712707182323%; + *width: 31.43852121782062%; + } + .row-fluid .span3 { + width: 22.92817679558011%; + *width: 22.87498530621841%; + } + .row-fluid .span2 { + width: 14.3646408839779%; + *width: 14.311449394616199%; + } + .row-fluid .span1 { + width: 5.801104972375691%; + *width: 5.747913483013988%; + } + .row-fluid .offset12 { + margin-left: 105.52486187845304%; + *margin-left: 105.41847889972962%; + } + .row-fluid .offset12:first-child { + margin-left: 102.76243093922652%; + *margin-left: 102.6560479605031%; + } + .row-fluid .offset11 { + margin-left: 96.96132596685082%; + *margin-left: 96.8549429881274%; + } + .row-fluid .offset11:first-child { + margin-left: 94.1988950276243%; + *margin-left: 94.09251204890089%; + } + .row-fluid .offset10 { + margin-left: 88.39779005524862%; + *margin-left: 88.2914070765252%; + } + .row-fluid .offset10:first-child { + margin-left: 85.6353591160221%; + *margin-left: 85.52897613729868%; + } + .row-fluid .offset9 { + margin-left: 79.8342541436464%; + *margin-left: 79.72787116492299%; + } + .row-fluid .offset9:first-child { + margin-left: 77.07182320441989%; + *margin-left: 76.96544022569647%; + } + .row-fluid .offset8 { + margin-left: 71.2707182320442%; + *margin-left: 71.16433525332079%; + } + .row-fluid .offset8:first-child { + margin-left: 68.50828729281768%; + *margin-left: 68.40190431409427%; + } + .row-fluid .offset7 { + margin-left: 62.70718232044199%; + *margin-left: 62.600799341718584%; + } + .row-fluid .offset7:first-child { + margin-left: 59.94475138121547%; + *margin-left: 59.838368402492065%; + } + .row-fluid .offset6 { + margin-left: 54.14364640883978%; + *margin-left: 54.037263430116376%; + } + .row-fluid .offset6:first-child { + margin-left: 51.38121546961326%; + *margin-left: 51.27483249088986%; + } + .row-fluid .offset5 { + margin-left: 45.58011049723757%; + *margin-left: 45.47372751851417%; + } + .row-fluid .offset5:first-child { + margin-left: 42.81767955801105%; + *margin-left: 42.71129657928765%; + } + .row-fluid .offset4 { + margin-left: 37.01657458563536%; + *margin-left: 36.91019160691196%; + } + .row-fluid .offset4:first-child { + margin-left: 34.25414364640884%; + *margin-left: 34.14776066768544%; + } + .row-fluid .offset3 { + margin-left: 28.45303867403315%; + *margin-left: 28.346655695309746%; + } + .row-fluid .offset3:first-child { + margin-left: 25.69060773480663%; + *margin-left: 25.584224756083227%; + } + .row-fluid .offset2 { + margin-left: 19.88950276243094%; + *margin-left: 19.783119783707537%; + } + .row-fluid .offset2:first-child { + margin-left: 17.12707182320442%; + *margin-left: 17.02068884448102%; + } + .row-fluid .offset1 { + margin-left: 11.32596685082873%; + *margin-left: 11.219583872105325%; + } + .row-fluid .offset1:first-child { + margin-left: 8.56353591160221%; + *margin-left: 8.457152932878806%; + } + input, + textarea, + .uneditable-input { + margin-left: 0; + } + .controls-row [class*="span"] + [class*="span"] { + margin-left: 20px; + } + input.span12, + textarea.span12, + .uneditable-input.span12 { + width: 710px; + } + input.span11, + textarea.span11, + .uneditable-input.span11 { + width: 648px; + } + input.span10, + textarea.span10, + .uneditable-input.span10 { + width: 586px; + } + input.span9, + textarea.span9, + .uneditable-input.span9 { + width: 524px; + } + input.span8, + textarea.span8, + .uneditable-input.span8 { + width: 462px; + } + input.span7, + textarea.span7, + .uneditable-input.span7 { + width: 400px; + } + input.span6, + textarea.span6, + .uneditable-input.span6 { + width: 338px; + } + input.span5, + textarea.span5, + .uneditable-input.span5 { + width: 276px; + } + input.span4, + textarea.span4, + .uneditable-input.span4 { + width: 214px; + } + input.span3, + textarea.span3, + .uneditable-input.span3 { + width: 152px; + } + input.span2, + textarea.span2, + .uneditable-input.span2 { + width: 90px; + } + input.span1, + textarea.span1, + .uneditable-input.span1 { + width: 28px; + } +} + +@media (max-width: 767px) { + body { + padding-right: 20px; + padding-left: 20px; + } + .navbar-fixed-top, + .navbar-fixed-bottom, + .navbar-static-top { + margin-right: -20px; + margin-left: -20px; + } + .container-fluid { + padding: 0; + } + .dl-horizontal dt { + float: none; + width: auto; + clear: none; + text-align: left; + } + .dl-horizontal dd { + margin-left: 0; + } + .container { + width: auto; + } + .row-fluid { + width: 100%; + } + .row, + .thumbnails { + margin-left: 0; + } + .thumbnails > li { + float: none; + margin-left: 0; + } + [class*="span"], + .uneditable-input[class*="span"], + .row-fluid [class*="span"] { + display: block; + float: none; + width: 100%; + margin-left: 0; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + } + .span12, + .row-fluid .span12 { + width: 100%; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + } + .row-fluid [class*="offset"]:first-child { + margin-left: 0; + } + .input-large, + .input-xlarge, + .input-xxlarge, + input[class*="span"], + select[class*="span"], + textarea[class*="span"], + .uneditable-input { + display: block; + width: 100%; + min-height: 30px; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + } + .input-prepend input, + .input-append input, + .input-prepend input[class*="span"], + .input-append input[class*="span"] { + display: inline-block; + width: auto; + } + .controls-row [class*="span"] + [class*="span"] { + margin-left: 0; + } + .modal { + position: fixed; + top: 20px; + right: 20px; + left: 20px; + width: auto; + margin: 0; + } + .modal.fade { + top: -100px; + } + .modal.fade.in { + top: 20px; + } +} + +@media (max-width: 480px) { + .nav-collapse { + -webkit-transform: translate3d(0, 0, 0); + } + .page-header h1 small { + display: block; + line-height: 20px; + } + input[type="checkbox"], + input[type="radio"] { + border: 1px solid #ccc; + } + .form-horizontal .control-label { + float: none; + width: auto; + padding-top: 0; + text-align: left; + } + .form-horizontal .controls { + margin-left: 0; + } + .form-horizontal .control-list { + padding-top: 0; + } + .form-horizontal .form-actions { + padding-right: 10px; + padding-left: 10px; + } + .media .pull-left, + .media .pull-right { + display: block; + float: none; + margin-bottom: 10px; + } + .media-object { + margin-right: 0; + margin-left: 0; + } + .modal { + top: 10px; + right: 10px; + left: 10px; + } + .modal-header .close { + padding: 10px; + margin: -10px; + } + .carousel-caption { + position: static; + } +} + +@media (max-width: 979px) { + body { + padding-top: 0; + } + .navbar-fixed-top, + .navbar-fixed-bottom { + position: static; + } + .navbar-fixed-top { + margin-bottom: 20px; + } + .navbar-fixed-bottom { + margin-top: 20px; + } + .navbar-fixed-top .navbar-inner, + .navbar-fixed-bottom .navbar-inner { + padding: 5px; + } + .navbar .container { + width: auto; + padding: 0; + } + .navbar .brand { + padding-right: 10px; + padding-left: 10px; + margin: 0 0 0 -5px; + } + .nav-collapse { + clear: both; + } + .nav-collapse .nav { + float: none; + margin: 0 0 10px; + } + .nav-collapse .nav > li { + float: none; + } + .nav-collapse .nav > li > a { + margin-bottom: 2px; + } + .nav-collapse .nav > .divider-vertical { + display: none; + } + .nav-collapse .nav .nav-header { + color: #777777; + text-shadow: none; + } + .nav-collapse .nav > li > a, + .nav-collapse .dropdown-menu a { + padding: 9px 15px; + font-weight: bold; + color: #777777; + -webkit-border-radius: 3px; + -moz-border-radius: 3px; + border-radius: 3px; + } + .nav-collapse .btn { + padding: 4px 10px 4px; + font-weight: normal; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; + } + .nav-collapse .dropdown-menu li + li a { + margin-bottom: 2px; + } + .nav-collapse .nav > li > a:hover, + .nav-collapse .nav > li > a:focus, + .nav-collapse .dropdown-menu a:hover, + .nav-collapse .dropdown-menu a:focus { + background-color: #f2f2f2; + } + .navbar-inverse .nav-collapse .nav > li > a, + .navbar-inverse .nav-collapse .dropdown-menu a { + color: #999999; + } + .navbar-inverse .nav-collapse .nav > li > a:hover, + .navbar-inverse .nav-collapse .nav > li > a:focus, + .navbar-inverse .nav-collapse .dropdown-menu a:hover, + .navbar-inverse .nav-collapse .dropdown-menu a:focus { + background-color: #111111; + } + .nav-collapse.in .btn-group { + padding: 0; + margin-top: 5px; + } + .nav-collapse .dropdown-menu { + position: static; + top: auto; + left: auto; + display: none; + float: none; + max-width: none; + padding: 0; + margin: 0 15px; + background-color: transparent; + border: none; + -webkit-border-radius: 0; + -moz-border-radius: 0; + border-radius: 0; + -webkit-box-shadow: none; + -moz-box-shadow: none; + box-shadow: none; + } + .nav-collapse .open > .dropdown-menu { + display: block; + } + .nav-collapse .dropdown-menu:before, + .nav-collapse .dropdown-menu:after { + display: none; + } + .nav-collapse .dropdown-menu .divider { + display: none; + } + .nav-collapse .nav > li > .dropdown-menu:before, + .nav-collapse .nav > li > .dropdown-menu:after { + display: none; + } + .nav-collapse .navbar-form, + .nav-collapse .navbar-search { + float: none; + padding: 10px 15px; + margin: 10px 0; + border-top: 1px solid #f2f2f2; + border-bottom: 1px solid #f2f2f2; + -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.1); + -moz-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.1); + box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.1); + } + .navbar-inverse .nav-collapse .navbar-form, + .navbar-inverse .nav-collapse .navbar-search { + border-top-color: #111111; + border-bottom-color: #111111; + } + .navbar .nav-collapse .nav.pull-right { + float: none; + margin-left: 0; + } + .nav-collapse, + .nav-collapse.collapse { + height: 0; + overflow: hidden; + } + .navbar .btn-navbar { + display: block; + } + .navbar-static .navbar-inner { + padding-right: 10px; + padding-left: 10px; + } +} + +@media (min-width: 980px) { + .nav-collapse.collapse { + height: auto !important; + overflow: visible !important; + } +} diff --git a/OpenAuth.Identity/wwwroot/lib/bootstrap/css/bootstrap-responsive.min.css b/OpenAuth.Identity/wwwroot/lib/bootstrap/css/bootstrap-responsive.min.css new file mode 100644 index 0000000..96a435b --- /dev/null +++ b/OpenAuth.Identity/wwwroot/lib/bootstrap/css/bootstrap-responsive.min.css @@ -0,0 +1,9 @@ +/*! + * Bootstrap Responsive v2.3.2 + * + * Copyright 2013 Twitter, Inc + * Licensed under the Apache License v2.0 + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Designed and built with all the love in the world by @mdo and @fat. + */.clearfix{*zoom:1}.clearfix:before,.clearfix:after{display:table;line-height:0;content:""}.clearfix:after{clear:both}.hide-text{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.input-block-level{display:block;width:100%;min-height:30px;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}@-ms-viewport{width:device-width}.hidden{display:none;visibility:hidden}.visible-phone{display:none!important}.visible-tablet{display:none!important}.hidden-desktop{display:none!important}.visible-desktop{display:inherit!important}@media(min-width:768px) and (max-width:979px){.hidden-desktop{display:inherit!important}.visible-desktop{display:none!important}.visible-tablet{display:inherit!important}.hidden-tablet{display:none!important}}@media(max-width:767px){.hidden-desktop{display:inherit!important}.visible-desktop{display:none!important}.visible-phone{display:inherit!important}.hidden-phone{display:none!important}}.visible-print{display:none!important}@media print{.visible-print{display:inherit!important}.hidden-print{display:none!important}}@media(min-width:1200px){.row{margin-left:-30px;*zoom:1}.row:before,.row:after{display:table;line-height:0;content:""}.row:after{clear:both}[class*="span"]{float:left;min-height:1px;margin-left:30px}.container,.navbar-static-top .container,.navbar-fixed-top .container,.navbar-fixed-bottom .container{width:1170px}.span12{width:1170px}.span11{width:1070px}.span10{width:970px}.span9{width:870px}.span8{width:770px}.span7{width:670px}.span6{width:570px}.span5{width:470px}.span4{width:370px}.span3{width:270px}.span2{width:170px}.span1{width:70px}.offset12{margin-left:1230px}.offset11{margin-left:1130px}.offset10{margin-left:1030px}.offset9{margin-left:930px}.offset8{margin-left:830px}.offset7{margin-left:730px}.offset6{margin-left:630px}.offset5{margin-left:530px}.offset4{margin-left:430px}.offset3{margin-left:330px}.offset2{margin-left:230px}.offset1{margin-left:130px}.row-fluid{width:100%;*zoom:1}.row-fluid:before,.row-fluid:after{display:table;line-height:0;content:""}.row-fluid:after{clear:both}.row-fluid [class*="span"]{display:block;float:left;width:100%;min-height:30px;margin-left:2.564102564102564%;*margin-left:2.5109110747408616%;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.row-fluid [class*="span"]:first-child{margin-left:0}.row-fluid .controls-row [class*="span"]+[class*="span"]{margin-left:2.564102564102564%}.row-fluid .span12{width:100%;*width:99.94680851063829%}.row-fluid .span11{width:91.45299145299145%;*width:91.39979996362975%}.row-fluid .span10{width:82.90598290598291%;*width:82.8527914166212%}.row-fluid .span9{width:74.35897435897436%;*width:74.30578286961266%}.row-fluid .span8{width:65.81196581196582%;*width:65.75877432260411%}.row-fluid .span7{width:57.26495726495726%;*width:57.21176577559556%}.row-fluid .span6{width:48.717948717948715%;*width:48.664757228587014%}.row-fluid .span5{width:40.17094017094017%;*width:40.11774868157847%}.row-fluid .span4{width:31.623931623931625%;*width:31.570740134569924%}.row-fluid .span3{width:23.076923076923077%;*width:23.023731587561375%}.row-fluid .span2{width:14.52991452991453%;*width:14.476723040552828%}.row-fluid .span1{width:5.982905982905983%;*width:5.929714493544281%}.row-fluid .offset12{margin-left:105.12820512820512%;*margin-left:105.02182214948171%}.row-fluid .offset12:first-child{margin-left:102.56410256410257%;*margin-left:102.45771958537915%}.row-fluid .offset11{margin-left:96.58119658119658%;*margin-left:96.47481360247316%}.row-fluid .offset11:first-child{margin-left:94.01709401709402%;*margin-left:93.91071103837061%}.row-fluid .offset10{margin-left:88.03418803418803%;*margin-left:87.92780505546462%}.row-fluid .offset10:first-child{margin-left:85.47008547008548%;*margin-left:85.36370249136206%}.row-fluid .offset9{margin-left:79.48717948717949%;*margin-left:79.38079650845607%}.row-fluid .offset9:first-child{margin-left:76.92307692307693%;*margin-left:76.81669394435352%}.row-fluid .offset8{margin-left:70.94017094017094%;*margin-left:70.83378796144753%}.row-fluid .offset8:first-child{margin-left:68.37606837606839%;*margin-left:68.26968539734497%}.row-fluid .offset7{margin-left:62.393162393162385%;*margin-left:62.28677941443899%}.row-fluid .offset7:first-child{margin-left:59.82905982905982%;*margin-left:59.72267685033642%}.row-fluid .offset6{margin-left:53.84615384615384%;*margin-left:53.739770867430444%}.row-fluid .offset6:first-child{margin-left:51.28205128205128%;*margin-left:51.175668303327875%}.row-fluid .offset5{margin-left:45.299145299145295%;*margin-left:45.1927623204219%}.row-fluid .offset5:first-child{margin-left:42.73504273504273%;*margin-left:42.62865975631933%}.row-fluid .offset4{margin-left:36.75213675213675%;*margin-left:36.645753773413354%}.row-fluid .offset4:first-child{margin-left:34.18803418803419%;*margin-left:34.081651209310785%}.row-fluid .offset3{margin-left:28.205128205128204%;*margin-left:28.0987452264048%}.row-fluid .offset3:first-child{margin-left:25.641025641025642%;*margin-left:25.53464266230224%}.row-fluid .offset2{margin-left:19.65811965811966%;*margin-left:19.551736679396257%}.row-fluid .offset2:first-child{margin-left:17.094017094017094%;*margin-left:16.98763411529369%}.row-fluid .offset1{margin-left:11.11111111111111%;*margin-left:11.004728132387708%}.row-fluid .offset1:first-child{margin-left:8.547008547008547%;*margin-left:8.440625568285142%}input,textarea,.uneditable-input{margin-left:0}.controls-row [class*="span"]+[class*="span"]{margin-left:30px}input.span12,textarea.span12,.uneditable-input.span12{width:1156px}input.span11,textarea.span11,.uneditable-input.span11{width:1056px}input.span10,textarea.span10,.uneditable-input.span10{width:956px}input.span9,textarea.span9,.uneditable-input.span9{width:856px}input.span8,textarea.span8,.uneditable-input.span8{width:756px}input.span7,textarea.span7,.uneditable-input.span7{width:656px}input.span6,textarea.span6,.uneditable-input.span6{width:556px}input.span5,textarea.span5,.uneditable-input.span5{width:456px}input.span4,textarea.span4,.uneditable-input.span4{width:356px}input.span3,textarea.span3,.uneditable-input.span3{width:256px}input.span2,textarea.span2,.uneditable-input.span2{width:156px}input.span1,textarea.span1,.uneditable-input.span1{width:56px}.thumbnails{margin-left:-30px}.thumbnails>li{margin-left:30px}.row-fluid .thumbnails{margin-left:0}}@media(min-width:768px) and (max-width:979px){.row{margin-left:-20px;*zoom:1}.row:before,.row:after{display:table;line-height:0;content:""}.row:after{clear:both}[class*="span"]{float:left;min-height:1px;margin-left:20px}.container,.navbar-static-top .container,.navbar-fixed-top .container,.navbar-fixed-bottom .container{width:724px}.span12{width:724px}.span11{width:662px}.span10{width:600px}.span9{width:538px}.span8{width:476px}.span7{width:414px}.span6{width:352px}.span5{width:290px}.span4{width:228px}.span3{width:166px}.span2{width:104px}.span1{width:42px}.offset12{margin-left:764px}.offset11{margin-left:702px}.offset10{margin-left:640px}.offset9{margin-left:578px}.offset8{margin-left:516px}.offset7{margin-left:454px}.offset6{margin-left:392px}.offset5{margin-left:330px}.offset4{margin-left:268px}.offset3{margin-left:206px}.offset2{margin-left:144px}.offset1{margin-left:82px}.row-fluid{width:100%;*zoom:1}.row-fluid:before,.row-fluid:after{display:table;line-height:0;content:""}.row-fluid:after{clear:both}.row-fluid [class*="span"]{display:block;float:left;width:100%;min-height:30px;margin-left:2.7624309392265194%;*margin-left:2.709239449864817%;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.row-fluid [class*="span"]:first-child{margin-left:0}.row-fluid .controls-row [class*="span"]+[class*="span"]{margin-left:2.7624309392265194%}.row-fluid .span12{width:100%;*width:99.94680851063829%}.row-fluid .span11{width:91.43646408839778%;*width:91.38327259903608%}.row-fluid .span10{width:82.87292817679558%;*width:82.81973668743387%}.row-fluid .span9{width:74.30939226519337%;*width:74.25620077583166%}.row-fluid .span8{width:65.74585635359117%;*width:65.69266486422946%}.row-fluid .span7{width:57.18232044198895%;*width:57.12912895262725%}.row-fluid .span6{width:48.61878453038674%;*width:48.56559304102504%}.row-fluid .span5{width:40.05524861878453%;*width:40.00205712942283%}.row-fluid .span4{width:31.491712707182323%;*width:31.43852121782062%}.row-fluid .span3{width:22.92817679558011%;*width:22.87498530621841%}.row-fluid .span2{width:14.3646408839779%;*width:14.311449394616199%}.row-fluid .span1{width:5.801104972375691%;*width:5.747913483013988%}.row-fluid .offset12{margin-left:105.52486187845304%;*margin-left:105.41847889972962%}.row-fluid .offset12:first-child{margin-left:102.76243093922652%;*margin-left:102.6560479605031%}.row-fluid .offset11{margin-left:96.96132596685082%;*margin-left:96.8549429881274%}.row-fluid .offset11:first-child{margin-left:94.1988950276243%;*margin-left:94.09251204890089%}.row-fluid .offset10{margin-left:88.39779005524862%;*margin-left:88.2914070765252%}.row-fluid .offset10:first-child{margin-left:85.6353591160221%;*margin-left:85.52897613729868%}.row-fluid .offset9{margin-left:79.8342541436464%;*margin-left:79.72787116492299%}.row-fluid .offset9:first-child{margin-left:77.07182320441989%;*margin-left:76.96544022569647%}.row-fluid .offset8{margin-left:71.2707182320442%;*margin-left:71.16433525332079%}.row-fluid .offset8:first-child{margin-left:68.50828729281768%;*margin-left:68.40190431409427%}.row-fluid .offset7{margin-left:62.70718232044199%;*margin-left:62.600799341718584%}.row-fluid .offset7:first-child{margin-left:59.94475138121547%;*margin-left:59.838368402492065%}.row-fluid .offset6{margin-left:54.14364640883978%;*margin-left:54.037263430116376%}.row-fluid .offset6:first-child{margin-left:51.38121546961326%;*margin-left:51.27483249088986%}.row-fluid .offset5{margin-left:45.58011049723757%;*margin-left:45.47372751851417%}.row-fluid .offset5:first-child{margin-left:42.81767955801105%;*margin-left:42.71129657928765%}.row-fluid .offset4{margin-left:37.01657458563536%;*margin-left:36.91019160691196%}.row-fluid .offset4:first-child{margin-left:34.25414364640884%;*margin-left:34.14776066768544%}.row-fluid .offset3{margin-left:28.45303867403315%;*margin-left:28.346655695309746%}.row-fluid .offset3:first-child{margin-left:25.69060773480663%;*margin-left:25.584224756083227%}.row-fluid .offset2{margin-left:19.88950276243094%;*margin-left:19.783119783707537%}.row-fluid .offset2:first-child{margin-left:17.12707182320442%;*margin-left:17.02068884448102%}.row-fluid .offset1{margin-left:11.32596685082873%;*margin-left:11.219583872105325%}.row-fluid .offset1:first-child{margin-left:8.56353591160221%;*margin-left:8.457152932878806%}input,textarea,.uneditable-input{margin-left:0}.controls-row [class*="span"]+[class*="span"]{margin-left:20px}input.span12,textarea.span12,.uneditable-input.span12{width:710px}input.span11,textarea.span11,.uneditable-input.span11{width:648px}input.span10,textarea.span10,.uneditable-input.span10{width:586px}input.span9,textarea.span9,.uneditable-input.span9{width:524px}input.span8,textarea.span8,.uneditable-input.span8{width:462px}input.span7,textarea.span7,.uneditable-input.span7{width:400px}input.span6,textarea.span6,.uneditable-input.span6{width:338px}input.span5,textarea.span5,.uneditable-input.span5{width:276px}input.span4,textarea.span4,.uneditable-input.span4{width:214px}input.span3,textarea.span3,.uneditable-input.span3{width:152px}input.span2,textarea.span2,.uneditable-input.span2{width:90px}input.span1,textarea.span1,.uneditable-input.span1{width:28px}}@media(max-width:767px){body{padding-right:20px;padding-left:20px}.navbar-fixed-top,.navbar-fixed-bottom,.navbar-static-top{margin-right:-20px;margin-left:-20px}.container-fluid{padding:0}.dl-horizontal dt{float:none;width:auto;clear:none;text-align:left}.dl-horizontal dd{margin-left:0}.container{width:auto}.row-fluid{width:100%}.row,.thumbnails{margin-left:0}.thumbnails>li{float:none;margin-left:0}[class*="span"],.uneditable-input[class*="span"],.row-fluid [class*="span"]{display:block;float:none;width:100%;margin-left:0;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.span12,.row-fluid .span12{width:100%;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.row-fluid [class*="offset"]:first-child{margin-left:0}.input-large,.input-xlarge,.input-xxlarge,input[class*="span"],select[class*="span"],textarea[class*="span"],.uneditable-input{display:block;width:100%;min-height:30px;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.input-prepend input,.input-append input,.input-prepend input[class*="span"],.input-append input[class*="span"]{display:inline-block;width:auto}.controls-row [class*="span"]+[class*="span"]{margin-left:0}.modal{position:fixed;top:20px;right:20px;left:20px;width:auto;margin:0}.modal.fade{top:-100px}.modal.fade.in{top:20px}}@media(max-width:480px){.nav-collapse{-webkit-transform:translate3d(0,0,0)}.page-header h1 small{display:block;line-height:20px}input[type="checkbox"],input[type="radio"]{border:1px solid #ccc}.form-horizontal .control-label{float:none;width:auto;padding-top:0;text-align:left}.form-horizontal .controls{margin-left:0}.form-horizontal .control-list{padding-top:0}.form-horizontal .form-actions{padding-right:10px;padding-left:10px}.media .pull-left,.media .pull-right{display:block;float:none;margin-bottom:10px}.media-object{margin-right:0;margin-left:0}.modal{top:10px;right:10px;left:10px}.modal-header .close{padding:10px;margin:-10px}.carousel-caption{position:static}}@media(max-width:979px){body{padding-top:0}.navbar-fixed-top,.navbar-fixed-bottom{position:static}.navbar-fixed-top{margin-bottom:20px}.navbar-fixed-bottom{margin-top:20px}.navbar-fixed-top .navbar-inner,.navbar-fixed-bottom .navbar-inner{padding:5px}.navbar .container{width:auto;padding:0}.navbar .brand{padding-right:10px;padding-left:10px;margin:0 0 0 -5px}.nav-collapse{clear:both}.nav-collapse .nav{float:none;margin:0 0 10px}.nav-collapse .nav>li{float:none}.nav-collapse .nav>li>a{margin-bottom:2px}.nav-collapse .nav>.divider-vertical{display:none}.nav-collapse .nav .nav-header{color:#777;text-shadow:none}.nav-collapse .nav>li>a,.nav-collapse .dropdown-menu a{padding:9px 15px;font-weight:bold;color:#777;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px}.nav-collapse .btn{padding:4px 10px 4px;font-weight:normal;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}.nav-collapse .dropdown-menu li+li a{margin-bottom:2px}.nav-collapse .nav>li>a:hover,.nav-collapse .nav>li>a:focus,.nav-collapse .dropdown-menu a:hover,.nav-collapse .dropdown-menu a:focus{background-color:#f2f2f2}.navbar-inverse .nav-collapse .nav>li>a,.navbar-inverse .nav-collapse .dropdown-menu a{color:#999}.navbar-inverse .nav-collapse .nav>li>a:hover,.navbar-inverse .nav-collapse .nav>li>a:focus,.navbar-inverse .nav-collapse .dropdown-menu a:hover,.navbar-inverse .nav-collapse .dropdown-menu a:focus{background-color:#111}.nav-collapse.in .btn-group{padding:0;margin-top:5px}.nav-collapse .dropdown-menu{position:static;top:auto;left:auto;display:none;float:none;max-width:none;padding:0;margin:0 15px;background-color:transparent;border:0;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none}.nav-collapse .open>.dropdown-menu{display:block}.nav-collapse .dropdown-menu:before,.nav-collapse .dropdown-menu:after{display:none}.nav-collapse .dropdown-menu .divider{display:none}.nav-collapse .nav>li>.dropdown-menu:before,.nav-collapse .nav>li>.dropdown-menu:after{display:none}.nav-collapse .navbar-form,.nav-collapse .navbar-search{float:none;padding:10px 15px;margin:10px 0;border-top:1px solid #f2f2f2;border-bottom:1px solid #f2f2f2;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,0.1),0 1px 0 rgba(255,255,255,0.1);-moz-box-shadow:inset 0 1px 0 rgba(255,255,255,0.1),0 1px 0 rgba(255,255,255,0.1);box-shadow:inset 0 1px 0 rgba(255,255,255,0.1),0 1px 0 rgba(255,255,255,0.1)}.navbar-inverse .nav-collapse .navbar-form,.navbar-inverse .nav-collapse .navbar-search{border-top-color:#111;border-bottom-color:#111}.navbar .nav-collapse .nav.pull-right{float:none;margin-left:0}.nav-collapse,.nav-collapse.collapse{height:0;overflow:hidden}.navbar .btn-navbar{display:block}.navbar-static .navbar-inner{padding-right:10px;padding-left:10px}}@media(min-width:980px){.nav-collapse.collapse{height:auto!important;overflow:visible!important}} diff --git a/OpenAuth.Identity/wwwroot/lib/bootstrap/css/bootstrap-theme.css b/OpenAuth.Identity/wwwroot/lib/bootstrap/css/bootstrap-theme.css new file mode 100644 index 0000000..31d8882 --- /dev/null +++ b/OpenAuth.Identity/wwwroot/lib/bootstrap/css/bootstrap-theme.css @@ -0,0 +1,587 @@ +/*! + * Bootstrap v3.3.7 (http://getbootstrap.com) + * Copyright 2011-2016 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + */ +.btn-default, +.btn-primary, +.btn-success, +.btn-info, +.btn-warning, +.btn-danger { + text-shadow: 0 -1px 0 rgba(0, 0, 0, .2); + -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .15), 0 1px 1px rgba(0, 0, 0, .075); + box-shadow: inset 0 1px 0 rgba(255, 255, 255, .15), 0 1px 1px rgba(0, 0, 0, .075); +} +.btn-default:active, +.btn-primary:active, +.btn-success:active, +.btn-info:active, +.btn-warning:active, +.btn-danger:active, +.btn-default.active, +.btn-primary.active, +.btn-success.active, +.btn-info.active, +.btn-warning.active, +.btn-danger.active { + -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125); + box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125); +} +.btn-default.disabled, +.btn-primary.disabled, +.btn-success.disabled, +.btn-info.disabled, +.btn-warning.disabled, +.btn-danger.disabled, +.btn-default[disabled], +.btn-primary[disabled], +.btn-success[disabled], +.btn-info[disabled], +.btn-warning[disabled], +.btn-danger[disabled], +fieldset[disabled] .btn-default, +fieldset[disabled] .btn-primary, +fieldset[disabled] .btn-success, +fieldset[disabled] .btn-info, +fieldset[disabled] .btn-warning, +fieldset[disabled] .btn-danger { + -webkit-box-shadow: none; + box-shadow: none; +} +.btn-default .badge, +.btn-primary .badge, +.btn-success .badge, +.btn-info .badge, +.btn-warning .badge, +.btn-danger .badge { + text-shadow: none; +} +.btn:active, +.btn.active { + background-image: none; +} +.btn-default { + text-shadow: 0 1px 0 #fff; + background-image: -webkit-linear-gradient(top, #fff 0%, #e0e0e0 100%); + background-image: -o-linear-gradient(top, #fff 0%, #e0e0e0 100%); + background-image: -webkit-gradient(linear, left top, left bottom, from(#fff), to(#e0e0e0)); + background-image: linear-gradient(to bottom, #fff 0%, #e0e0e0 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#ffe0e0e0', GradientType=0); + filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); + background-repeat: repeat-x; + border-color: #dbdbdb; + border-color: #ccc; +} +.btn-default:hover, +.btn-default:focus { + background-color: #e0e0e0; + background-position: 0 -15px; +} +.btn-default:active, +.btn-default.active { + background-color: #e0e0e0; + border-color: #dbdbdb; +} +.btn-default.disabled, +.btn-default[disabled], +fieldset[disabled] .btn-default, +.btn-default.disabled:hover, +.btn-default[disabled]:hover, +fieldset[disabled] .btn-default:hover, +.btn-default.disabled:focus, +.btn-default[disabled]:focus, +fieldset[disabled] .btn-default:focus, +.btn-default.disabled.focus, +.btn-default[disabled].focus, +fieldset[disabled] .btn-default.focus, +.btn-default.disabled:active, +.btn-default[disabled]:active, +fieldset[disabled] .btn-default:active, +.btn-default.disabled.active, +.btn-default[disabled].active, +fieldset[disabled] .btn-default.active { + background-color: #e0e0e0; + background-image: none; +} +.btn-primary { + background-image: -webkit-linear-gradient(top, #337ab7 0%, #265a88 100%); + background-image: -o-linear-gradient(top, #337ab7 0%, #265a88 100%); + background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#265a88)); + background-image: linear-gradient(to bottom, #337ab7 0%, #265a88 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff265a88', GradientType=0); + filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); + background-repeat: repeat-x; + border-color: #245580; +} +.btn-primary:hover, +.btn-primary:focus { + background-color: #265a88; + background-position: 0 -15px; +} +.btn-primary:active, +.btn-primary.active { + background-color: #265a88; + border-color: #245580; +} +.btn-primary.disabled, +.btn-primary[disabled], +fieldset[disabled] .btn-primary, +.btn-primary.disabled:hover, +.btn-primary[disabled]:hover, +fieldset[disabled] .btn-primary:hover, +.btn-primary.disabled:focus, +.btn-primary[disabled]:focus, +fieldset[disabled] .btn-primary:focus, +.btn-primary.disabled.focus, +.btn-primary[disabled].focus, +fieldset[disabled] .btn-primary.focus, +.btn-primary.disabled:active, +.btn-primary[disabled]:active, +fieldset[disabled] .btn-primary:active, +.btn-primary.disabled.active, +.btn-primary[disabled].active, +fieldset[disabled] .btn-primary.active { + background-color: #265a88; + background-image: none; +} +.btn-success { + background-image: -webkit-linear-gradient(top, #5cb85c 0%, #419641 100%); + background-image: -o-linear-gradient(top, #5cb85c 0%, #419641 100%); + background-image: -webkit-gradient(linear, left top, left bottom, from(#5cb85c), to(#419641)); + background-image: linear-gradient(to bottom, #5cb85c 0%, #419641 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff419641', GradientType=0); + filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); + background-repeat: repeat-x; + border-color: #3e8f3e; +} +.btn-success:hover, +.btn-success:focus { + background-color: #419641; + background-position: 0 -15px; +} +.btn-success:active, +.btn-success.active { + background-color: #419641; + border-color: #3e8f3e; +} +.btn-success.disabled, +.btn-success[disabled], +fieldset[disabled] .btn-success, +.btn-success.disabled:hover, +.btn-success[disabled]:hover, +fieldset[disabled] .btn-success:hover, +.btn-success.disabled:focus, +.btn-success[disabled]:focus, +fieldset[disabled] .btn-success:focus, +.btn-success.disabled.focus, +.btn-success[disabled].focus, +fieldset[disabled] .btn-success.focus, +.btn-success.disabled:active, +.btn-success[disabled]:active, +fieldset[disabled] .btn-success:active, +.btn-success.disabled.active, +.btn-success[disabled].active, +fieldset[disabled] .btn-success.active { + background-color: #419641; + background-image: none; +} +.btn-info { + background-image: -webkit-linear-gradient(top, #5bc0de 0%, #2aabd2 100%); + background-image: -o-linear-gradient(top, #5bc0de 0%, #2aabd2 100%); + background-image: -webkit-gradient(linear, left top, left bottom, from(#5bc0de), to(#2aabd2)); + background-image: linear-gradient(to bottom, #5bc0de 0%, #2aabd2 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff2aabd2', GradientType=0); + filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); + background-repeat: repeat-x; + border-color: #28a4c9; +} +.btn-info:hover, +.btn-info:focus { + background-color: #2aabd2; + background-position: 0 -15px; +} +.btn-info:active, +.btn-info.active { + background-color: #2aabd2; + border-color: #28a4c9; +} +.btn-info.disabled, +.btn-info[disabled], +fieldset[disabled] .btn-info, +.btn-info.disabled:hover, +.btn-info[disabled]:hover, +fieldset[disabled] .btn-info:hover, +.btn-info.disabled:focus, +.btn-info[disabled]:focus, +fieldset[disabled] .btn-info:focus, +.btn-info.disabled.focus, +.btn-info[disabled].focus, +fieldset[disabled] .btn-info.focus, +.btn-info.disabled:active, +.btn-info[disabled]:active, +fieldset[disabled] .btn-info:active, +.btn-info.disabled.active, +.btn-info[disabled].active, +fieldset[disabled] .btn-info.active { + background-color: #2aabd2; + background-image: none; +} +.btn-warning { + background-image: -webkit-linear-gradient(top, #f0ad4e 0%, #eb9316 100%); + background-image: -o-linear-gradient(top, #f0ad4e 0%, #eb9316 100%); + background-image: -webkit-gradient(linear, left top, left bottom, from(#f0ad4e), to(#eb9316)); + background-image: linear-gradient(to bottom, #f0ad4e 0%, #eb9316 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffeb9316', GradientType=0); + filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); + background-repeat: repeat-x; + border-color: #e38d13; +} +.btn-warning:hover, +.btn-warning:focus { + background-color: #eb9316; + background-position: 0 -15px; +} +.btn-warning:active, +.btn-warning.active { + background-color: #eb9316; + border-color: #e38d13; +} +.btn-warning.disabled, +.btn-warning[disabled], +fieldset[disabled] .btn-warning, +.btn-warning.disabled:hover, +.btn-warning[disabled]:hover, +fieldset[disabled] .btn-warning:hover, +.btn-warning.disabled:focus, +.btn-warning[disabled]:focus, +fieldset[disabled] .btn-warning:focus, +.btn-warning.disabled.focus, +.btn-warning[disabled].focus, +fieldset[disabled] .btn-warning.focus, +.btn-warning.disabled:active, +.btn-warning[disabled]:active, +fieldset[disabled] .btn-warning:active, +.btn-warning.disabled.active, +.btn-warning[disabled].active, +fieldset[disabled] .btn-warning.active { + background-color: #eb9316; + background-image: none; +} +.btn-danger { + background-image: -webkit-linear-gradient(top, #d9534f 0%, #c12e2a 100%); + background-image: -o-linear-gradient(top, #d9534f 0%, #c12e2a 100%); + background-image: -webkit-gradient(linear, left top, left bottom, from(#d9534f), to(#c12e2a)); + background-image: linear-gradient(to bottom, #d9534f 0%, #c12e2a 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc12e2a', GradientType=0); + filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); + background-repeat: repeat-x; + border-color: #b92c28; +} +.btn-danger:hover, +.btn-danger:focus { + background-color: #c12e2a; + background-position: 0 -15px; +} +.btn-danger:active, +.btn-danger.active { + background-color: #c12e2a; + border-color: #b92c28; +} +.btn-danger.disabled, +.btn-danger[disabled], +fieldset[disabled] .btn-danger, +.btn-danger.disabled:hover, +.btn-danger[disabled]:hover, +fieldset[disabled] .btn-danger:hover, +.btn-danger.disabled:focus, +.btn-danger[disabled]:focus, +fieldset[disabled] .btn-danger:focus, +.btn-danger.disabled.focus, +.btn-danger[disabled].focus, +fieldset[disabled] .btn-danger.focus, +.btn-danger.disabled:active, +.btn-danger[disabled]:active, +fieldset[disabled] .btn-danger:active, +.btn-danger.disabled.active, +.btn-danger[disabled].active, +fieldset[disabled] .btn-danger.active { + background-color: #c12e2a; + background-image: none; +} +.thumbnail, +.img-thumbnail { + -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, .075); + box-shadow: 0 1px 2px rgba(0, 0, 0, .075); +} +.dropdown-menu > li > a:hover, +.dropdown-menu > li > a:focus { + background-color: #e8e8e8; + background-image: -webkit-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%); + background-image: -o-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%); + background-image: -webkit-gradient(linear, left top, left bottom, from(#f5f5f5), to(#e8e8e8)); + background-image: linear-gradient(to bottom, #f5f5f5 0%, #e8e8e8 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0); + background-repeat: repeat-x; +} +.dropdown-menu > .active > a, +.dropdown-menu > .active > a:hover, +.dropdown-menu > .active > a:focus { + background-color: #2e6da4; + background-image: -webkit-linear-gradient(top, #337ab7 0%, #2e6da4 100%); + background-image: -o-linear-gradient(top, #337ab7 0%, #2e6da4 100%); + background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#2e6da4)); + background-image: linear-gradient(to bottom, #337ab7 0%, #2e6da4 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0); + background-repeat: repeat-x; +} +.navbar-default { + background-image: -webkit-linear-gradient(top, #fff 0%, #f8f8f8 100%); + background-image: -o-linear-gradient(top, #fff 0%, #f8f8f8 100%); + background-image: -webkit-gradient(linear, left top, left bottom, from(#fff), to(#f8f8f8)); + background-image: linear-gradient(to bottom, #fff 0%, #f8f8f8 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#fff8f8f8', GradientType=0); + filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); + background-repeat: repeat-x; + border-radius: 4px; + -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .15), 0 1px 5px rgba(0, 0, 0, .075); + box-shadow: inset 0 1px 0 rgba(255, 255, 255, .15), 0 1px 5px rgba(0, 0, 0, .075); +} +.navbar-default .navbar-nav > .open > a, +.navbar-default .navbar-nav > .active > a { + background-image: -webkit-linear-gradient(top, #dbdbdb 0%, #e2e2e2 100%); + background-image: -o-linear-gradient(top, #dbdbdb 0%, #e2e2e2 100%); + background-image: -webkit-gradient(linear, left top, left bottom, from(#dbdbdb), to(#e2e2e2)); + background-image: linear-gradient(to bottom, #dbdbdb 0%, #e2e2e2 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdbdbdb', endColorstr='#ffe2e2e2', GradientType=0); + background-repeat: repeat-x; + -webkit-box-shadow: inset 0 3px 9px rgba(0, 0, 0, .075); + box-shadow: inset 0 3px 9px rgba(0, 0, 0, .075); +} +.navbar-brand, +.navbar-nav > li > a { + text-shadow: 0 1px 0 rgba(255, 255, 255, .25); +} +.navbar-inverse { + background-image: -webkit-linear-gradient(top, #3c3c3c 0%, #222 100%); + background-image: -o-linear-gradient(top, #3c3c3c 0%, #222 100%); + background-image: -webkit-gradient(linear, left top, left bottom, from(#3c3c3c), to(#222)); + background-image: linear-gradient(to bottom, #3c3c3c 0%, #222 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff3c3c3c', endColorstr='#ff222222', GradientType=0); + filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); + background-repeat: repeat-x; + border-radius: 4px; +} +.navbar-inverse .navbar-nav > .open > a, +.navbar-inverse .navbar-nav > .active > a { + background-image: -webkit-linear-gradient(top, #080808 0%, #0f0f0f 100%); + background-image: -o-linear-gradient(top, #080808 0%, #0f0f0f 100%); + background-image: -webkit-gradient(linear, left top, left bottom, from(#080808), to(#0f0f0f)); + background-image: linear-gradient(to bottom, #080808 0%, #0f0f0f 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff080808', endColorstr='#ff0f0f0f', GradientType=0); + background-repeat: repeat-x; + -webkit-box-shadow: inset 0 3px 9px rgba(0, 0, 0, .25); + box-shadow: inset 0 3px 9px rgba(0, 0, 0, .25); +} +.navbar-inverse .navbar-brand, +.navbar-inverse .navbar-nav > li > a { + text-shadow: 0 -1px 0 rgba(0, 0, 0, .25); +} +.navbar-static-top, +.navbar-fixed-top, +.navbar-fixed-bottom { + border-radius: 0; +} +@media (max-width: 767px) { + .navbar .navbar-nav .open .dropdown-menu > .active > a, + .navbar .navbar-nav .open .dropdown-menu > .active > a:hover, + .navbar .navbar-nav .open .dropdown-menu > .active > a:focus { + color: #fff; + background-image: -webkit-linear-gradient(top, #337ab7 0%, #2e6da4 100%); + background-image: -o-linear-gradient(top, #337ab7 0%, #2e6da4 100%); + background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#2e6da4)); + background-image: linear-gradient(to bottom, #337ab7 0%, #2e6da4 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0); + background-repeat: repeat-x; + } +} +.alert { + text-shadow: 0 1px 0 rgba(255, 255, 255, .2); + -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .25), 0 1px 2px rgba(0, 0, 0, .05); + box-shadow: inset 0 1px 0 rgba(255, 255, 255, .25), 0 1px 2px rgba(0, 0, 0, .05); +} +.alert-success { + background-image: -webkit-linear-gradient(top, #dff0d8 0%, #c8e5bc 100%); + background-image: -o-linear-gradient(top, #dff0d8 0%, #c8e5bc 100%); + background-image: -webkit-gradient(linear, left top, left bottom, from(#dff0d8), to(#c8e5bc)); + background-image: linear-gradient(to bottom, #dff0d8 0%, #c8e5bc 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffc8e5bc', GradientType=0); + background-repeat: repeat-x; + border-color: #b2dba1; +} +.alert-info { + background-image: -webkit-linear-gradient(top, #d9edf7 0%, #b9def0 100%); + background-image: -o-linear-gradient(top, #d9edf7 0%, #b9def0 100%); + background-image: -webkit-gradient(linear, left top, left bottom, from(#d9edf7), to(#b9def0)); + background-image: linear-gradient(to bottom, #d9edf7 0%, #b9def0 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffb9def0', GradientType=0); + background-repeat: repeat-x; + border-color: #9acfea; +} +.alert-warning { + background-image: -webkit-linear-gradient(top, #fcf8e3 0%, #f8efc0 100%); + background-image: -o-linear-gradient(top, #fcf8e3 0%, #f8efc0 100%); + background-image: -webkit-gradient(linear, left top, left bottom, from(#fcf8e3), to(#f8efc0)); + background-image: linear-gradient(to bottom, #fcf8e3 0%, #f8efc0 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fff8efc0', GradientType=0); + background-repeat: repeat-x; + border-color: #f5e79e; +} +.alert-danger { + background-image: -webkit-linear-gradient(top, #f2dede 0%, #e7c3c3 100%); + background-image: -o-linear-gradient(top, #f2dede 0%, #e7c3c3 100%); + background-image: -webkit-gradient(linear, left top, left bottom, from(#f2dede), to(#e7c3c3)); + background-image: linear-gradient(to bottom, #f2dede 0%, #e7c3c3 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffe7c3c3', GradientType=0); + background-repeat: repeat-x; + border-color: #dca7a7; +} +.progress { + background-image: -webkit-linear-gradient(top, #ebebeb 0%, #f5f5f5 100%); + background-image: -o-linear-gradient(top, #ebebeb 0%, #f5f5f5 100%); + background-image: -webkit-gradient(linear, left top, left bottom, from(#ebebeb), to(#f5f5f5)); + background-image: linear-gradient(to bottom, #ebebeb 0%, #f5f5f5 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffebebeb', endColorstr='#fff5f5f5', GradientType=0); + background-repeat: repeat-x; +} +.progress-bar { + background-image: -webkit-linear-gradient(top, #337ab7 0%, #286090 100%); + background-image: -o-linear-gradient(top, #337ab7 0%, #286090 100%); + background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#286090)); + background-image: linear-gradient(to bottom, #337ab7 0%, #286090 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff286090', GradientType=0); + background-repeat: repeat-x; +} +.progress-bar-success { + background-image: -webkit-linear-gradient(top, #5cb85c 0%, #449d44 100%); + background-image: -o-linear-gradient(top, #5cb85c 0%, #449d44 100%); + background-image: -webkit-gradient(linear, left top, left bottom, from(#5cb85c), to(#449d44)); + background-image: linear-gradient(to bottom, #5cb85c 0%, #449d44 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff449d44', GradientType=0); + background-repeat: repeat-x; +} +.progress-bar-info { + background-image: -webkit-linear-gradient(top, #5bc0de 0%, #31b0d5 100%); + background-image: -o-linear-gradient(top, #5bc0de 0%, #31b0d5 100%); + background-image: -webkit-gradient(linear, left top, left bottom, from(#5bc0de), to(#31b0d5)); + background-image: linear-gradient(to bottom, #5bc0de 0%, #31b0d5 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff31b0d5', GradientType=0); + background-repeat: repeat-x; +} +.progress-bar-warning { + background-image: -webkit-linear-gradient(top, #f0ad4e 0%, #ec971f 100%); + background-image: -o-linear-gradient(top, #f0ad4e 0%, #ec971f 100%); + background-image: -webkit-gradient(linear, left top, left bottom, from(#f0ad4e), to(#ec971f)); + background-image: linear-gradient(to bottom, #f0ad4e 0%, #ec971f 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffec971f', GradientType=0); + background-repeat: repeat-x; +} +.progress-bar-danger { + background-image: -webkit-linear-gradient(top, #d9534f 0%, #c9302c 100%); + background-image: -o-linear-gradient(top, #d9534f 0%, #c9302c 100%); + background-image: -webkit-gradient(linear, left top, left bottom, from(#d9534f), to(#c9302c)); + background-image: linear-gradient(to bottom, #d9534f 0%, #c9302c 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc9302c', GradientType=0); + background-repeat: repeat-x; +} +.progress-bar-striped { + background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); + background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); + background-image: linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); +} +.list-group { + border-radius: 4px; + -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, .075); + box-shadow: 0 1px 2px rgba(0, 0, 0, .075); +} +.list-group-item.active, +.list-group-item.active:hover, +.list-group-item.active:focus { + text-shadow: 0 -1px 0 #286090; + background-image: -webkit-linear-gradient(top, #337ab7 0%, #2b669a 100%); + background-image: -o-linear-gradient(top, #337ab7 0%, #2b669a 100%); + background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#2b669a)); + background-image: linear-gradient(to bottom, #337ab7 0%, #2b669a 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2b669a', GradientType=0); + background-repeat: repeat-x; + border-color: #2b669a; +} +.list-group-item.active .badge, +.list-group-item.active:hover .badge, +.list-group-item.active:focus .badge { + text-shadow: none; +} +.panel { + -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, .05); + box-shadow: 0 1px 2px rgba(0, 0, 0, .05); +} +.panel-default > .panel-heading { + background-image: -webkit-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%); + background-image: -o-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%); + background-image: -webkit-gradient(linear, left top, left bottom, from(#f5f5f5), to(#e8e8e8)); + background-image: linear-gradient(to bottom, #f5f5f5 0%, #e8e8e8 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0); + background-repeat: repeat-x; +} +.panel-primary > .panel-heading { + background-image: -webkit-linear-gradient(top, #337ab7 0%, #2e6da4 100%); + background-image: -o-linear-gradient(top, #337ab7 0%, #2e6da4 100%); + background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#2e6da4)); + background-image: linear-gradient(to bottom, #337ab7 0%, #2e6da4 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0); + background-repeat: repeat-x; +} +.panel-success > .panel-heading { + background-image: -webkit-linear-gradient(top, #dff0d8 0%, #d0e9c6 100%); + background-image: -o-linear-gradient(top, #dff0d8 0%, #d0e9c6 100%); + background-image: -webkit-gradient(linear, left top, left bottom, from(#dff0d8), to(#d0e9c6)); + background-image: linear-gradient(to bottom, #dff0d8 0%, #d0e9c6 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffd0e9c6', GradientType=0); + background-repeat: repeat-x; +} +.panel-info > .panel-heading { + background-image: -webkit-linear-gradient(top, #d9edf7 0%, #c4e3f3 100%); + background-image: -o-linear-gradient(top, #d9edf7 0%, #c4e3f3 100%); + background-image: -webkit-gradient(linear, left top, left bottom, from(#d9edf7), to(#c4e3f3)); + background-image: linear-gradient(to bottom, #d9edf7 0%, #c4e3f3 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffc4e3f3', GradientType=0); + background-repeat: repeat-x; +} +.panel-warning > .panel-heading { + background-image: -webkit-linear-gradient(top, #fcf8e3 0%, #faf2cc 100%); + background-image: -o-linear-gradient(top, #fcf8e3 0%, #faf2cc 100%); + background-image: -webkit-gradient(linear, left top, left bottom, from(#fcf8e3), to(#faf2cc)); + background-image: linear-gradient(to bottom, #fcf8e3 0%, #faf2cc 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fffaf2cc', GradientType=0); + background-repeat: repeat-x; +} +.panel-danger > .panel-heading { + background-image: -webkit-linear-gradient(top, #f2dede 0%, #ebcccc 100%); + background-image: -o-linear-gradient(top, #f2dede 0%, #ebcccc 100%); + background-image: -webkit-gradient(linear, left top, left bottom, from(#f2dede), to(#ebcccc)); + background-image: linear-gradient(to bottom, #f2dede 0%, #ebcccc 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffebcccc', GradientType=0); + background-repeat: repeat-x; +} +.well { + background-image: -webkit-linear-gradient(top, #e8e8e8 0%, #f5f5f5 100%); + background-image: -o-linear-gradient(top, #e8e8e8 0%, #f5f5f5 100%); + background-image: -webkit-gradient(linear, left top, left bottom, from(#e8e8e8), to(#f5f5f5)); + background-image: linear-gradient(to bottom, #e8e8e8 0%, #f5f5f5 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffe8e8e8', endColorstr='#fff5f5f5', GradientType=0); + background-repeat: repeat-x; + border-color: #dcdcdc; + -webkit-box-shadow: inset 0 1px 3px rgba(0, 0, 0, .05), 0 1px 0 rgba(255, 255, 255, .1); + box-shadow: inset 0 1px 3px rgba(0, 0, 0, .05), 0 1px 0 rgba(255, 255, 255, .1); +} +/*# sourceMappingURL=bootstrap-theme.css.map */ diff --git a/OpenAuth.Identity/wwwroot/lib/bootstrap/css/bootstrap-theme.css.map b/OpenAuth.Identity/wwwroot/lib/bootstrap/css/bootstrap-theme.css.map new file mode 100644 index 0000000..d876f60 --- /dev/null +++ b/OpenAuth.Identity/wwwroot/lib/bootstrap/css/bootstrap-theme.css.map @@ -0,0 +1 @@ +{"version":3,"sources":["bootstrap-theme.css","less/theme.less","less/mixins/vendor-prefixes.less","less/mixins/gradients.less","less/mixins/reset-filter.less"],"names":[],"mappings":"AAAA;;;;GAIG;ACeH;;;;;;EAME,yCAAA;EC2CA,4FAAA;EACQ,oFAAA;CFvDT;ACgBC;;;;;;;;;;;;ECsCA,yDAAA;EACQ,iDAAA;CFxCT;ACMC;;;;;;;;;;;;;;;;;;ECiCA,yBAAA;EACQ,iBAAA;CFnBT;AC/BD;;;;;;EAuBI,kBAAA;CDgBH;ACyBC;;EAEE,uBAAA;CDvBH;AC4BD;EErEI,sEAAA;EACA,iEAAA;EACA,2FAAA;EAAA,oEAAA;EAEA,uHAAA;ECnBF,oEAAA;EH4CA,4BAAA;EACA,sBAAA;EAuC2C,0BAAA;EAA2B,mBAAA;CDjBvE;ACpBC;;EAEE,0BAAA;EACA,6BAAA;CDsBH;ACnBC;;EAEE,0BAAA;EACA,sBAAA;CDqBH;ACfG;;;;;;;;;;;;;;;;;;EAME,0BAAA;EACA,uBAAA;CD6BL;ACbD;EEtEI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EAEA,uHAAA;ECnBF,oEAAA;EH4CA,4BAAA;EACA,sBAAA;CD8DD;AC5DC;;EAEE,0BAAA;EACA,6BAAA;CD8DH;AC3DC;;EAEE,0BAAA;EACA,sBAAA;CD6DH;ACvDG;;;;;;;;;;;;;;;;;;EAME,0BAAA;EACA,uBAAA;CDqEL;ACpDD;EEvEI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EAEA,uHAAA;ECnBF,oEAAA;EH4CA,4BAAA;EACA,sBAAA;CDsGD;ACpGC;;EAEE,0BAAA;EACA,6BAAA;CDsGH;ACnGC;;EAEE,0BAAA;EACA,sBAAA;CDqGH;AC/FG;;;;;;;;;;;;;;;;;;EAME,0BAAA;EACA,uBAAA;CD6GL;AC3FD;EExEI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EAEA,uHAAA;ECnBF,oEAAA;EH4CA,4BAAA;EACA,sBAAA;CD8ID;AC5IC;;EAEE,0BAAA;EACA,6BAAA;CD8IH;AC3IC;;EAEE,0BAAA;EACA,sBAAA;CD6IH;ACvIG;;;;;;;;;;;;;;;;;;EAME,0BAAA;EACA,uBAAA;CDqJL;AClID;EEzEI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EAEA,uHAAA;ECnBF,oEAAA;EH4CA,4BAAA;EACA,sBAAA;CDsLD;ACpLC;;EAEE,0BAAA;EACA,6BAAA;CDsLH;ACnLC;;EAEE,0BAAA;EACA,sBAAA;CDqLH;AC/KG;;;;;;;;;;;;;;;;;;EAME,0BAAA;EACA,uBAAA;CD6LL;ACzKD;EE1EI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EAEA,uHAAA;ECnBF,oEAAA;EH4CA,4BAAA;EACA,sBAAA;CD8ND;AC5NC;;EAEE,0BAAA;EACA,6BAAA;CD8NH;AC3NC;;EAEE,0BAAA;EACA,sBAAA;CD6NH;ACvNG;;;;;;;;;;;;;;;;;;EAME,0BAAA;EACA,uBAAA;CDqOL;AC1MD;;EClCE,mDAAA;EACQ,2CAAA;CFgPT;ACrMD;;EE3FI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EACA,4BAAA;EACA,uHAAA;EF0FF,0BAAA;CD2MD;ACzMD;;;EEhGI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EACA,4BAAA;EACA,uHAAA;EFgGF,0BAAA;CD+MD;ACtMD;EE7GI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EACA,4BAAA;EACA,uHAAA;ECnBF,oEAAA;EH+HA,mBAAA;ECjEA,4FAAA;EACQ,oFAAA;CF8QT;ACjND;;EE7GI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EACA,4BAAA;EACA,uHAAA;ED2CF,yDAAA;EACQ,iDAAA;CFwRT;AC9MD;;EAEE,+CAAA;CDgND;AC5MD;EEhII,sEAAA;EACA,iEAAA;EACA,2FAAA;EAAA,oEAAA;EACA,4BAAA;EACA,uHAAA;ECnBF,oEAAA;EHkJA,mBAAA;CDkND;ACrND;;EEhII,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EACA,4BAAA;EACA,uHAAA;ED2CF,wDAAA;EACQ,gDAAA;CF+ST;AC/ND;;EAYI,0CAAA;CDuNH;AClND;;;EAGE,iBAAA;CDoND;AC/LD;EAfI;;;IAGE,YAAA;IE7JF,yEAAA;IACA,oEAAA;IACA,8FAAA;IAAA,uEAAA;IACA,4BAAA;IACA,uHAAA;GH+WD;CACF;AC3MD;EACE,8CAAA;EC3HA,2FAAA;EACQ,mFAAA;CFyUT;ACnMD;EEtLI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EACA,4BAAA;EACA,uHAAA;EF8KF,sBAAA;CD+MD;AC1MD;EEvLI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EACA,4BAAA;EACA,uHAAA;EF8KF,sBAAA;CDuND;ACjND;EExLI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EACA,4BAAA;EACA,uHAAA;EF8KF,sBAAA;CD+ND;ACxND;EEzLI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EACA,4BAAA;EACA,uHAAA;EF8KF,sBAAA;CDuOD;ACxND;EEjMI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EACA,4BAAA;EACA,uHAAA;CH4ZH;ACrND;EE3MI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EACA,4BAAA;EACA,uHAAA;CHmaH;AC3ND;EE5MI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EACA,4BAAA;EACA,uHAAA;CH0aH;ACjOD;EE7MI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EACA,4BAAA;EACA,uHAAA;CHibH;ACvOD;EE9MI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EACA,4BAAA;EACA,uHAAA;CHwbH;AC7OD;EE/MI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EACA,4BAAA;EACA,uHAAA;CH+bH;AChPD;EElLI,8MAAA;EACA,yMAAA;EACA,sMAAA;CHqaH;AC5OD;EACE,mBAAA;EC9KA,mDAAA;EACQ,2CAAA;CF6ZT;AC7OD;;;EAGE,8BAAA;EEnOE,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EACA,4BAAA;EACA,uHAAA;EFiOF,sBAAA;CDmPD;ACxPD;;;EAQI,kBAAA;CDqPH;AC3OD;ECnME,kDAAA;EACQ,0CAAA;CFibT;ACrOD;EE5PI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EACA,4BAAA;EACA,uHAAA;CHoeH;AC3OD;EE7PI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EACA,4BAAA;EACA,uHAAA;CH2eH;ACjPD;EE9PI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EACA,4BAAA;EACA,uHAAA;CHkfH;ACvPD;EE/PI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EACA,4BAAA;EACA,uHAAA;CHyfH;AC7PD;EEhQI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EACA,4BAAA;EACA,uHAAA;CHggBH;ACnQD;EEjQI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EACA,4BAAA;EACA,uHAAA;CHugBH;ACnQD;EExQI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EACA,4BAAA;EACA,uHAAA;EFsQF,sBAAA;EC3NA,0FAAA;EACQ,kFAAA;CFqeT","file":"bootstrap-theme.css","sourcesContent":["/*!\n * Bootstrap v3.3.7 (http://getbootstrap.com)\n * Copyright 2011-2016 Twitter, Inc.\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)\n */\n.btn-default,\n.btn-primary,\n.btn-success,\n.btn-info,\n.btn-warning,\n.btn-danger {\n text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.2);\n -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.15), 0 1px 1px rgba(0, 0, 0, 0.075);\n box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.15), 0 1px 1px rgba(0, 0, 0, 0.075);\n}\n.btn-default:active,\n.btn-primary:active,\n.btn-success:active,\n.btn-info:active,\n.btn-warning:active,\n.btn-danger:active,\n.btn-default.active,\n.btn-primary.active,\n.btn-success.active,\n.btn-info.active,\n.btn-warning.active,\n.btn-danger.active {\n -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);\n box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);\n}\n.btn-default.disabled,\n.btn-primary.disabled,\n.btn-success.disabled,\n.btn-info.disabled,\n.btn-warning.disabled,\n.btn-danger.disabled,\n.btn-default[disabled],\n.btn-primary[disabled],\n.btn-success[disabled],\n.btn-info[disabled],\n.btn-warning[disabled],\n.btn-danger[disabled],\nfieldset[disabled] .btn-default,\nfieldset[disabled] .btn-primary,\nfieldset[disabled] .btn-success,\nfieldset[disabled] .btn-info,\nfieldset[disabled] .btn-warning,\nfieldset[disabled] .btn-danger {\n -webkit-box-shadow: none;\n box-shadow: none;\n}\n.btn-default .badge,\n.btn-primary .badge,\n.btn-success .badge,\n.btn-info .badge,\n.btn-warning .badge,\n.btn-danger .badge {\n text-shadow: none;\n}\n.btn:active,\n.btn.active {\n background-image: none;\n}\n.btn-default {\n background-image: -webkit-linear-gradient(top, #fff 0%, #e0e0e0 100%);\n background-image: -o-linear-gradient(top, #fff 0%, #e0e0e0 100%);\n background-image: linear-gradient(to bottom, #fff 0%, #e0e0e0 100%);\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#ffe0e0e0', GradientType=0);\n filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);\n background-repeat: repeat-x;\n border-color: #dbdbdb;\n text-shadow: 0 1px 0 #fff;\n border-color: #ccc;\n}\n.btn-default:hover,\n.btn-default:focus {\n background-color: #e0e0e0;\n background-position: 0 -15px;\n}\n.btn-default:active,\n.btn-default.active {\n background-color: #e0e0e0;\n border-color: #dbdbdb;\n}\n.btn-default.disabled,\n.btn-default[disabled],\nfieldset[disabled] .btn-default,\n.btn-default.disabled:hover,\n.btn-default[disabled]:hover,\nfieldset[disabled] .btn-default:hover,\n.btn-default.disabled:focus,\n.btn-default[disabled]:focus,\nfieldset[disabled] .btn-default:focus,\n.btn-default.disabled.focus,\n.btn-default[disabled].focus,\nfieldset[disabled] .btn-default.focus,\n.btn-default.disabled:active,\n.btn-default[disabled]:active,\nfieldset[disabled] .btn-default:active,\n.btn-default.disabled.active,\n.btn-default[disabled].active,\nfieldset[disabled] .btn-default.active {\n background-color: #e0e0e0;\n background-image: none;\n}\n.btn-primary {\n background-image: -webkit-linear-gradient(top, #337ab7 0%, #265a88 100%);\n background-image: -o-linear-gradient(top, #337ab7 0%, #265a88 100%);\n background-image: linear-gradient(to bottom, #337ab7 0%, #265a88 100%);\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff265a88', GradientType=0);\n filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);\n background-repeat: repeat-x;\n border-color: #245580;\n}\n.btn-primary:hover,\n.btn-primary:focus {\n background-color: #265a88;\n background-position: 0 -15px;\n}\n.btn-primary:active,\n.btn-primary.active {\n background-color: #265a88;\n border-color: #245580;\n}\n.btn-primary.disabled,\n.btn-primary[disabled],\nfieldset[disabled] .btn-primary,\n.btn-primary.disabled:hover,\n.btn-primary[disabled]:hover,\nfieldset[disabled] .btn-primary:hover,\n.btn-primary.disabled:focus,\n.btn-primary[disabled]:focus,\nfieldset[disabled] .btn-primary:focus,\n.btn-primary.disabled.focus,\n.btn-primary[disabled].focus,\nfieldset[disabled] .btn-primary.focus,\n.btn-primary.disabled:active,\n.btn-primary[disabled]:active,\nfieldset[disabled] .btn-primary:active,\n.btn-primary.disabled.active,\n.btn-primary[disabled].active,\nfieldset[disabled] .btn-primary.active {\n background-color: #265a88;\n background-image: none;\n}\n.btn-success {\n background-image: -webkit-linear-gradient(top, #5cb85c 0%, #419641 100%);\n background-image: -o-linear-gradient(top, #5cb85c 0%, #419641 100%);\n background-image: linear-gradient(to bottom, #5cb85c 0%, #419641 100%);\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff419641', GradientType=0);\n filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);\n background-repeat: repeat-x;\n border-color: #3e8f3e;\n}\n.btn-success:hover,\n.btn-success:focus {\n background-color: #419641;\n background-position: 0 -15px;\n}\n.btn-success:active,\n.btn-success.active {\n background-color: #419641;\n border-color: #3e8f3e;\n}\n.btn-success.disabled,\n.btn-success[disabled],\nfieldset[disabled] .btn-success,\n.btn-success.disabled:hover,\n.btn-success[disabled]:hover,\nfieldset[disabled] .btn-success:hover,\n.btn-success.disabled:focus,\n.btn-success[disabled]:focus,\nfieldset[disabled] .btn-success:focus,\n.btn-success.disabled.focus,\n.btn-success[disabled].focus,\nfieldset[disabled] .btn-success.focus,\n.btn-success.disabled:active,\n.btn-success[disabled]:active,\nfieldset[disabled] .btn-success:active,\n.btn-success.disabled.active,\n.btn-success[disabled].active,\nfieldset[disabled] .btn-success.active {\n background-color: #419641;\n background-image: none;\n}\n.btn-info {\n background-image: -webkit-linear-gradient(top, #5bc0de 0%, #2aabd2 100%);\n background-image: -o-linear-gradient(top, #5bc0de 0%, #2aabd2 100%);\n background-image: linear-gradient(to bottom, #5bc0de 0%, #2aabd2 100%);\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff2aabd2', GradientType=0);\n filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);\n background-repeat: repeat-x;\n border-color: #28a4c9;\n}\n.btn-info:hover,\n.btn-info:focus {\n background-color: #2aabd2;\n background-position: 0 -15px;\n}\n.btn-info:active,\n.btn-info.active {\n background-color: #2aabd2;\n border-color: #28a4c9;\n}\n.btn-info.disabled,\n.btn-info[disabled],\nfieldset[disabled] .btn-info,\n.btn-info.disabled:hover,\n.btn-info[disabled]:hover,\nfieldset[disabled] .btn-info:hover,\n.btn-info.disabled:focus,\n.btn-info[disabled]:focus,\nfieldset[disabled] .btn-info:focus,\n.btn-info.disabled.focus,\n.btn-info[disabled].focus,\nfieldset[disabled] .btn-info.focus,\n.btn-info.disabled:active,\n.btn-info[disabled]:active,\nfieldset[disabled] .btn-info:active,\n.btn-info.disabled.active,\n.btn-info[disabled].active,\nfieldset[disabled] .btn-info.active {\n background-color: #2aabd2;\n background-image: none;\n}\n.btn-warning {\n background-image: -webkit-linear-gradient(top, #f0ad4e 0%, #eb9316 100%);\n background-image: -o-linear-gradient(top, #f0ad4e 0%, #eb9316 100%);\n background-image: linear-gradient(to bottom, #f0ad4e 0%, #eb9316 100%);\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffeb9316', GradientType=0);\n filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);\n background-repeat: repeat-x;\n border-color: #e38d13;\n}\n.btn-warning:hover,\n.btn-warning:focus {\n background-color: #eb9316;\n background-position: 0 -15px;\n}\n.btn-warning:active,\n.btn-warning.active {\n background-color: #eb9316;\n border-color: #e38d13;\n}\n.btn-warning.disabled,\n.btn-warning[disabled],\nfieldset[disabled] .btn-warning,\n.btn-warning.disabled:hover,\n.btn-warning[disabled]:hover,\nfieldset[disabled] .btn-warning:hover,\n.btn-warning.disabled:focus,\n.btn-warning[disabled]:focus,\nfieldset[disabled] .btn-warning:focus,\n.btn-warning.disabled.focus,\n.btn-warning[disabled].focus,\nfieldset[disabled] .btn-warning.focus,\n.btn-warning.disabled:active,\n.btn-warning[disabled]:active,\nfieldset[disabled] .btn-warning:active,\n.btn-warning.disabled.active,\n.btn-warning[disabled].active,\nfieldset[disabled] .btn-warning.active {\n background-color: #eb9316;\n background-image: none;\n}\n.btn-danger {\n background-image: -webkit-linear-gradient(top, #d9534f 0%, #c12e2a 100%);\n background-image: -o-linear-gradient(top, #d9534f 0%, #c12e2a 100%);\n background-image: linear-gradient(to bottom, #d9534f 0%, #c12e2a 100%);\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc12e2a', GradientType=0);\n filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);\n background-repeat: repeat-x;\n border-color: #b92c28;\n}\n.btn-danger:hover,\n.btn-danger:focus {\n background-color: #c12e2a;\n background-position: 0 -15px;\n}\n.btn-danger:active,\n.btn-danger.active {\n background-color: #c12e2a;\n border-color: #b92c28;\n}\n.btn-danger.disabled,\n.btn-danger[disabled],\nfieldset[disabled] .btn-danger,\n.btn-danger.disabled:hover,\n.btn-danger[disabled]:hover,\nfieldset[disabled] .btn-danger:hover,\n.btn-danger.disabled:focus,\n.btn-danger[disabled]:focus,\nfieldset[disabled] .btn-danger:focus,\n.btn-danger.disabled.focus,\n.btn-danger[disabled].focus,\nfieldset[disabled] .btn-danger.focus,\n.btn-danger.disabled:active,\n.btn-danger[disabled]:active,\nfieldset[disabled] .btn-danger:active,\n.btn-danger.disabled.active,\n.btn-danger[disabled].active,\nfieldset[disabled] .btn-danger.active {\n background-color: #c12e2a;\n background-image: none;\n}\n.thumbnail,\n.img-thumbnail {\n -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.075);\n box-shadow: 0 1px 2px rgba(0, 0, 0, 0.075);\n}\n.dropdown-menu > li > a:hover,\n.dropdown-menu > li > a:focus {\n background-image: -webkit-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%);\n background-image: -o-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%);\n background-image: linear-gradient(to bottom, #f5f5f5 0%, #e8e8e8 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0);\n background-color: #e8e8e8;\n}\n.dropdown-menu > .active > a,\n.dropdown-menu > .active > a:hover,\n.dropdown-menu > .active > a:focus {\n background-image: -webkit-linear-gradient(top, #337ab7 0%, #2e6da4 100%);\n background-image: -o-linear-gradient(top, #337ab7 0%, #2e6da4 100%);\n background-image: linear-gradient(to bottom, #337ab7 0%, #2e6da4 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);\n background-color: #2e6da4;\n}\n.navbar-default {\n background-image: -webkit-linear-gradient(top, #ffffff 0%, #f8f8f8 100%);\n background-image: -o-linear-gradient(top, #ffffff 0%, #f8f8f8 100%);\n background-image: linear-gradient(to bottom, #ffffff 0%, #f8f8f8 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#fff8f8f8', GradientType=0);\n filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);\n border-radius: 4px;\n -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.15), 0 1px 5px rgba(0, 0, 0, 0.075);\n box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.15), 0 1px 5px rgba(0, 0, 0, 0.075);\n}\n.navbar-default .navbar-nav > .open > a,\n.navbar-default .navbar-nav > .active > a {\n background-image: -webkit-linear-gradient(top, #dbdbdb 0%, #e2e2e2 100%);\n background-image: -o-linear-gradient(top, #dbdbdb 0%, #e2e2e2 100%);\n background-image: linear-gradient(to bottom, #dbdbdb 0%, #e2e2e2 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdbdbdb', endColorstr='#ffe2e2e2', GradientType=0);\n -webkit-box-shadow: inset 0 3px 9px rgba(0, 0, 0, 0.075);\n box-shadow: inset 0 3px 9px rgba(0, 0, 0, 0.075);\n}\n.navbar-brand,\n.navbar-nav > li > a {\n text-shadow: 0 1px 0 rgba(255, 255, 255, 0.25);\n}\n.navbar-inverse {\n background-image: -webkit-linear-gradient(top, #3c3c3c 0%, #222 100%);\n background-image: -o-linear-gradient(top, #3c3c3c 0%, #222 100%);\n background-image: linear-gradient(to bottom, #3c3c3c 0%, #222 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff3c3c3c', endColorstr='#ff222222', GradientType=0);\n filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);\n border-radius: 4px;\n}\n.navbar-inverse .navbar-nav > .open > a,\n.navbar-inverse .navbar-nav > .active > a {\n background-image: -webkit-linear-gradient(top, #080808 0%, #0f0f0f 100%);\n background-image: -o-linear-gradient(top, #080808 0%, #0f0f0f 100%);\n background-image: linear-gradient(to bottom, #080808 0%, #0f0f0f 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff080808', endColorstr='#ff0f0f0f', GradientType=0);\n -webkit-box-shadow: inset 0 3px 9px rgba(0, 0, 0, 0.25);\n box-shadow: inset 0 3px 9px rgba(0, 0, 0, 0.25);\n}\n.navbar-inverse .navbar-brand,\n.navbar-inverse .navbar-nav > li > a {\n text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);\n}\n.navbar-static-top,\n.navbar-fixed-top,\n.navbar-fixed-bottom {\n border-radius: 0;\n}\n@media (max-width: 767px) {\n .navbar .navbar-nav .open .dropdown-menu > .active > a,\n .navbar .navbar-nav .open .dropdown-menu > .active > a:hover,\n .navbar .navbar-nav .open .dropdown-menu > .active > a:focus {\n color: #fff;\n background-image: -webkit-linear-gradient(top, #337ab7 0%, #2e6da4 100%);\n background-image: -o-linear-gradient(top, #337ab7 0%, #2e6da4 100%);\n background-image: linear-gradient(to bottom, #337ab7 0%, #2e6da4 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);\n }\n}\n.alert {\n text-shadow: 0 1px 0 rgba(255, 255, 255, 0.2);\n -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.25), 0 1px 2px rgba(0, 0, 0, 0.05);\n box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.25), 0 1px 2px rgba(0, 0, 0, 0.05);\n}\n.alert-success {\n background-image: -webkit-linear-gradient(top, #dff0d8 0%, #c8e5bc 100%);\n background-image: -o-linear-gradient(top, #dff0d8 0%, #c8e5bc 100%);\n background-image: linear-gradient(to bottom, #dff0d8 0%, #c8e5bc 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffc8e5bc', GradientType=0);\n border-color: #b2dba1;\n}\n.alert-info {\n background-image: -webkit-linear-gradient(top, #d9edf7 0%, #b9def0 100%);\n background-image: -o-linear-gradient(top, #d9edf7 0%, #b9def0 100%);\n background-image: linear-gradient(to bottom, #d9edf7 0%, #b9def0 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffb9def0', GradientType=0);\n border-color: #9acfea;\n}\n.alert-warning {\n background-image: -webkit-linear-gradient(top, #fcf8e3 0%, #f8efc0 100%);\n background-image: -o-linear-gradient(top, #fcf8e3 0%, #f8efc0 100%);\n background-image: linear-gradient(to bottom, #fcf8e3 0%, #f8efc0 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fff8efc0', GradientType=0);\n border-color: #f5e79e;\n}\n.alert-danger {\n background-image: -webkit-linear-gradient(top, #f2dede 0%, #e7c3c3 100%);\n background-image: -o-linear-gradient(top, #f2dede 0%, #e7c3c3 100%);\n background-image: linear-gradient(to bottom, #f2dede 0%, #e7c3c3 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffe7c3c3', GradientType=0);\n border-color: #dca7a7;\n}\n.progress {\n background-image: -webkit-linear-gradient(top, #ebebeb 0%, #f5f5f5 100%);\n background-image: -o-linear-gradient(top, #ebebeb 0%, #f5f5f5 100%);\n background-image: linear-gradient(to bottom, #ebebeb 0%, #f5f5f5 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffebebeb', endColorstr='#fff5f5f5', GradientType=0);\n}\n.progress-bar {\n background-image: -webkit-linear-gradient(top, #337ab7 0%, #286090 100%);\n background-image: -o-linear-gradient(top, #337ab7 0%, #286090 100%);\n background-image: linear-gradient(to bottom, #337ab7 0%, #286090 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff286090', GradientType=0);\n}\n.progress-bar-success {\n background-image: -webkit-linear-gradient(top, #5cb85c 0%, #449d44 100%);\n background-image: -o-linear-gradient(top, #5cb85c 0%, #449d44 100%);\n background-image: linear-gradient(to bottom, #5cb85c 0%, #449d44 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff449d44', GradientType=0);\n}\n.progress-bar-info {\n background-image: -webkit-linear-gradient(top, #5bc0de 0%, #31b0d5 100%);\n background-image: -o-linear-gradient(top, #5bc0de 0%, #31b0d5 100%);\n background-image: linear-gradient(to bottom, #5bc0de 0%, #31b0d5 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff31b0d5', GradientType=0);\n}\n.progress-bar-warning {\n background-image: -webkit-linear-gradient(top, #f0ad4e 0%, #ec971f 100%);\n background-image: -o-linear-gradient(top, #f0ad4e 0%, #ec971f 100%);\n background-image: linear-gradient(to bottom, #f0ad4e 0%, #ec971f 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffec971f', GradientType=0);\n}\n.progress-bar-danger {\n background-image: -webkit-linear-gradient(top, #d9534f 0%, #c9302c 100%);\n background-image: -o-linear-gradient(top, #d9534f 0%, #c9302c 100%);\n background-image: linear-gradient(to bottom, #d9534f 0%, #c9302c 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc9302c', GradientType=0);\n}\n.progress-bar-striped {\n background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n}\n.list-group {\n border-radius: 4px;\n -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.075);\n box-shadow: 0 1px 2px rgba(0, 0, 0, 0.075);\n}\n.list-group-item.active,\n.list-group-item.active:hover,\n.list-group-item.active:focus {\n text-shadow: 0 -1px 0 #286090;\n background-image: -webkit-linear-gradient(top, #337ab7 0%, #2b669a 100%);\n background-image: -o-linear-gradient(top, #337ab7 0%, #2b669a 100%);\n background-image: linear-gradient(to bottom, #337ab7 0%, #2b669a 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2b669a', GradientType=0);\n border-color: #2b669a;\n}\n.list-group-item.active .badge,\n.list-group-item.active:hover .badge,\n.list-group-item.active:focus .badge {\n text-shadow: none;\n}\n.panel {\n -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);\n box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);\n}\n.panel-default > .panel-heading {\n background-image: -webkit-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%);\n background-image: -o-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%);\n background-image: linear-gradient(to bottom, #f5f5f5 0%, #e8e8e8 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0);\n}\n.panel-primary > .panel-heading {\n background-image: -webkit-linear-gradient(top, #337ab7 0%, #2e6da4 100%);\n background-image: -o-linear-gradient(top, #337ab7 0%, #2e6da4 100%);\n background-image: linear-gradient(to bottom, #337ab7 0%, #2e6da4 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);\n}\n.panel-success > .panel-heading {\n background-image: -webkit-linear-gradient(top, #dff0d8 0%, #d0e9c6 100%);\n background-image: -o-linear-gradient(top, #dff0d8 0%, #d0e9c6 100%);\n background-image: linear-gradient(to bottom, #dff0d8 0%, #d0e9c6 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffd0e9c6', GradientType=0);\n}\n.panel-info > .panel-heading {\n background-image: -webkit-linear-gradient(top, #d9edf7 0%, #c4e3f3 100%);\n background-image: -o-linear-gradient(top, #d9edf7 0%, #c4e3f3 100%);\n background-image: linear-gradient(to bottom, #d9edf7 0%, #c4e3f3 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffc4e3f3', GradientType=0);\n}\n.panel-warning > .panel-heading {\n background-image: -webkit-linear-gradient(top, #fcf8e3 0%, #faf2cc 100%);\n background-image: -o-linear-gradient(top, #fcf8e3 0%, #faf2cc 100%);\n background-image: linear-gradient(to bottom, #fcf8e3 0%, #faf2cc 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fffaf2cc', GradientType=0);\n}\n.panel-danger > .panel-heading {\n background-image: -webkit-linear-gradient(top, #f2dede 0%, #ebcccc 100%);\n background-image: -o-linear-gradient(top, #f2dede 0%, #ebcccc 100%);\n background-image: linear-gradient(to bottom, #f2dede 0%, #ebcccc 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffebcccc', GradientType=0);\n}\n.well {\n background-image: -webkit-linear-gradient(top, #e8e8e8 0%, #f5f5f5 100%);\n background-image: -o-linear-gradient(top, #e8e8e8 0%, #f5f5f5 100%);\n background-image: linear-gradient(to bottom, #e8e8e8 0%, #f5f5f5 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffe8e8e8', endColorstr='#fff5f5f5', GradientType=0);\n border-color: #dcdcdc;\n -webkit-box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.05), 0 1px 0 rgba(255, 255, 255, 0.1);\n box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.05), 0 1px 0 rgba(255, 255, 255, 0.1);\n}\n/*# sourceMappingURL=bootstrap-theme.css.map */","/*!\n * Bootstrap v3.3.7 (http://getbootstrap.com)\n * Copyright 2011-2016 Twitter, Inc.\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)\n */\n\n//\n// Load core variables and mixins\n// --------------------------------------------------\n\n@import \"variables.less\";\n@import \"mixins.less\";\n\n\n//\n// Buttons\n// --------------------------------------------------\n\n// Common styles\n.btn-default,\n.btn-primary,\n.btn-success,\n.btn-info,\n.btn-warning,\n.btn-danger {\n text-shadow: 0 -1px 0 rgba(0,0,0,.2);\n @shadow: inset 0 1px 0 rgba(255,255,255,.15), 0 1px 1px rgba(0,0,0,.075);\n .box-shadow(@shadow);\n\n // Reset the shadow\n &:active,\n &.active {\n .box-shadow(inset 0 3px 5px rgba(0,0,0,.125));\n }\n\n &.disabled,\n &[disabled],\n fieldset[disabled] & {\n .box-shadow(none);\n }\n\n .badge {\n text-shadow: none;\n }\n}\n\n// Mixin for generating new styles\n.btn-styles(@btn-color: #555) {\n #gradient > .vertical(@start-color: @btn-color; @end-color: darken(@btn-color, 12%));\n .reset-filter(); // Disable gradients for IE9 because filter bleeds through rounded corners; see https://github.com/twbs/bootstrap/issues/10620\n background-repeat: repeat-x;\n border-color: darken(@btn-color, 14%);\n\n &:hover,\n &:focus {\n background-color: darken(@btn-color, 12%);\n background-position: 0 -15px;\n }\n\n &:active,\n &.active {\n background-color: darken(@btn-color, 12%);\n border-color: darken(@btn-color, 14%);\n }\n\n &.disabled,\n &[disabled],\n fieldset[disabled] & {\n &,\n &:hover,\n &:focus,\n &.focus,\n &:active,\n &.active {\n background-color: darken(@btn-color, 12%);\n background-image: none;\n }\n }\n}\n\n// Common styles\n.btn {\n // Remove the gradient for the pressed/active state\n &:active,\n &.active {\n background-image: none;\n }\n}\n\n// Apply the mixin to the buttons\n.btn-default { .btn-styles(@btn-default-bg); text-shadow: 0 1px 0 #fff; border-color: #ccc; }\n.btn-primary { .btn-styles(@btn-primary-bg); }\n.btn-success { .btn-styles(@btn-success-bg); }\n.btn-info { .btn-styles(@btn-info-bg); }\n.btn-warning { .btn-styles(@btn-warning-bg); }\n.btn-danger { .btn-styles(@btn-danger-bg); }\n\n\n//\n// Images\n// --------------------------------------------------\n\n.thumbnail,\n.img-thumbnail {\n .box-shadow(0 1px 2px rgba(0,0,0,.075));\n}\n\n\n//\n// Dropdowns\n// --------------------------------------------------\n\n.dropdown-menu > li > a:hover,\n.dropdown-menu > li > a:focus {\n #gradient > .vertical(@start-color: @dropdown-link-hover-bg; @end-color: darken(@dropdown-link-hover-bg, 5%));\n background-color: darken(@dropdown-link-hover-bg, 5%);\n}\n.dropdown-menu > .active > a,\n.dropdown-menu > .active > a:hover,\n.dropdown-menu > .active > a:focus {\n #gradient > .vertical(@start-color: @dropdown-link-active-bg; @end-color: darken(@dropdown-link-active-bg, 5%));\n background-color: darken(@dropdown-link-active-bg, 5%);\n}\n\n\n//\n// Navbar\n// --------------------------------------------------\n\n// Default navbar\n.navbar-default {\n #gradient > .vertical(@start-color: lighten(@navbar-default-bg, 10%); @end-color: @navbar-default-bg);\n .reset-filter(); // Remove gradient in IE<10 to fix bug where dropdowns don't get triggered\n border-radius: @navbar-border-radius;\n @shadow: inset 0 1px 0 rgba(255,255,255,.15), 0 1px 5px rgba(0,0,0,.075);\n .box-shadow(@shadow);\n\n .navbar-nav > .open > a,\n .navbar-nav > .active > a {\n #gradient > .vertical(@start-color: darken(@navbar-default-link-active-bg, 5%); @end-color: darken(@navbar-default-link-active-bg, 2%));\n .box-shadow(inset 0 3px 9px rgba(0,0,0,.075));\n }\n}\n.navbar-brand,\n.navbar-nav > li > a {\n text-shadow: 0 1px 0 rgba(255,255,255,.25);\n}\n\n// Inverted navbar\n.navbar-inverse {\n #gradient > .vertical(@start-color: lighten(@navbar-inverse-bg, 10%); @end-color: @navbar-inverse-bg);\n .reset-filter(); // Remove gradient in IE<10 to fix bug where dropdowns don't get triggered; see https://github.com/twbs/bootstrap/issues/10257\n border-radius: @navbar-border-radius;\n .navbar-nav > .open > a,\n .navbar-nav > .active > a {\n #gradient > .vertical(@start-color: @navbar-inverse-link-active-bg; @end-color: lighten(@navbar-inverse-link-active-bg, 2.5%));\n .box-shadow(inset 0 3px 9px rgba(0,0,0,.25));\n }\n\n .navbar-brand,\n .navbar-nav > li > a {\n text-shadow: 0 -1px 0 rgba(0,0,0,.25);\n }\n}\n\n// Undo rounded corners in static and fixed navbars\n.navbar-static-top,\n.navbar-fixed-top,\n.navbar-fixed-bottom {\n border-radius: 0;\n}\n\n// Fix active state of dropdown items in collapsed mode\n@media (max-width: @grid-float-breakpoint-max) {\n .navbar .navbar-nav .open .dropdown-menu > .active > a {\n &,\n &:hover,\n &:focus {\n color: #fff;\n #gradient > .vertical(@start-color: @dropdown-link-active-bg; @end-color: darken(@dropdown-link-active-bg, 5%));\n }\n }\n}\n\n\n//\n// Alerts\n// --------------------------------------------------\n\n// Common styles\n.alert {\n text-shadow: 0 1px 0 rgba(255,255,255,.2);\n @shadow: inset 0 1px 0 rgba(255,255,255,.25), 0 1px 2px rgba(0,0,0,.05);\n .box-shadow(@shadow);\n}\n\n// Mixin for generating new styles\n.alert-styles(@color) {\n #gradient > .vertical(@start-color: @color; @end-color: darken(@color, 7.5%));\n border-color: darken(@color, 15%);\n}\n\n// Apply the mixin to the alerts\n.alert-success { .alert-styles(@alert-success-bg); }\n.alert-info { .alert-styles(@alert-info-bg); }\n.alert-warning { .alert-styles(@alert-warning-bg); }\n.alert-danger { .alert-styles(@alert-danger-bg); }\n\n\n//\n// Progress bars\n// --------------------------------------------------\n\n// Give the progress background some depth\n.progress {\n #gradient > .vertical(@start-color: darken(@progress-bg, 4%); @end-color: @progress-bg)\n}\n\n// Mixin for generating new styles\n.progress-bar-styles(@color) {\n #gradient > .vertical(@start-color: @color; @end-color: darken(@color, 10%));\n}\n\n// Apply the mixin to the progress bars\n.progress-bar { .progress-bar-styles(@progress-bar-bg); }\n.progress-bar-success { .progress-bar-styles(@progress-bar-success-bg); }\n.progress-bar-info { .progress-bar-styles(@progress-bar-info-bg); }\n.progress-bar-warning { .progress-bar-styles(@progress-bar-warning-bg); }\n.progress-bar-danger { .progress-bar-styles(@progress-bar-danger-bg); }\n\n// Reset the striped class because our mixins don't do multiple gradients and\n// the above custom styles override the new `.progress-bar-striped` in v3.2.0.\n.progress-bar-striped {\n #gradient > .striped();\n}\n\n\n//\n// List groups\n// --------------------------------------------------\n\n.list-group {\n border-radius: @border-radius-base;\n .box-shadow(0 1px 2px rgba(0,0,0,.075));\n}\n.list-group-item.active,\n.list-group-item.active:hover,\n.list-group-item.active:focus {\n text-shadow: 0 -1px 0 darken(@list-group-active-bg, 10%);\n #gradient > .vertical(@start-color: @list-group-active-bg; @end-color: darken(@list-group-active-bg, 7.5%));\n border-color: darken(@list-group-active-border, 7.5%);\n\n .badge {\n text-shadow: none;\n }\n}\n\n\n//\n// Panels\n// --------------------------------------------------\n\n// Common styles\n.panel {\n .box-shadow(0 1px 2px rgba(0,0,0,.05));\n}\n\n// Mixin for generating new styles\n.panel-heading-styles(@color) {\n #gradient > .vertical(@start-color: @color; @end-color: darken(@color, 5%));\n}\n\n// Apply the mixin to the panel headings only\n.panel-default > .panel-heading { .panel-heading-styles(@panel-default-heading-bg); }\n.panel-primary > .panel-heading { .panel-heading-styles(@panel-primary-heading-bg); }\n.panel-success > .panel-heading { .panel-heading-styles(@panel-success-heading-bg); }\n.panel-info > .panel-heading { .panel-heading-styles(@panel-info-heading-bg); }\n.panel-warning > .panel-heading { .panel-heading-styles(@panel-warning-heading-bg); }\n.panel-danger > .panel-heading { .panel-heading-styles(@panel-danger-heading-bg); }\n\n\n//\n// Wells\n// --------------------------------------------------\n\n.well {\n #gradient > .vertical(@start-color: darken(@well-bg, 5%); @end-color: @well-bg);\n border-color: darken(@well-bg, 10%);\n @shadow: inset 0 1px 3px rgba(0,0,0,.05), 0 1px 0 rgba(255,255,255,.1);\n .box-shadow(@shadow);\n}\n","// Vendor Prefixes\n//\n// All vendor mixins are deprecated as of v3.2.0 due to the introduction of\n// Autoprefixer in our Gruntfile. They have been removed in v4.\n\n// - Animations\n// - Backface visibility\n// - Box shadow\n// - Box sizing\n// - Content columns\n// - Hyphens\n// - Placeholder text\n// - Transformations\n// - Transitions\n// - User Select\n\n\n// Animations\n.animation(@animation) {\n -webkit-animation: @animation;\n -o-animation: @animation;\n animation: @animation;\n}\n.animation-name(@name) {\n -webkit-animation-name: @name;\n animation-name: @name;\n}\n.animation-duration(@duration) {\n -webkit-animation-duration: @duration;\n animation-duration: @duration;\n}\n.animation-timing-function(@timing-function) {\n -webkit-animation-timing-function: @timing-function;\n animation-timing-function: @timing-function;\n}\n.animation-delay(@delay) {\n -webkit-animation-delay: @delay;\n animation-delay: @delay;\n}\n.animation-iteration-count(@iteration-count) {\n -webkit-animation-iteration-count: @iteration-count;\n animation-iteration-count: @iteration-count;\n}\n.animation-direction(@direction) {\n -webkit-animation-direction: @direction;\n animation-direction: @direction;\n}\n.animation-fill-mode(@fill-mode) {\n -webkit-animation-fill-mode: @fill-mode;\n animation-fill-mode: @fill-mode;\n}\n\n// Backface visibility\n// Prevent browsers from flickering when using CSS 3D transforms.\n// Default value is `visible`, but can be changed to `hidden`\n\n.backface-visibility(@visibility) {\n -webkit-backface-visibility: @visibility;\n -moz-backface-visibility: @visibility;\n backface-visibility: @visibility;\n}\n\n// Drop shadows\n//\n// Note: Deprecated `.box-shadow()` as of v3.1.0 since all of Bootstrap's\n// supported browsers that have box shadow capabilities now support it.\n\n.box-shadow(@shadow) {\n -webkit-box-shadow: @shadow; // iOS <4.3 & Android <4.1\n box-shadow: @shadow;\n}\n\n// Box sizing\n.box-sizing(@boxmodel) {\n -webkit-box-sizing: @boxmodel;\n -moz-box-sizing: @boxmodel;\n box-sizing: @boxmodel;\n}\n\n// CSS3 Content Columns\n.content-columns(@column-count; @column-gap: @grid-gutter-width) {\n -webkit-column-count: @column-count;\n -moz-column-count: @column-count;\n column-count: @column-count;\n -webkit-column-gap: @column-gap;\n -moz-column-gap: @column-gap;\n column-gap: @column-gap;\n}\n\n// Optional hyphenation\n.hyphens(@mode: auto) {\n word-wrap: break-word;\n -webkit-hyphens: @mode;\n -moz-hyphens: @mode;\n -ms-hyphens: @mode; // IE10+\n -o-hyphens: @mode;\n hyphens: @mode;\n}\n\n// Placeholder text\n.placeholder(@color: @input-color-placeholder) {\n // Firefox\n &::-moz-placeholder {\n color: @color;\n opacity: 1; // Override Firefox's unusual default opacity; see https://github.com/twbs/bootstrap/pull/11526\n }\n &:-ms-input-placeholder { color: @color; } // Internet Explorer 10+\n &::-webkit-input-placeholder { color: @color; } // Safari and Chrome\n}\n\n// Transformations\n.scale(@ratio) {\n -webkit-transform: scale(@ratio);\n -ms-transform: scale(@ratio); // IE9 only\n -o-transform: scale(@ratio);\n transform: scale(@ratio);\n}\n.scale(@ratioX; @ratioY) {\n -webkit-transform: scale(@ratioX, @ratioY);\n -ms-transform: scale(@ratioX, @ratioY); // IE9 only\n -o-transform: scale(@ratioX, @ratioY);\n transform: scale(@ratioX, @ratioY);\n}\n.scaleX(@ratio) {\n -webkit-transform: scaleX(@ratio);\n -ms-transform: scaleX(@ratio); // IE9 only\n -o-transform: scaleX(@ratio);\n transform: scaleX(@ratio);\n}\n.scaleY(@ratio) {\n -webkit-transform: scaleY(@ratio);\n -ms-transform: scaleY(@ratio); // IE9 only\n -o-transform: scaleY(@ratio);\n transform: scaleY(@ratio);\n}\n.skew(@x; @y) {\n -webkit-transform: skewX(@x) skewY(@y);\n -ms-transform: skewX(@x) skewY(@y); // See https://github.com/twbs/bootstrap/issues/4885; IE9+\n -o-transform: skewX(@x) skewY(@y);\n transform: skewX(@x) skewY(@y);\n}\n.translate(@x; @y) {\n -webkit-transform: translate(@x, @y);\n -ms-transform: translate(@x, @y); // IE9 only\n -o-transform: translate(@x, @y);\n transform: translate(@x, @y);\n}\n.translate3d(@x; @y; @z) {\n -webkit-transform: translate3d(@x, @y, @z);\n transform: translate3d(@x, @y, @z);\n}\n.rotate(@degrees) {\n -webkit-transform: rotate(@degrees);\n -ms-transform: rotate(@degrees); // IE9 only\n -o-transform: rotate(@degrees);\n transform: rotate(@degrees);\n}\n.rotateX(@degrees) {\n -webkit-transform: rotateX(@degrees);\n -ms-transform: rotateX(@degrees); // IE9 only\n -o-transform: rotateX(@degrees);\n transform: rotateX(@degrees);\n}\n.rotateY(@degrees) {\n -webkit-transform: rotateY(@degrees);\n -ms-transform: rotateY(@degrees); // IE9 only\n -o-transform: rotateY(@degrees);\n transform: rotateY(@degrees);\n}\n.perspective(@perspective) {\n -webkit-perspective: @perspective;\n -moz-perspective: @perspective;\n perspective: @perspective;\n}\n.perspective-origin(@perspective) {\n -webkit-perspective-origin: @perspective;\n -moz-perspective-origin: @perspective;\n perspective-origin: @perspective;\n}\n.transform-origin(@origin) {\n -webkit-transform-origin: @origin;\n -moz-transform-origin: @origin;\n -ms-transform-origin: @origin; // IE9 only\n transform-origin: @origin;\n}\n\n\n// Transitions\n\n.transition(@transition) {\n -webkit-transition: @transition;\n -o-transition: @transition;\n transition: @transition;\n}\n.transition-property(@transition-property) {\n -webkit-transition-property: @transition-property;\n transition-property: @transition-property;\n}\n.transition-delay(@transition-delay) {\n -webkit-transition-delay: @transition-delay;\n transition-delay: @transition-delay;\n}\n.transition-duration(@transition-duration) {\n -webkit-transition-duration: @transition-duration;\n transition-duration: @transition-duration;\n}\n.transition-timing-function(@timing-function) {\n -webkit-transition-timing-function: @timing-function;\n transition-timing-function: @timing-function;\n}\n.transition-transform(@transition) {\n -webkit-transition: -webkit-transform @transition;\n -moz-transition: -moz-transform @transition;\n -o-transition: -o-transform @transition;\n transition: transform @transition;\n}\n\n\n// User select\n// For selecting text on the page\n\n.user-select(@select) {\n -webkit-user-select: @select;\n -moz-user-select: @select;\n -ms-user-select: @select; // IE10+\n user-select: @select;\n}\n","// Gradients\n\n#gradient {\n\n // Horizontal gradient, from left to right\n //\n // Creates two color stops, start and end, by specifying a color and position for each color stop.\n // Color stops are not available in IE9 and below.\n .horizontal(@start-color: #555; @end-color: #333; @start-percent: 0%; @end-percent: 100%) {\n background-image: -webkit-linear-gradient(left, @start-color @start-percent, @end-color @end-percent); // Safari 5.1-6, Chrome 10+\n background-image: -o-linear-gradient(left, @start-color @start-percent, @end-color @end-percent); // Opera 12\n background-image: linear-gradient(to right, @start-color @start-percent, @end-color @end-percent); // Standard, IE10, Firefox 16+, Opera 12.10+, Safari 7+, Chrome 26+\n background-repeat: repeat-x;\n filter: e(%(\"progid:DXImageTransform.Microsoft.gradient(startColorstr='%d', endColorstr='%d', GradientType=1)\",argb(@start-color),argb(@end-color))); // IE9 and down\n }\n\n // Vertical gradient, from top to bottom\n //\n // Creates two color stops, start and end, by specifying a color and position for each color stop.\n // Color stops are not available in IE9 and below.\n .vertical(@start-color: #555; @end-color: #333; @start-percent: 0%; @end-percent: 100%) {\n background-image: -webkit-linear-gradient(top, @start-color @start-percent, @end-color @end-percent); // Safari 5.1-6, Chrome 10+\n background-image: -o-linear-gradient(top, @start-color @start-percent, @end-color @end-percent); // Opera 12\n background-image: linear-gradient(to bottom, @start-color @start-percent, @end-color @end-percent); // Standard, IE10, Firefox 16+, Opera 12.10+, Safari 7+, Chrome 26+\n background-repeat: repeat-x;\n filter: e(%(\"progid:DXImageTransform.Microsoft.gradient(startColorstr='%d', endColorstr='%d', GradientType=0)\",argb(@start-color),argb(@end-color))); // IE9 and down\n }\n\n .directional(@start-color: #555; @end-color: #333; @deg: 45deg) {\n background-repeat: repeat-x;\n background-image: -webkit-linear-gradient(@deg, @start-color, @end-color); // Safari 5.1-6, Chrome 10+\n background-image: -o-linear-gradient(@deg, @start-color, @end-color); // Opera 12\n background-image: linear-gradient(@deg, @start-color, @end-color); // Standard, IE10, Firefox 16+, Opera 12.10+, Safari 7+, Chrome 26+\n }\n .horizontal-three-colors(@start-color: #00b3ee; @mid-color: #7a43b6; @color-stop: 50%; @end-color: #c3325f) {\n background-image: -webkit-linear-gradient(left, @start-color, @mid-color @color-stop, @end-color);\n background-image: -o-linear-gradient(left, @start-color, @mid-color @color-stop, @end-color);\n background-image: linear-gradient(to right, @start-color, @mid-color @color-stop, @end-color);\n background-repeat: no-repeat;\n filter: e(%(\"progid:DXImageTransform.Microsoft.gradient(startColorstr='%d', endColorstr='%d', GradientType=1)\",argb(@start-color),argb(@end-color))); // IE9 and down, gets no color-stop at all for proper fallback\n }\n .vertical-three-colors(@start-color: #00b3ee; @mid-color: #7a43b6; @color-stop: 50%; @end-color: #c3325f) {\n background-image: -webkit-linear-gradient(@start-color, @mid-color @color-stop, @end-color);\n background-image: -o-linear-gradient(@start-color, @mid-color @color-stop, @end-color);\n background-image: linear-gradient(@start-color, @mid-color @color-stop, @end-color);\n background-repeat: no-repeat;\n filter: e(%(\"progid:DXImageTransform.Microsoft.gradient(startColorstr='%d', endColorstr='%d', GradientType=0)\",argb(@start-color),argb(@end-color))); // IE9 and down, gets no color-stop at all for proper fallback\n }\n .radial(@inner-color: #555; @outer-color: #333) {\n background-image: -webkit-radial-gradient(circle, @inner-color, @outer-color);\n background-image: radial-gradient(circle, @inner-color, @outer-color);\n background-repeat: no-repeat;\n }\n .striped(@color: rgba(255,255,255,.15); @angle: 45deg) {\n background-image: -webkit-linear-gradient(@angle, @color 25%, transparent 25%, transparent 50%, @color 50%, @color 75%, transparent 75%, transparent);\n background-image: -o-linear-gradient(@angle, @color 25%, transparent 25%, transparent 50%, @color 50%, @color 75%, transparent 75%, transparent);\n background-image: linear-gradient(@angle, @color 25%, transparent 25%, transparent 50%, @color 50%, @color 75%, transparent 75%, transparent);\n }\n}\n","// Reset filters for IE\n//\n// When you need to remove a gradient background, do not forget to use this to reset\n// the IE filter for IE9 and below.\n\n.reset-filter() {\n filter: e(%(\"progid:DXImageTransform.Microsoft.gradient(enabled = false)\"));\n}\n"]} \ No newline at end of file diff --git a/OpenAuth.Identity/wwwroot/lib/bootstrap/css/bootstrap-theme.min.css b/OpenAuth.Identity/wwwroot/lib/bootstrap/css/bootstrap-theme.min.css new file mode 100644 index 0000000..5e39401 --- /dev/null +++ b/OpenAuth.Identity/wwwroot/lib/bootstrap/css/bootstrap-theme.min.css @@ -0,0 +1,6 @@ +/*! + * Bootstrap v3.3.7 (http://getbootstrap.com) + * Copyright 2011-2016 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + */.btn-danger,.btn-default,.btn-info,.btn-primary,.btn-success,.btn-warning{text-shadow:0 -1px 0 rgba(0,0,0,.2);-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.15),0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 0 rgba(255,255,255,.15),0 1px 1px rgba(0,0,0,.075)}.btn-danger.active,.btn-danger:active,.btn-default.active,.btn-default:active,.btn-info.active,.btn-info:active,.btn-primary.active,.btn-primary:active,.btn-success.active,.btn-success:active,.btn-warning.active,.btn-warning:active{-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,.125);box-shadow:inset 0 3px 5px rgba(0,0,0,.125)}.btn-danger.disabled,.btn-danger[disabled],.btn-default.disabled,.btn-default[disabled],.btn-info.disabled,.btn-info[disabled],.btn-primary.disabled,.btn-primary[disabled],.btn-success.disabled,.btn-success[disabled],.btn-warning.disabled,.btn-warning[disabled],fieldset[disabled] .btn-danger,fieldset[disabled] .btn-default,fieldset[disabled] .btn-info,fieldset[disabled] .btn-primary,fieldset[disabled] .btn-success,fieldset[disabled] .btn-warning{-webkit-box-shadow:none;box-shadow:none}.btn-danger .badge,.btn-default .badge,.btn-info .badge,.btn-primary .badge,.btn-success .badge,.btn-warning .badge{text-shadow:none}.btn.active,.btn:active{background-image:none}.btn-default{text-shadow:0 1px 0 #fff;background-image:-webkit-linear-gradient(top,#fff 0,#e0e0e0 100%);background-image:-o-linear-gradient(top,#fff 0,#e0e0e0 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#fff),to(#e0e0e0));background-image:linear-gradient(to bottom,#fff 0,#e0e0e0 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#ffe0e0e0', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#dbdbdb;border-color:#ccc}.btn-default:focus,.btn-default:hover{background-color:#e0e0e0;background-position:0 -15px}.btn-default.active,.btn-default:active{background-color:#e0e0e0;border-color:#dbdbdb}.btn-default.disabled,.btn-default.disabled.active,.btn-default.disabled.focus,.btn-default.disabled:active,.btn-default.disabled:focus,.btn-default.disabled:hover,.btn-default[disabled],.btn-default[disabled].active,.btn-default[disabled].focus,.btn-default[disabled]:active,.btn-default[disabled]:focus,.btn-default[disabled]:hover,fieldset[disabled] .btn-default,fieldset[disabled] .btn-default.active,fieldset[disabled] .btn-default.focus,fieldset[disabled] .btn-default:active,fieldset[disabled] .btn-default:focus,fieldset[disabled] .btn-default:hover{background-color:#e0e0e0;background-image:none}.btn-primary{background-image:-webkit-linear-gradient(top,#337ab7 0,#265a88 100%);background-image:-o-linear-gradient(top,#337ab7 0,#265a88 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#337ab7),to(#265a88));background-image:linear-gradient(to bottom,#337ab7 0,#265a88 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff265a88', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#245580}.btn-primary:focus,.btn-primary:hover{background-color:#265a88;background-position:0 -15px}.btn-primary.active,.btn-primary:active{background-color:#265a88;border-color:#245580}.btn-primary.disabled,.btn-primary.disabled.active,.btn-primary.disabled.focus,.btn-primary.disabled:active,.btn-primary.disabled:focus,.btn-primary.disabled:hover,.btn-primary[disabled],.btn-primary[disabled].active,.btn-primary[disabled].focus,.btn-primary[disabled]:active,.btn-primary[disabled]:focus,.btn-primary[disabled]:hover,fieldset[disabled] .btn-primary,fieldset[disabled] .btn-primary.active,fieldset[disabled] .btn-primary.focus,fieldset[disabled] .btn-primary:active,fieldset[disabled] .btn-primary:focus,fieldset[disabled] .btn-primary:hover{background-color:#265a88;background-image:none}.btn-success{background-image:-webkit-linear-gradient(top,#5cb85c 0,#419641 100%);background-image:-o-linear-gradient(top,#5cb85c 0,#419641 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#5cb85c),to(#419641));background-image:linear-gradient(to bottom,#5cb85c 0,#419641 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff419641', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#3e8f3e}.btn-success:focus,.btn-success:hover{background-color:#419641;background-position:0 -15px}.btn-success.active,.btn-success:active{background-color:#419641;border-color:#3e8f3e}.btn-success.disabled,.btn-success.disabled.active,.btn-success.disabled.focus,.btn-success.disabled:active,.btn-success.disabled:focus,.btn-success.disabled:hover,.btn-success[disabled],.btn-success[disabled].active,.btn-success[disabled].focus,.btn-success[disabled]:active,.btn-success[disabled]:focus,.btn-success[disabled]:hover,fieldset[disabled] .btn-success,fieldset[disabled] .btn-success.active,fieldset[disabled] .btn-success.focus,fieldset[disabled] .btn-success:active,fieldset[disabled] .btn-success:focus,fieldset[disabled] .btn-success:hover{background-color:#419641;background-image:none}.btn-info{background-image:-webkit-linear-gradient(top,#5bc0de 0,#2aabd2 100%);background-image:-o-linear-gradient(top,#5bc0de 0,#2aabd2 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#5bc0de),to(#2aabd2));background-image:linear-gradient(to bottom,#5bc0de 0,#2aabd2 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff2aabd2', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#28a4c9}.btn-info:focus,.btn-info:hover{background-color:#2aabd2;background-position:0 -15px}.btn-info.active,.btn-info:active{background-color:#2aabd2;border-color:#28a4c9}.btn-info.disabled,.btn-info.disabled.active,.btn-info.disabled.focus,.btn-info.disabled:active,.btn-info.disabled:focus,.btn-info.disabled:hover,.btn-info[disabled],.btn-info[disabled].active,.btn-info[disabled].focus,.btn-info[disabled]:active,.btn-info[disabled]:focus,.btn-info[disabled]:hover,fieldset[disabled] .btn-info,fieldset[disabled] .btn-info.active,fieldset[disabled] .btn-info.focus,fieldset[disabled] .btn-info:active,fieldset[disabled] .btn-info:focus,fieldset[disabled] .btn-info:hover{background-color:#2aabd2;background-image:none}.btn-warning{background-image:-webkit-linear-gradient(top,#f0ad4e 0,#eb9316 100%);background-image:-o-linear-gradient(top,#f0ad4e 0,#eb9316 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f0ad4e),to(#eb9316));background-image:linear-gradient(to bottom,#f0ad4e 0,#eb9316 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffeb9316', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#e38d13}.btn-warning:focus,.btn-warning:hover{background-color:#eb9316;background-position:0 -15px}.btn-warning.active,.btn-warning:active{background-color:#eb9316;border-color:#e38d13}.btn-warning.disabled,.btn-warning.disabled.active,.btn-warning.disabled.focus,.btn-warning.disabled:active,.btn-warning.disabled:focus,.btn-warning.disabled:hover,.btn-warning[disabled],.btn-warning[disabled].active,.btn-warning[disabled].focus,.btn-warning[disabled]:active,.btn-warning[disabled]:focus,.btn-warning[disabled]:hover,fieldset[disabled] .btn-warning,fieldset[disabled] .btn-warning.active,fieldset[disabled] .btn-warning.focus,fieldset[disabled] .btn-warning:active,fieldset[disabled] .btn-warning:focus,fieldset[disabled] .btn-warning:hover{background-color:#eb9316;background-image:none}.btn-danger{background-image:-webkit-linear-gradient(top,#d9534f 0,#c12e2a 100%);background-image:-o-linear-gradient(top,#d9534f 0,#c12e2a 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#d9534f),to(#c12e2a));background-image:linear-gradient(to bottom,#d9534f 0,#c12e2a 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc12e2a', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#b92c28}.btn-danger:focus,.btn-danger:hover{background-color:#c12e2a;background-position:0 -15px}.btn-danger.active,.btn-danger:active{background-color:#c12e2a;border-color:#b92c28}.btn-danger.disabled,.btn-danger.disabled.active,.btn-danger.disabled.focus,.btn-danger.disabled:active,.btn-danger.disabled:focus,.btn-danger.disabled:hover,.btn-danger[disabled],.btn-danger[disabled].active,.btn-danger[disabled].focus,.btn-danger[disabled]:active,.btn-danger[disabled]:focus,.btn-danger[disabled]:hover,fieldset[disabled] .btn-danger,fieldset[disabled] .btn-danger.active,fieldset[disabled] .btn-danger.focus,fieldset[disabled] .btn-danger:active,fieldset[disabled] .btn-danger:focus,fieldset[disabled] .btn-danger:hover{background-color:#c12e2a;background-image:none}.img-thumbnail,.thumbnail{-webkit-box-shadow:0 1px 2px rgba(0,0,0,.075);box-shadow:0 1px 2px rgba(0,0,0,.075)}.dropdown-menu>li>a:focus,.dropdown-menu>li>a:hover{background-color:#e8e8e8;background-image:-webkit-linear-gradient(top,#f5f5f5 0,#e8e8e8 100%);background-image:-o-linear-gradient(top,#f5f5f5 0,#e8e8e8 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f5f5f5),to(#e8e8e8));background-image:linear-gradient(to bottom,#f5f5f5 0,#e8e8e8 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0);background-repeat:repeat-x}.dropdown-menu>.active>a,.dropdown-menu>.active>a:focus,.dropdown-menu>.active>a:hover{background-color:#2e6da4;background-image:-webkit-linear-gradient(top,#337ab7 0,#2e6da4 100%);background-image:-o-linear-gradient(top,#337ab7 0,#2e6da4 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#337ab7),to(#2e6da4));background-image:linear-gradient(to bottom,#337ab7 0,#2e6da4 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);background-repeat:repeat-x}.navbar-default{background-image:-webkit-linear-gradient(top,#fff 0,#f8f8f8 100%);background-image:-o-linear-gradient(top,#fff 0,#f8f8f8 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#fff),to(#f8f8f8));background-image:linear-gradient(to bottom,#fff 0,#f8f8f8 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#fff8f8f8', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-radius:4px;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.15),0 1px 5px rgba(0,0,0,.075);box-shadow:inset 0 1px 0 rgba(255,255,255,.15),0 1px 5px rgba(0,0,0,.075)}.navbar-default .navbar-nav>.active>a,.navbar-default .navbar-nav>.open>a{background-image:-webkit-linear-gradient(top,#dbdbdb 0,#e2e2e2 100%);background-image:-o-linear-gradient(top,#dbdbdb 0,#e2e2e2 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#dbdbdb),to(#e2e2e2));background-image:linear-gradient(to bottom,#dbdbdb 0,#e2e2e2 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdbdbdb', endColorstr='#ffe2e2e2', GradientType=0);background-repeat:repeat-x;-webkit-box-shadow:inset 0 3px 9px rgba(0,0,0,.075);box-shadow:inset 0 3px 9px rgba(0,0,0,.075)}.navbar-brand,.navbar-nav>li>a{text-shadow:0 1px 0 rgba(255,255,255,.25)}.navbar-inverse{background-image:-webkit-linear-gradient(top,#3c3c3c 0,#222 100%);background-image:-o-linear-gradient(top,#3c3c3c 0,#222 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#3c3c3c),to(#222));background-image:linear-gradient(to bottom,#3c3c3c 0,#222 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff3c3c3c', endColorstr='#ff222222', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-radius:4px}.navbar-inverse .navbar-nav>.active>a,.navbar-inverse .navbar-nav>.open>a{background-image:-webkit-linear-gradient(top,#080808 0,#0f0f0f 100%);background-image:-o-linear-gradient(top,#080808 0,#0f0f0f 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#080808),to(#0f0f0f));background-image:linear-gradient(to bottom,#080808 0,#0f0f0f 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff080808', endColorstr='#ff0f0f0f', GradientType=0);background-repeat:repeat-x;-webkit-box-shadow:inset 0 3px 9px rgba(0,0,0,.25);box-shadow:inset 0 3px 9px rgba(0,0,0,.25)}.navbar-inverse .navbar-brand,.navbar-inverse .navbar-nav>li>a{text-shadow:0 -1px 0 rgba(0,0,0,.25)}.navbar-fixed-bottom,.navbar-fixed-top,.navbar-static-top{border-radius:0}@media (max-width:767px){.navbar .navbar-nav .open .dropdown-menu>.active>a,.navbar .navbar-nav .open .dropdown-menu>.active>a:focus,.navbar .navbar-nav .open .dropdown-menu>.active>a:hover{color:#fff;background-image:-webkit-linear-gradient(top,#337ab7 0,#2e6da4 100%);background-image:-o-linear-gradient(top,#337ab7 0,#2e6da4 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#337ab7),to(#2e6da4));background-image:linear-gradient(to bottom,#337ab7 0,#2e6da4 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);background-repeat:repeat-x}}.alert{text-shadow:0 1px 0 rgba(255,255,255,.2);-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.25),0 1px 2px rgba(0,0,0,.05);box-shadow:inset 0 1px 0 rgba(255,255,255,.25),0 1px 2px rgba(0,0,0,.05)}.alert-success{background-image:-webkit-linear-gradient(top,#dff0d8 0,#c8e5bc 100%);background-image:-o-linear-gradient(top,#dff0d8 0,#c8e5bc 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#dff0d8),to(#c8e5bc));background-image:linear-gradient(to bottom,#dff0d8 0,#c8e5bc 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffc8e5bc', GradientType=0);background-repeat:repeat-x;border-color:#b2dba1}.alert-info{background-image:-webkit-linear-gradient(top,#d9edf7 0,#b9def0 100%);background-image:-o-linear-gradient(top,#d9edf7 0,#b9def0 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#d9edf7),to(#b9def0));background-image:linear-gradient(to bottom,#d9edf7 0,#b9def0 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffb9def0', GradientType=0);background-repeat:repeat-x;border-color:#9acfea}.alert-warning{background-image:-webkit-linear-gradient(top,#fcf8e3 0,#f8efc0 100%);background-image:-o-linear-gradient(top,#fcf8e3 0,#f8efc0 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#fcf8e3),to(#f8efc0));background-image:linear-gradient(to bottom,#fcf8e3 0,#f8efc0 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fff8efc0', GradientType=0);background-repeat:repeat-x;border-color:#f5e79e}.alert-danger{background-image:-webkit-linear-gradient(top,#f2dede 0,#e7c3c3 100%);background-image:-o-linear-gradient(top,#f2dede 0,#e7c3c3 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f2dede),to(#e7c3c3));background-image:linear-gradient(to bottom,#f2dede 0,#e7c3c3 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffe7c3c3', GradientType=0);background-repeat:repeat-x;border-color:#dca7a7}.progress{background-image:-webkit-linear-gradient(top,#ebebeb 0,#f5f5f5 100%);background-image:-o-linear-gradient(top,#ebebeb 0,#f5f5f5 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#ebebeb),to(#f5f5f5));background-image:linear-gradient(to bottom,#ebebeb 0,#f5f5f5 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffebebeb', endColorstr='#fff5f5f5', GradientType=0);background-repeat:repeat-x}.progress-bar{background-image:-webkit-linear-gradient(top,#337ab7 0,#286090 100%);background-image:-o-linear-gradient(top,#337ab7 0,#286090 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#337ab7),to(#286090));background-image:linear-gradient(to bottom,#337ab7 0,#286090 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff286090', GradientType=0);background-repeat:repeat-x}.progress-bar-success{background-image:-webkit-linear-gradient(top,#5cb85c 0,#449d44 100%);background-image:-o-linear-gradient(top,#5cb85c 0,#449d44 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#5cb85c),to(#449d44));background-image:linear-gradient(to bottom,#5cb85c 0,#449d44 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff449d44', GradientType=0);background-repeat:repeat-x}.progress-bar-info{background-image:-webkit-linear-gradient(top,#5bc0de 0,#31b0d5 100%);background-image:-o-linear-gradient(top,#5bc0de 0,#31b0d5 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#5bc0de),to(#31b0d5));background-image:linear-gradient(to bottom,#5bc0de 0,#31b0d5 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff31b0d5', GradientType=0);background-repeat:repeat-x}.progress-bar-warning{background-image:-webkit-linear-gradient(top,#f0ad4e 0,#ec971f 100%);background-image:-o-linear-gradient(top,#f0ad4e 0,#ec971f 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f0ad4e),to(#ec971f));background-image:linear-gradient(to bottom,#f0ad4e 0,#ec971f 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffec971f', GradientType=0);background-repeat:repeat-x}.progress-bar-danger{background-image:-webkit-linear-gradient(top,#d9534f 0,#c9302c 100%);background-image:-o-linear-gradient(top,#d9534f 0,#c9302c 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#d9534f),to(#c9302c));background-image:linear-gradient(to bottom,#d9534f 0,#c9302c 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc9302c', GradientType=0);background-repeat:repeat-x}.progress-bar-striped{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.list-group{border-radius:4px;-webkit-box-shadow:0 1px 2px rgba(0,0,0,.075);box-shadow:0 1px 2px rgba(0,0,0,.075)}.list-group-item.active,.list-group-item.active:focus,.list-group-item.active:hover{text-shadow:0 -1px 0 #286090;background-image:-webkit-linear-gradient(top,#337ab7 0,#2b669a 100%);background-image:-o-linear-gradient(top,#337ab7 0,#2b669a 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#337ab7),to(#2b669a));background-image:linear-gradient(to bottom,#337ab7 0,#2b669a 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2b669a', GradientType=0);background-repeat:repeat-x;border-color:#2b669a}.list-group-item.active .badge,.list-group-item.active:focus .badge,.list-group-item.active:hover .badge{text-shadow:none}.panel{-webkit-box-shadow:0 1px 2px rgba(0,0,0,.05);box-shadow:0 1px 2px rgba(0,0,0,.05)}.panel-default>.panel-heading{background-image:-webkit-linear-gradient(top,#f5f5f5 0,#e8e8e8 100%);background-image:-o-linear-gradient(top,#f5f5f5 0,#e8e8e8 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f5f5f5),to(#e8e8e8));background-image:linear-gradient(to bottom,#f5f5f5 0,#e8e8e8 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0);background-repeat:repeat-x}.panel-primary>.panel-heading{background-image:-webkit-linear-gradient(top,#337ab7 0,#2e6da4 100%);background-image:-o-linear-gradient(top,#337ab7 0,#2e6da4 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#337ab7),to(#2e6da4));background-image:linear-gradient(to bottom,#337ab7 0,#2e6da4 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);background-repeat:repeat-x}.panel-success>.panel-heading{background-image:-webkit-linear-gradient(top,#dff0d8 0,#d0e9c6 100%);background-image:-o-linear-gradient(top,#dff0d8 0,#d0e9c6 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#dff0d8),to(#d0e9c6));background-image:linear-gradient(to bottom,#dff0d8 0,#d0e9c6 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffd0e9c6', GradientType=0);background-repeat:repeat-x}.panel-info>.panel-heading{background-image:-webkit-linear-gradient(top,#d9edf7 0,#c4e3f3 100%);background-image:-o-linear-gradient(top,#d9edf7 0,#c4e3f3 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#d9edf7),to(#c4e3f3));background-image:linear-gradient(to bottom,#d9edf7 0,#c4e3f3 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffc4e3f3', GradientType=0);background-repeat:repeat-x}.panel-warning>.panel-heading{background-image:-webkit-linear-gradient(top,#fcf8e3 0,#faf2cc 100%);background-image:-o-linear-gradient(top,#fcf8e3 0,#faf2cc 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#fcf8e3),to(#faf2cc));background-image:linear-gradient(to bottom,#fcf8e3 0,#faf2cc 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fffaf2cc', GradientType=0);background-repeat:repeat-x}.panel-danger>.panel-heading{background-image:-webkit-linear-gradient(top,#f2dede 0,#ebcccc 100%);background-image:-o-linear-gradient(top,#f2dede 0,#ebcccc 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f2dede),to(#ebcccc));background-image:linear-gradient(to bottom,#f2dede 0,#ebcccc 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffebcccc', GradientType=0);background-repeat:repeat-x}.well{background-image:-webkit-linear-gradient(top,#e8e8e8 0,#f5f5f5 100%);background-image:-o-linear-gradient(top,#e8e8e8 0,#f5f5f5 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#e8e8e8),to(#f5f5f5));background-image:linear-gradient(to bottom,#e8e8e8 0,#f5f5f5 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffe8e8e8', endColorstr='#fff5f5f5', GradientType=0);background-repeat:repeat-x;border-color:#dcdcdc;-webkit-box-shadow:inset 0 1px 3px rgba(0,0,0,.05),0 1px 0 rgba(255,255,255,.1);box-shadow:inset 0 1px 3px rgba(0,0,0,.05),0 1px 0 rgba(255,255,255,.1)} +/*# sourceMappingURL=bootstrap-theme.min.css.map */ \ No newline at end of file diff --git a/OpenAuth.Identity/wwwroot/lib/bootstrap/css/bootstrap-theme.min.css.map b/OpenAuth.Identity/wwwroot/lib/bootstrap/css/bootstrap-theme.min.css.map new file mode 100644 index 0000000..94813e9 --- /dev/null +++ b/OpenAuth.Identity/wwwroot/lib/bootstrap/css/bootstrap-theme.min.css.map @@ -0,0 +1 @@ +{"version":3,"sources":["less/theme.less","less/mixins/vendor-prefixes.less","less/mixins/gradients.less","less/mixins/reset-filter.less"],"names":[],"mappings":";;;;AAmBA,YAAA,aAAA,UAAA,aAAA,aAAA,aAME,YAAA,EAAA,KAAA,EAAA,eC2CA,mBAAA,MAAA,EAAA,IAAA,EAAA,sBAAA,EAAA,IAAA,IAAA,iBACQ,WAAA,MAAA,EAAA,IAAA,EAAA,sBAAA,EAAA,IAAA,IAAA,iBDvCR,mBAAA,mBAAA,oBAAA,oBAAA,iBAAA,iBAAA,oBAAA,oBAAA,oBAAA,oBAAA,oBAAA,oBCsCA,mBAAA,MAAA,EAAA,IAAA,IAAA,iBACQ,WAAA,MAAA,EAAA,IAAA,IAAA,iBDlCR,qBAAA,sBAAA,sBAAA,uBAAA,mBAAA,oBAAA,sBAAA,uBAAA,sBAAA,uBAAA,sBAAA,uBAAA,+BAAA,gCAAA,6BAAA,gCAAA,gCAAA,gCCiCA,mBAAA,KACQ,WAAA,KDlDV,mBAAA,oBAAA,iBAAA,oBAAA,oBAAA,oBAuBI,YAAA,KAyCF,YAAA,YAEE,iBAAA,KAKJ,aErEI,YAAA,EAAA,IAAA,EAAA,KACA,iBAAA,iDACA,iBAAA,4CAAA,iBAAA,qEAEA,iBAAA,+CCnBF,OAAA,+GH4CA,OAAA,0DACA,kBAAA,SAuC2C,aAAA,QAA2B,aAAA,KArCtE,mBAAA,mBAEE,iBAAA,QACA,oBAAA,EAAA,MAGF,oBAAA,oBAEE,iBAAA,QACA,aAAA,QAMA,sBAAA,6BAAA,4BAAA,6BAAA,4BAAA,4BAAA,uBAAA,8BAAA,6BAAA,8BAAA,6BAAA,6BAAA,gCAAA,uCAAA,sCAAA,uCAAA,sCAAA,sCAME,iBAAA,QACA,iBAAA,KAgBN,aEtEI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDAEA,OAAA,+GCnBF,OAAA,0DH4CA,kBAAA,SACA,aAAA,QAEA,mBAAA,mBAEE,iBAAA,QACA,oBAAA,EAAA,MAGF,oBAAA,oBAEE,iBAAA,QACA,aAAA,QAMA,sBAAA,6BAAA,4BAAA,6BAAA,4BAAA,4BAAA,uBAAA,8BAAA,6BAAA,8BAAA,6BAAA,6BAAA,gCAAA,uCAAA,sCAAA,uCAAA,sCAAA,sCAME,iBAAA,QACA,iBAAA,KAiBN,aEvEI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDAEA,OAAA,+GCnBF,OAAA,0DH4CA,kBAAA,SACA,aAAA,QAEA,mBAAA,mBAEE,iBAAA,QACA,oBAAA,EAAA,MAGF,oBAAA,oBAEE,iBAAA,QACA,aAAA,QAMA,sBAAA,6BAAA,4BAAA,6BAAA,4BAAA,4BAAA,uBAAA,8BAAA,6BAAA,8BAAA,6BAAA,6BAAA,gCAAA,uCAAA,sCAAA,uCAAA,sCAAA,sCAME,iBAAA,QACA,iBAAA,KAkBN,UExEI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDAEA,OAAA,+GCnBF,OAAA,0DH4CA,kBAAA,SACA,aAAA,QAEA,gBAAA,gBAEE,iBAAA,QACA,oBAAA,EAAA,MAGF,iBAAA,iBAEE,iBAAA,QACA,aAAA,QAMA,mBAAA,0BAAA,yBAAA,0BAAA,yBAAA,yBAAA,oBAAA,2BAAA,0BAAA,2BAAA,0BAAA,0BAAA,6BAAA,oCAAA,mCAAA,oCAAA,mCAAA,mCAME,iBAAA,QACA,iBAAA,KAmBN,aEzEI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDAEA,OAAA,+GCnBF,OAAA,0DH4CA,kBAAA,SACA,aAAA,QAEA,mBAAA,mBAEE,iBAAA,QACA,oBAAA,EAAA,MAGF,oBAAA,oBAEE,iBAAA,QACA,aAAA,QAMA,sBAAA,6BAAA,4BAAA,6BAAA,4BAAA,4BAAA,uBAAA,8BAAA,6BAAA,8BAAA,6BAAA,6BAAA,gCAAA,uCAAA,sCAAA,uCAAA,sCAAA,sCAME,iBAAA,QACA,iBAAA,KAoBN,YE1EI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDAEA,OAAA,+GCnBF,OAAA,0DH4CA,kBAAA,SACA,aAAA,QAEA,kBAAA,kBAEE,iBAAA,QACA,oBAAA,EAAA,MAGF,mBAAA,mBAEE,iBAAA,QACA,aAAA,QAMA,qBAAA,4BAAA,2BAAA,4BAAA,2BAAA,2BAAA,sBAAA,6BAAA,4BAAA,6BAAA,4BAAA,4BAAA,+BAAA,sCAAA,qCAAA,sCAAA,qCAAA,qCAME,iBAAA,QACA,iBAAA,KA2BN,eAAA,WClCE,mBAAA,EAAA,IAAA,IAAA,iBACQ,WAAA,EAAA,IAAA,IAAA,iBD2CV,0BAAA,0BE3FI,iBAAA,QACA,iBAAA,oDACA,iBAAA,+CAAA,iBAAA,wEACA,iBAAA,kDACA,OAAA,+GF0FF,kBAAA,SAEF,yBAAA,+BAAA,+BEhGI,iBAAA,QACA,iBAAA,oDACA,iBAAA,+CAAA,iBAAA,wEACA,iBAAA,kDACA,OAAA,+GFgGF,kBAAA,SASF,gBE7GI,iBAAA,iDACA,iBAAA,4CACA,iBAAA,qEAAA,iBAAA,+CACA,OAAA,+GACA,OAAA,0DCnBF,kBAAA,SH+HA,cAAA,ICjEA,mBAAA,MAAA,EAAA,IAAA,EAAA,sBAAA,EAAA,IAAA,IAAA,iBACQ,WAAA,MAAA,EAAA,IAAA,EAAA,sBAAA,EAAA,IAAA,IAAA,iBD6DV,sCAAA,oCE7GI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SD2CF,mBAAA,MAAA,EAAA,IAAA,IAAA,iBACQ,WAAA,MAAA,EAAA,IAAA,IAAA,iBD0EV,cAAA,iBAEE,YAAA,EAAA,IAAA,EAAA,sBAIF,gBEhII,iBAAA,iDACA,iBAAA,4CACA,iBAAA,qEAAA,iBAAA,+CACA,OAAA,+GACA,OAAA,0DCnBF,kBAAA,SHkJA,cAAA,IAHF,sCAAA,oCEhII,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SD2CF,mBAAA,MAAA,EAAA,IAAA,IAAA,gBACQ,WAAA,MAAA,EAAA,IAAA,IAAA,gBDgFV,8BAAA,iCAYI,YAAA,EAAA,KAAA,EAAA,gBAKJ,qBAAA,kBAAA,mBAGE,cAAA,EAqBF,yBAfI,mDAAA,yDAAA,yDAGE,MAAA,KE7JF,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,UFqKJ,OACE,YAAA,EAAA,IAAA,EAAA,qBC3HA,mBAAA,MAAA,EAAA,IAAA,EAAA,sBAAA,EAAA,IAAA,IAAA,gBACQ,WAAA,MAAA,EAAA,IAAA,EAAA,sBAAA,EAAA,IAAA,IAAA,gBDsIV,eEtLI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SF8KF,aAAA,QAKF,YEvLI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SF8KF,aAAA,QAMF,eExLI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SF8KF,aAAA,QAOF,cEzLI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SF8KF,aAAA,QAeF,UEjMI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SFuMJ,cE3MI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SFwMJ,sBE5MI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SFyMJ,mBE7MI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SF0MJ,sBE9MI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SF2MJ,qBE/MI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SF+MJ,sBElLI,iBAAA,yKACA,iBAAA,oKACA,iBAAA,iKFyLJ,YACE,cAAA,IC9KA,mBAAA,EAAA,IAAA,IAAA,iBACQ,WAAA,EAAA,IAAA,IAAA,iBDgLV,wBAAA,8BAAA,8BAGE,YAAA,EAAA,KAAA,EAAA,QEnOE,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SFiOF,aAAA,QALF,+BAAA,qCAAA,qCAQI,YAAA,KAUJ,OCnME,mBAAA,EAAA,IAAA,IAAA,gBACQ,WAAA,EAAA,IAAA,IAAA,gBD4MV,8BE5PI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SFyPJ,8BE7PI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SF0PJ,8BE9PI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SF2PJ,2BE/PI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SF4PJ,8BEhQI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SF6PJ,6BEjQI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SFoQJ,MExQI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SFsQF,aAAA,QC3NA,mBAAA,MAAA,EAAA,IAAA,IAAA,gBAAA,EAAA,IAAA,EAAA,qBACQ,WAAA,MAAA,EAAA,IAAA,IAAA,gBAAA,EAAA,IAAA,EAAA","sourcesContent":["/*!\n * Bootstrap v3.3.7 (http://getbootstrap.com)\n * Copyright 2011-2016 Twitter, Inc.\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)\n */\n\n//\n// Load core variables and mixins\n// --------------------------------------------------\n\n@import \"variables.less\";\n@import \"mixins.less\";\n\n\n//\n// Buttons\n// --------------------------------------------------\n\n// Common styles\n.btn-default,\n.btn-primary,\n.btn-success,\n.btn-info,\n.btn-warning,\n.btn-danger {\n text-shadow: 0 -1px 0 rgba(0,0,0,.2);\n @shadow: inset 0 1px 0 rgba(255,255,255,.15), 0 1px 1px rgba(0,0,0,.075);\n .box-shadow(@shadow);\n\n // Reset the shadow\n &:active,\n &.active {\n .box-shadow(inset 0 3px 5px rgba(0,0,0,.125));\n }\n\n &.disabled,\n &[disabled],\n fieldset[disabled] & {\n .box-shadow(none);\n }\n\n .badge {\n text-shadow: none;\n }\n}\n\n// Mixin for generating new styles\n.btn-styles(@btn-color: #555) {\n #gradient > .vertical(@start-color: @btn-color; @end-color: darken(@btn-color, 12%));\n .reset-filter(); // Disable gradients for IE9 because filter bleeds through rounded corners; see https://github.com/twbs/bootstrap/issues/10620\n background-repeat: repeat-x;\n border-color: darken(@btn-color, 14%);\n\n &:hover,\n &:focus {\n background-color: darken(@btn-color, 12%);\n background-position: 0 -15px;\n }\n\n &:active,\n &.active {\n background-color: darken(@btn-color, 12%);\n border-color: darken(@btn-color, 14%);\n }\n\n &.disabled,\n &[disabled],\n fieldset[disabled] & {\n &,\n &:hover,\n &:focus,\n &.focus,\n &:active,\n &.active {\n background-color: darken(@btn-color, 12%);\n background-image: none;\n }\n }\n}\n\n// Common styles\n.btn {\n // Remove the gradient for the pressed/active state\n &:active,\n &.active {\n background-image: none;\n }\n}\n\n// Apply the mixin to the buttons\n.btn-default { .btn-styles(@btn-default-bg); text-shadow: 0 1px 0 #fff; border-color: #ccc; }\n.btn-primary { .btn-styles(@btn-primary-bg); }\n.btn-success { .btn-styles(@btn-success-bg); }\n.btn-info { .btn-styles(@btn-info-bg); }\n.btn-warning { .btn-styles(@btn-warning-bg); }\n.btn-danger { .btn-styles(@btn-danger-bg); }\n\n\n//\n// Images\n// --------------------------------------------------\n\n.thumbnail,\n.img-thumbnail {\n .box-shadow(0 1px 2px rgba(0,0,0,.075));\n}\n\n\n//\n// Dropdowns\n// --------------------------------------------------\n\n.dropdown-menu > li > a:hover,\n.dropdown-menu > li > a:focus {\n #gradient > .vertical(@start-color: @dropdown-link-hover-bg; @end-color: darken(@dropdown-link-hover-bg, 5%));\n background-color: darken(@dropdown-link-hover-bg, 5%);\n}\n.dropdown-menu > .active > a,\n.dropdown-menu > .active > a:hover,\n.dropdown-menu > .active > a:focus {\n #gradient > .vertical(@start-color: @dropdown-link-active-bg; @end-color: darken(@dropdown-link-active-bg, 5%));\n background-color: darken(@dropdown-link-active-bg, 5%);\n}\n\n\n//\n// Navbar\n// --------------------------------------------------\n\n// Default navbar\n.navbar-default {\n #gradient > .vertical(@start-color: lighten(@navbar-default-bg, 10%); @end-color: @navbar-default-bg);\n .reset-filter(); // Remove gradient in IE<10 to fix bug where dropdowns don't get triggered\n border-radius: @navbar-border-radius;\n @shadow: inset 0 1px 0 rgba(255,255,255,.15), 0 1px 5px rgba(0,0,0,.075);\n .box-shadow(@shadow);\n\n .navbar-nav > .open > a,\n .navbar-nav > .active > a {\n #gradient > .vertical(@start-color: darken(@navbar-default-link-active-bg, 5%); @end-color: darken(@navbar-default-link-active-bg, 2%));\n .box-shadow(inset 0 3px 9px rgba(0,0,0,.075));\n }\n}\n.navbar-brand,\n.navbar-nav > li > a {\n text-shadow: 0 1px 0 rgba(255,255,255,.25);\n}\n\n// Inverted navbar\n.navbar-inverse {\n #gradient > .vertical(@start-color: lighten(@navbar-inverse-bg, 10%); @end-color: @navbar-inverse-bg);\n .reset-filter(); // Remove gradient in IE<10 to fix bug where dropdowns don't get triggered; see https://github.com/twbs/bootstrap/issues/10257\n border-radius: @navbar-border-radius;\n .navbar-nav > .open > a,\n .navbar-nav > .active > a {\n #gradient > .vertical(@start-color: @navbar-inverse-link-active-bg; @end-color: lighten(@navbar-inverse-link-active-bg, 2.5%));\n .box-shadow(inset 0 3px 9px rgba(0,0,0,.25));\n }\n\n .navbar-brand,\n .navbar-nav > li > a {\n text-shadow: 0 -1px 0 rgba(0,0,0,.25);\n }\n}\n\n// Undo rounded corners in static and fixed navbars\n.navbar-static-top,\n.navbar-fixed-top,\n.navbar-fixed-bottom {\n border-radius: 0;\n}\n\n// Fix active state of dropdown items in collapsed mode\n@media (max-width: @grid-float-breakpoint-max) {\n .navbar .navbar-nav .open .dropdown-menu > .active > a {\n &,\n &:hover,\n &:focus {\n color: #fff;\n #gradient > .vertical(@start-color: @dropdown-link-active-bg; @end-color: darken(@dropdown-link-active-bg, 5%));\n }\n }\n}\n\n\n//\n// Alerts\n// --------------------------------------------------\n\n// Common styles\n.alert {\n text-shadow: 0 1px 0 rgba(255,255,255,.2);\n @shadow: inset 0 1px 0 rgba(255,255,255,.25), 0 1px 2px rgba(0,0,0,.05);\n .box-shadow(@shadow);\n}\n\n// Mixin for generating new styles\n.alert-styles(@color) {\n #gradient > .vertical(@start-color: @color; @end-color: darken(@color, 7.5%));\n border-color: darken(@color, 15%);\n}\n\n// Apply the mixin to the alerts\n.alert-success { .alert-styles(@alert-success-bg); }\n.alert-info { .alert-styles(@alert-info-bg); }\n.alert-warning { .alert-styles(@alert-warning-bg); }\n.alert-danger { .alert-styles(@alert-danger-bg); }\n\n\n//\n// Progress bars\n// --------------------------------------------------\n\n// Give the progress background some depth\n.progress {\n #gradient > .vertical(@start-color: darken(@progress-bg, 4%); @end-color: @progress-bg)\n}\n\n// Mixin for generating new styles\n.progress-bar-styles(@color) {\n #gradient > .vertical(@start-color: @color; @end-color: darken(@color, 10%));\n}\n\n// Apply the mixin to the progress bars\n.progress-bar { .progress-bar-styles(@progress-bar-bg); }\n.progress-bar-success { .progress-bar-styles(@progress-bar-success-bg); }\n.progress-bar-info { .progress-bar-styles(@progress-bar-info-bg); }\n.progress-bar-warning { .progress-bar-styles(@progress-bar-warning-bg); }\n.progress-bar-danger { .progress-bar-styles(@progress-bar-danger-bg); }\n\n// Reset the striped class because our mixins don't do multiple gradients and\n// the above custom styles override the new `.progress-bar-striped` in v3.2.0.\n.progress-bar-striped {\n #gradient > .striped();\n}\n\n\n//\n// List groups\n// --------------------------------------------------\n\n.list-group {\n border-radius: @border-radius-base;\n .box-shadow(0 1px 2px rgba(0,0,0,.075));\n}\n.list-group-item.active,\n.list-group-item.active:hover,\n.list-group-item.active:focus {\n text-shadow: 0 -1px 0 darken(@list-group-active-bg, 10%);\n #gradient > .vertical(@start-color: @list-group-active-bg; @end-color: darken(@list-group-active-bg, 7.5%));\n border-color: darken(@list-group-active-border, 7.5%);\n\n .badge {\n text-shadow: none;\n }\n}\n\n\n//\n// Panels\n// --------------------------------------------------\n\n// Common styles\n.panel {\n .box-shadow(0 1px 2px rgba(0,0,0,.05));\n}\n\n// Mixin for generating new styles\n.panel-heading-styles(@color) {\n #gradient > .vertical(@start-color: @color; @end-color: darken(@color, 5%));\n}\n\n// Apply the mixin to the panel headings only\n.panel-default > .panel-heading { .panel-heading-styles(@panel-default-heading-bg); }\n.panel-primary > .panel-heading { .panel-heading-styles(@panel-primary-heading-bg); }\n.panel-success > .panel-heading { .panel-heading-styles(@panel-success-heading-bg); }\n.panel-info > .panel-heading { .panel-heading-styles(@panel-info-heading-bg); }\n.panel-warning > .panel-heading { .panel-heading-styles(@panel-warning-heading-bg); }\n.panel-danger > .panel-heading { .panel-heading-styles(@panel-danger-heading-bg); }\n\n\n//\n// Wells\n// --------------------------------------------------\n\n.well {\n #gradient > .vertical(@start-color: darken(@well-bg, 5%); @end-color: @well-bg);\n border-color: darken(@well-bg, 10%);\n @shadow: inset 0 1px 3px rgba(0,0,0,.05), 0 1px 0 rgba(255,255,255,.1);\n .box-shadow(@shadow);\n}\n","// Vendor Prefixes\n//\n// All vendor mixins are deprecated as of v3.2.0 due to the introduction of\n// Autoprefixer in our Gruntfile. They have been removed in v4.\n\n// - Animations\n// - Backface visibility\n// - Box shadow\n// - Box sizing\n// - Content columns\n// - Hyphens\n// - Placeholder text\n// - Transformations\n// - Transitions\n// - User Select\n\n\n// Animations\n.animation(@animation) {\n -webkit-animation: @animation;\n -o-animation: @animation;\n animation: @animation;\n}\n.animation-name(@name) {\n -webkit-animation-name: @name;\n animation-name: @name;\n}\n.animation-duration(@duration) {\n -webkit-animation-duration: @duration;\n animation-duration: @duration;\n}\n.animation-timing-function(@timing-function) {\n -webkit-animation-timing-function: @timing-function;\n animation-timing-function: @timing-function;\n}\n.animation-delay(@delay) {\n -webkit-animation-delay: @delay;\n animation-delay: @delay;\n}\n.animation-iteration-count(@iteration-count) {\n -webkit-animation-iteration-count: @iteration-count;\n animation-iteration-count: @iteration-count;\n}\n.animation-direction(@direction) {\n -webkit-animation-direction: @direction;\n animation-direction: @direction;\n}\n.animation-fill-mode(@fill-mode) {\n -webkit-animation-fill-mode: @fill-mode;\n animation-fill-mode: @fill-mode;\n}\n\n// Backface visibility\n// Prevent browsers from flickering when using CSS 3D transforms.\n// Default value is `visible`, but can be changed to `hidden`\n\n.backface-visibility(@visibility) {\n -webkit-backface-visibility: @visibility;\n -moz-backface-visibility: @visibility;\n backface-visibility: @visibility;\n}\n\n// Drop shadows\n//\n// Note: Deprecated `.box-shadow()` as of v3.1.0 since all of Bootstrap's\n// supported browsers that have box shadow capabilities now support it.\n\n.box-shadow(@shadow) {\n -webkit-box-shadow: @shadow; // iOS <4.3 & Android <4.1\n box-shadow: @shadow;\n}\n\n// Box sizing\n.box-sizing(@boxmodel) {\n -webkit-box-sizing: @boxmodel;\n -moz-box-sizing: @boxmodel;\n box-sizing: @boxmodel;\n}\n\n// CSS3 Content Columns\n.content-columns(@column-count; @column-gap: @grid-gutter-width) {\n -webkit-column-count: @column-count;\n -moz-column-count: @column-count;\n column-count: @column-count;\n -webkit-column-gap: @column-gap;\n -moz-column-gap: @column-gap;\n column-gap: @column-gap;\n}\n\n// Optional hyphenation\n.hyphens(@mode: auto) {\n word-wrap: break-word;\n -webkit-hyphens: @mode;\n -moz-hyphens: @mode;\n -ms-hyphens: @mode; // IE10+\n -o-hyphens: @mode;\n hyphens: @mode;\n}\n\n// Placeholder text\n.placeholder(@color: @input-color-placeholder) {\n // Firefox\n &::-moz-placeholder {\n color: @color;\n opacity: 1; // Override Firefox's unusual default opacity; see https://github.com/twbs/bootstrap/pull/11526\n }\n &:-ms-input-placeholder { color: @color; } // Internet Explorer 10+\n &::-webkit-input-placeholder { color: @color; } // Safari and Chrome\n}\n\n// Transformations\n.scale(@ratio) {\n -webkit-transform: scale(@ratio);\n -ms-transform: scale(@ratio); // IE9 only\n -o-transform: scale(@ratio);\n transform: scale(@ratio);\n}\n.scale(@ratioX; @ratioY) {\n -webkit-transform: scale(@ratioX, @ratioY);\n -ms-transform: scale(@ratioX, @ratioY); // IE9 only\n -o-transform: scale(@ratioX, @ratioY);\n transform: scale(@ratioX, @ratioY);\n}\n.scaleX(@ratio) {\n -webkit-transform: scaleX(@ratio);\n -ms-transform: scaleX(@ratio); // IE9 only\n -o-transform: scaleX(@ratio);\n transform: scaleX(@ratio);\n}\n.scaleY(@ratio) {\n -webkit-transform: scaleY(@ratio);\n -ms-transform: scaleY(@ratio); // IE9 only\n -o-transform: scaleY(@ratio);\n transform: scaleY(@ratio);\n}\n.skew(@x; @y) {\n -webkit-transform: skewX(@x) skewY(@y);\n -ms-transform: skewX(@x) skewY(@y); // See https://github.com/twbs/bootstrap/issues/4885; IE9+\n -o-transform: skewX(@x) skewY(@y);\n transform: skewX(@x) skewY(@y);\n}\n.translate(@x; @y) {\n -webkit-transform: translate(@x, @y);\n -ms-transform: translate(@x, @y); // IE9 only\n -o-transform: translate(@x, @y);\n transform: translate(@x, @y);\n}\n.translate3d(@x; @y; @z) {\n -webkit-transform: translate3d(@x, @y, @z);\n transform: translate3d(@x, @y, @z);\n}\n.rotate(@degrees) {\n -webkit-transform: rotate(@degrees);\n -ms-transform: rotate(@degrees); // IE9 only\n -o-transform: rotate(@degrees);\n transform: rotate(@degrees);\n}\n.rotateX(@degrees) {\n -webkit-transform: rotateX(@degrees);\n -ms-transform: rotateX(@degrees); // IE9 only\n -o-transform: rotateX(@degrees);\n transform: rotateX(@degrees);\n}\n.rotateY(@degrees) {\n -webkit-transform: rotateY(@degrees);\n -ms-transform: rotateY(@degrees); // IE9 only\n -o-transform: rotateY(@degrees);\n transform: rotateY(@degrees);\n}\n.perspective(@perspective) {\n -webkit-perspective: @perspective;\n -moz-perspective: @perspective;\n perspective: @perspective;\n}\n.perspective-origin(@perspective) {\n -webkit-perspective-origin: @perspective;\n -moz-perspective-origin: @perspective;\n perspective-origin: @perspective;\n}\n.transform-origin(@origin) {\n -webkit-transform-origin: @origin;\n -moz-transform-origin: @origin;\n -ms-transform-origin: @origin; // IE9 only\n transform-origin: @origin;\n}\n\n\n// Transitions\n\n.transition(@transition) {\n -webkit-transition: @transition;\n -o-transition: @transition;\n transition: @transition;\n}\n.transition-property(@transition-property) {\n -webkit-transition-property: @transition-property;\n transition-property: @transition-property;\n}\n.transition-delay(@transition-delay) {\n -webkit-transition-delay: @transition-delay;\n transition-delay: @transition-delay;\n}\n.transition-duration(@transition-duration) {\n -webkit-transition-duration: @transition-duration;\n transition-duration: @transition-duration;\n}\n.transition-timing-function(@timing-function) {\n -webkit-transition-timing-function: @timing-function;\n transition-timing-function: @timing-function;\n}\n.transition-transform(@transition) {\n -webkit-transition: -webkit-transform @transition;\n -moz-transition: -moz-transform @transition;\n -o-transition: -o-transform @transition;\n transition: transform @transition;\n}\n\n\n// User select\n// For selecting text on the page\n\n.user-select(@select) {\n -webkit-user-select: @select;\n -moz-user-select: @select;\n -ms-user-select: @select; // IE10+\n user-select: @select;\n}\n","// Gradients\n\n#gradient {\n\n // Horizontal gradient, from left to right\n //\n // Creates two color stops, start and end, by specifying a color and position for each color stop.\n // Color stops are not available in IE9 and below.\n .horizontal(@start-color: #555; @end-color: #333; @start-percent: 0%; @end-percent: 100%) {\n background-image: -webkit-linear-gradient(left, @start-color @start-percent, @end-color @end-percent); // Safari 5.1-6, Chrome 10+\n background-image: -o-linear-gradient(left, @start-color @start-percent, @end-color @end-percent); // Opera 12\n background-image: linear-gradient(to right, @start-color @start-percent, @end-color @end-percent); // Standard, IE10, Firefox 16+, Opera 12.10+, Safari 7+, Chrome 26+\n background-repeat: repeat-x;\n filter: e(%(\"progid:DXImageTransform.Microsoft.gradient(startColorstr='%d', endColorstr='%d', GradientType=1)\",argb(@start-color),argb(@end-color))); // IE9 and down\n }\n\n // Vertical gradient, from top to bottom\n //\n // Creates two color stops, start and end, by specifying a color and position for each color stop.\n // Color stops are not available in IE9 and below.\n .vertical(@start-color: #555; @end-color: #333; @start-percent: 0%; @end-percent: 100%) {\n background-image: -webkit-linear-gradient(top, @start-color @start-percent, @end-color @end-percent); // Safari 5.1-6, Chrome 10+\n background-image: -o-linear-gradient(top, @start-color @start-percent, @end-color @end-percent); // Opera 12\n background-image: linear-gradient(to bottom, @start-color @start-percent, @end-color @end-percent); // Standard, IE10, Firefox 16+, Opera 12.10+, Safari 7+, Chrome 26+\n background-repeat: repeat-x;\n filter: e(%(\"progid:DXImageTransform.Microsoft.gradient(startColorstr='%d', endColorstr='%d', GradientType=0)\",argb(@start-color),argb(@end-color))); // IE9 and down\n }\n\n .directional(@start-color: #555; @end-color: #333; @deg: 45deg) {\n background-repeat: repeat-x;\n background-image: -webkit-linear-gradient(@deg, @start-color, @end-color); // Safari 5.1-6, Chrome 10+\n background-image: -o-linear-gradient(@deg, @start-color, @end-color); // Opera 12\n background-image: linear-gradient(@deg, @start-color, @end-color); // Standard, IE10, Firefox 16+, Opera 12.10+, Safari 7+, Chrome 26+\n }\n .horizontal-three-colors(@start-color: #00b3ee; @mid-color: #7a43b6; @color-stop: 50%; @end-color: #c3325f) {\n background-image: -webkit-linear-gradient(left, @start-color, @mid-color @color-stop, @end-color);\n background-image: -o-linear-gradient(left, @start-color, @mid-color @color-stop, @end-color);\n background-image: linear-gradient(to right, @start-color, @mid-color @color-stop, @end-color);\n background-repeat: no-repeat;\n filter: e(%(\"progid:DXImageTransform.Microsoft.gradient(startColorstr='%d', endColorstr='%d', GradientType=1)\",argb(@start-color),argb(@end-color))); // IE9 and down, gets no color-stop at all for proper fallback\n }\n .vertical-three-colors(@start-color: #00b3ee; @mid-color: #7a43b6; @color-stop: 50%; @end-color: #c3325f) {\n background-image: -webkit-linear-gradient(@start-color, @mid-color @color-stop, @end-color);\n background-image: -o-linear-gradient(@start-color, @mid-color @color-stop, @end-color);\n background-image: linear-gradient(@start-color, @mid-color @color-stop, @end-color);\n background-repeat: no-repeat;\n filter: e(%(\"progid:DXImageTransform.Microsoft.gradient(startColorstr='%d', endColorstr='%d', GradientType=0)\",argb(@start-color),argb(@end-color))); // IE9 and down, gets no color-stop at all for proper fallback\n }\n .radial(@inner-color: #555; @outer-color: #333) {\n background-image: -webkit-radial-gradient(circle, @inner-color, @outer-color);\n background-image: radial-gradient(circle, @inner-color, @outer-color);\n background-repeat: no-repeat;\n }\n .striped(@color: rgba(255,255,255,.15); @angle: 45deg) {\n background-image: -webkit-linear-gradient(@angle, @color 25%, transparent 25%, transparent 50%, @color 50%, @color 75%, transparent 75%, transparent);\n background-image: -o-linear-gradient(@angle, @color 25%, transparent 25%, transparent 50%, @color 50%, @color 75%, transparent 75%, transparent);\n background-image: linear-gradient(@angle, @color 25%, transparent 25%, transparent 50%, @color 50%, @color 75%, transparent 75%, transparent);\n }\n}\n","// Reset filters for IE\n//\n// When you need to remove a gradient background, do not forget to use this to reset\n// the IE filter for IE9 and below.\n\n.reset-filter() {\n filter: e(%(\"progid:DXImageTransform.Microsoft.gradient(enabled = false)\"));\n}\n"]} \ No newline at end of file diff --git a/OpenAuth.Identity/wwwroot/lib/bootstrap/css/bootstrap.css b/OpenAuth.Identity/wwwroot/lib/bootstrap/css/bootstrap.css new file mode 100644 index 0000000..680e768 --- /dev/null +++ b/OpenAuth.Identity/wwwroot/lib/bootstrap/css/bootstrap.css @@ -0,0 +1,6800 @@ +/*! + * Bootstrap v3.3.5 (http://getbootstrap.com) + * Copyright 2011-2015 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + */ +/*! normalize.css v3.0.3 | MIT License | github.com/necolas/normalize.css */ +html { + font-family: sans-serif; + -webkit-text-size-adjust: 100%; + -ms-text-size-adjust: 100%; +} +body { + margin: 0; +} +article, +aside, +details, +figcaption, +figure, +footer, +header, +hgroup, +main, +menu, +nav, +section, +summary { + display: block; +} +audio, +canvas, +progress, +video { + display: inline-block; + vertical-align: baseline; +} +audio:not([controls]) { + display: none; + height: 0; +} +[hidden], +template { + display: none; +} +a { + background-color: transparent; +} +a:active, +a:hover { + outline: 0; +} +abbr[title] { + border-bottom: 1px dotted; +} +b, +strong { + font-weight: bold; +} +dfn { + font-style: italic; +} +h1 { + margin: .67em 0; + font-size: 2em; +} +mark { + color: #000; + background: #ff0; +} +small { + font-size: 80%; +} +sub, +sup { + position: relative; + font-size: 75%; + line-height: 0; + vertical-align: baseline; +} +sup { + top: -.5em; +} +sub { + bottom: -.25em; +} +img { + border: 0; +} +svg:not(:root) { + overflow: hidden; +} +figure { + margin: 1em 40px; +} +hr { + height: 0; + -webkit-box-sizing: content-box; + -moz-box-sizing: content-box; + box-sizing: content-box; +} +pre { + overflow: auto; +} +code, +kbd, +pre, +samp { + font-family: monospace, monospace; + font-size: 1em; +} +button, +input, +optgroup, +select, +textarea { + margin: 0; + font: inherit; + color: inherit; +} +button { + overflow: visible; +} +button, +select { + text-transform: none; +} +button, +html input[type="button"], +input[type="reset"], +input[type="submit"] { + -webkit-appearance: button; + cursor: pointer; +} +button[disabled], +html input[disabled] { + cursor: default; +} +button::-moz-focus-inner, +input::-moz-focus-inner { + padding: 0; + border: 0; +} +input { + line-height: normal; +} +input[type="checkbox"], +input[type="radio"] { + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + padding: 0; +} +input[type="number"]::-webkit-inner-spin-button, +input[type="number"]::-webkit-outer-spin-button { + height: auto; +} +input[type="search"] { + -webkit-box-sizing: content-box; + -moz-box-sizing: content-box; + box-sizing: content-box; + -webkit-appearance: textfield; +} +input[type="search"]::-webkit-search-cancel-button, +input[type="search"]::-webkit-search-decoration { + -webkit-appearance: none; +} +fieldset { + padding: .35em .625em .75em; + margin: 0 2px; + border: 1px solid #c0c0c0; +} +legend { + padding: 0; + border: 0; +} +textarea { + overflow: auto; +} +optgroup { + font-weight: bold; +} +table { + border-spacing: 0; + border-collapse: collapse; +} +td, +th { + padding: 0; +} +/*! Source: https://github.com/h5bp/html5-boilerplate/blob/master/src/css/main.css */ +@media print { + *, + *:before, + *:after { + color: #000 !important; + text-shadow: none !important; + background: transparent !important; + -webkit-box-shadow: none !important; + box-shadow: none !important; + } + a, + a:visited { + text-decoration: underline; + } + a[href]:after { + content: " (" attr(href) ")"; + } + abbr[title]:after { + content: " (" attr(title) ")"; + } + a[href^="#"]:after, + a[href^="javascript:"]:after { + content: ""; + } + pre, + blockquote { + border: 1px solid #999; + + page-break-inside: avoid; + } + thead { + display: table-header-group; + } + tr, + img { + page-break-inside: avoid; + } + img { + max-width: 100% !important; + } + p, + h2, + h3 { + orphans: 3; + widows: 3; + } + h2, + h3 { + page-break-after: avoid; + } + .navbar { + display: none; + } + .btn > .caret, + .dropup > .btn > .caret { + border-top-color: #000 !important; + } + .label { + border: 1px solid #000; + } + .table { + border-collapse: collapse !important; + } + .table td, + .table th { + background-color: #fff !important; + } + .table-bordered th, + .table-bordered td { + border: 1px solid #ddd !important; + } +} +@font-face { + font-family: 'Glyphicons Halflings'; + + src: url('../fonts/glyphicons-halflings-regular.eot'); + src: url('../fonts/glyphicons-halflings-regular.eot?#iefix') format('embedded-opentype'), url('../fonts/glyphicons-halflings-regular.woff2') format('woff2'), url('../fonts/glyphicons-halflings-regular.woff') format('woff'), url('../fonts/glyphicons-halflings-regular.ttf') format('truetype'), url('../fonts/glyphicons-halflings-regular.svg#glyphicons_halflingsregular') format('svg'); +} +.glyphicon { + position: relative; + top: 1px; + display: inline-block; + font-family: 'Glyphicons Halflings'; + font-style: normal; + font-weight: normal; + line-height: 1; + + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} +.glyphicon-asterisk:before { + content: "\2a"; +} +.glyphicon-plus:before { + content: "\2b"; +} +.glyphicon-euro:before, +.glyphicon-eur:before { + content: "\20ac"; +} +.glyphicon-minus:before { + content: "\2212"; +} +.glyphicon-cloud:before { + content: "\2601"; +} +.glyphicon-envelope:before { + content: "\2709"; +} +.glyphicon-pencil:before { + content: "\270f"; +} +.glyphicon-glass:before { + content: "\e001"; +} +.glyphicon-music:before { + content: "\e002"; +} +.glyphicon-search:before { + content: "\e003"; +} +.glyphicon-heart:before { + content: "\e005"; +} +.glyphicon-star:before { + content: "\e006"; +} +.glyphicon-star-empty:before { + content: "\e007"; +} +.glyphicon-user:before { + content: "\e008"; +} +.glyphicon-film:before { + content: "\e009"; +} +.glyphicon-th-large:before { + content: "\e010"; +} +.glyphicon-th:before { + content: "\e011"; +} +.glyphicon-th-list:before { + content: "\e012"; +} +.glyphicon-ok:before { + content: "\e013"; +} +.glyphicon-remove:before { + content: "\e014"; +} +.glyphicon-zoom-in:before { + content: "\e015"; +} +.glyphicon-zoom-out:before { + content: "\e016"; +} +.glyphicon-off:before { + content: "\e017"; +} +.glyphicon-signal:before { + content: "\e018"; +} +.glyphicon-cog:before { + content: "\e019"; +} +.glyphicon-trash:before { + content: "\e020"; +} +.glyphicon-home:before { + content: "\e021"; +} +.glyphicon-file:before { + content: "\e022"; +} +.glyphicon-time:before { + content: "\e023"; +} +.glyphicon-road:before { + content: "\e024"; +} +.glyphicon-download-alt:before { + content: "\e025"; +} +.glyphicon-download:before { + content: "\e026"; +} +.glyphicon-upload:before { + content: "\e027"; +} +.glyphicon-inbox:before { + content: "\e028"; +} +.glyphicon-play-circle:before { + content: "\e029"; +} +.glyphicon-repeat:before { + content: "\e030"; +} +.glyphicon-refresh:before { + content: "\e031"; +} +.glyphicon-list-alt:before { + content: "\e032"; +} +.glyphicon-lock:before { + content: "\e033"; +} +.glyphicon-flag:before { + content: "\e034"; +} +.glyphicon-headphones:before { + content: "\e035"; +} +.glyphicon-volume-off:before { + content: "\e036"; +} +.glyphicon-volume-down:before { + content: "\e037"; +} +.glyphicon-volume-up:before { + content: "\e038"; +} +.glyphicon-qrcode:before { + content: "\e039"; +} +.glyphicon-barcode:before { + content: "\e040"; +} +.glyphicon-tag:before { + content: "\e041"; +} +.glyphicon-tags:before { + content: "\e042"; +} +.glyphicon-book:before { + content: "\e043"; +} +.glyphicon-bookmark:before { + content: "\e044"; +} +.glyphicon-print:before { + content: "\e045"; +} +.glyphicon-camera:before { + content: "\e046"; +} +.glyphicon-font:before { + content: "\e047"; +} +.glyphicon-bold:before { + content: "\e048"; +} +.glyphicon-italic:before { + content: "\e049"; +} +.glyphicon-text-height:before { + content: "\e050"; +} +.glyphicon-text-width:before { + content: "\e051"; +} +.glyphicon-align-left:before { + content: "\e052"; +} +.glyphicon-align-center:before { + content: "\e053"; +} +.glyphicon-align-right:before { + content: "\e054"; +} +.glyphicon-align-justify:before { + content: "\e055"; +} +.glyphicon-list:before { + content: "\e056"; +} +.glyphicon-indent-left:before { + content: "\e057"; +} +.glyphicon-indent-right:before { + content: "\e058"; +} +.glyphicon-facetime-video:before { + content: "\e059"; +} +.glyphicon-picture:before { + content: "\e060"; +} +.glyphicon-map-marker:before { + content: "\e062"; +} +.glyphicon-adjust:before { + content: "\e063"; +} +.glyphicon-tint:before { + content: "\e064"; +} +.glyphicon-edit:before { + content: "\e065"; +} +.glyphicon-share:before { + content: "\e066"; +} +.glyphicon-check:before { + content: "\e067"; +} +.glyphicon-move:before { + content: "\e068"; +} +.glyphicon-step-backward:before { + content: "\e069"; +} +.glyphicon-fast-backward:before { + content: "\e070"; +} +.glyphicon-backward:before { + content: "\e071"; +} +.glyphicon-play:before { + content: "\e072"; +} +.glyphicon-pause:before { + content: "\e073"; +} +.glyphicon-stop:before { + content: "\e074"; +} +.glyphicon-forward:before { + content: "\e075"; +} +.glyphicon-fast-forward:before { + content: "\e076"; +} +.glyphicon-step-forward:before { + content: "\e077"; +} +.glyphicon-eject:before { + content: "\e078"; +} +.glyphicon-chevron-left:before { + content: "\e079"; +} +.glyphicon-chevron-right:before { + content: "\e080"; +} +.glyphicon-plus-sign:before { + content: "\e081"; +} +.glyphicon-minus-sign:before { + content: "\e082"; +} +.glyphicon-remove-sign:before { + content: "\e083"; +} +.glyphicon-ok-sign:before { + content: "\e084"; +} +.glyphicon-question-sign:before { + content: "\e085"; +} +.glyphicon-info-sign:before { + content: "\e086"; +} +.glyphicon-screenshot:before { + content: "\e087"; +} +.glyphicon-remove-circle:before { + content: "\e088"; +} +.glyphicon-ok-circle:before { + content: "\e089"; +} +.glyphicon-ban-circle:before { + content: "\e090"; +} +.glyphicon-arrow-left:before { + content: "\e091"; +} +.glyphicon-arrow-right:before { + content: "\e092"; +} +.glyphicon-arrow-up:before { + content: "\e093"; +} +.glyphicon-arrow-down:before { + content: "\e094"; +} +.glyphicon-share-alt:before { + content: "\e095"; +} +.glyphicon-resize-full:before { + content: "\e096"; +} +.glyphicon-resize-small:before { + content: "\e097"; +} +.glyphicon-exclamation-sign:before { + content: "\e101"; +} +.glyphicon-gift:before { + content: "\e102"; +} +.glyphicon-leaf:before { + content: "\e103"; +} +.glyphicon-fire:before { + content: "\e104"; +} +.glyphicon-eye-open:before { + content: "\e105"; +} +.glyphicon-eye-close:before { + content: "\e106"; +} +.glyphicon-warning-sign:before { + content: "\e107"; +} +.glyphicon-plane:before { + content: "\e108"; +} +.glyphicon-calendar:before { + content: "\e109"; +} +.glyphicon-random:before { + content: "\e110"; +} +.glyphicon-comment:before { + content: "\e111"; +} +.glyphicon-magnet:before { + content: "\e112"; +} +.glyphicon-chevron-up:before { + content: "\e113"; +} +.glyphicon-chevron-down:before { + content: "\e114"; +} +.glyphicon-retweet:before { + content: "\e115"; +} +.glyphicon-shopping-cart:before { + content: "\e116"; +} +.glyphicon-folder-close:before { + content: "\e117"; +} +.glyphicon-folder-open:before { + content: "\e118"; +} +.glyphicon-resize-vertical:before { + content: "\e119"; +} +.glyphicon-resize-horizontal:before { + content: "\e120"; +} +.glyphicon-hdd:before { + content: "\e121"; +} +.glyphicon-bullhorn:before { + content: "\e122"; +} +.glyphicon-bell:before { + content: "\e123"; +} +.glyphicon-certificate:before { + content: "\e124"; +} +.glyphicon-thumbs-up:before { + content: "\e125"; +} +.glyphicon-thumbs-down:before { + content: "\e126"; +} +.glyphicon-hand-right:before { + content: "\e127"; +} +.glyphicon-hand-left:before { + content: "\e128"; +} +.glyphicon-hand-up:before { + content: "\e129"; +} +.glyphicon-hand-down:before { + content: "\e130"; +} +.glyphicon-circle-arrow-right:before { + content: "\e131"; +} +.glyphicon-circle-arrow-left:before { + content: "\e132"; +} +.glyphicon-circle-arrow-up:before { + content: "\e133"; +} +.glyphicon-circle-arrow-down:before { + content: "\e134"; +} +.glyphicon-globe:before { + content: "\e135"; +} +.glyphicon-wrench:before { + content: "\e136"; +} +.glyphicon-tasks:before { + content: "\e137"; +} +.glyphicon-filter:before { + content: "\e138"; +} +.glyphicon-briefcase:before { + content: "\e139"; +} +.glyphicon-fullscreen:before { + content: "\e140"; +} +.glyphicon-dashboard:before { + content: "\e141"; +} +.glyphicon-paperclip:before { + content: "\e142"; +} +.glyphicon-heart-empty:before { + content: "\e143"; +} +.glyphicon-link:before { + content: "\e144"; +} +.glyphicon-phone:before { + content: "\e145"; +} +.glyphicon-pushpin:before { + content: "\e146"; +} +.glyphicon-usd:before { + content: "\e148"; +} +.glyphicon-gbp:before { + content: "\e149"; +} +.glyphicon-sort:before { + content: "\e150"; +} +.glyphicon-sort-by-alphabet:before { + content: "\e151"; +} +.glyphicon-sort-by-alphabet-alt:before { + content: "\e152"; +} +.glyphicon-sort-by-order:before { + content: "\e153"; +} +.glyphicon-sort-by-order-alt:before { + content: "\e154"; +} +.glyphicon-sort-by-attributes:before { + content: "\e155"; +} +.glyphicon-sort-by-attributes-alt:before { + content: "\e156"; +} +.glyphicon-unchecked:before { + content: "\e157"; +} +.glyphicon-expand:before { + content: "\e158"; +} +.glyphicon-collapse-down:before { + content: "\e159"; +} +.glyphicon-collapse-up:before { + content: "\e160"; +} +.glyphicon-log-in:before { + content: "\e161"; +} +.glyphicon-flash:before { + content: "\e162"; +} +.glyphicon-log-out:before { + content: "\e163"; +} +.glyphicon-new-window:before { + content: "\e164"; +} +.glyphicon-record:before { + content: "\e165"; +} +.glyphicon-save:before { + content: "\e166"; +} +.glyphicon-open:before { + content: "\e167"; +} +.glyphicon-saved:before { + content: "\e168"; +} +.glyphicon-import:before { + content: "\e169"; +} +.glyphicon-export:before { + content: "\e170"; +} +.glyphicon-send:before { + content: "\e171"; +} +.glyphicon-floppy-disk:before { + content: "\e172"; +} +.glyphicon-floppy-saved:before { + content: "\e173"; +} +.glyphicon-floppy-remove:before { + content: "\e174"; +} +.glyphicon-floppy-save:before { + content: "\e175"; +} +.glyphicon-floppy-open:before { + content: "\e176"; +} +.glyphicon-credit-card:before { + content: "\e177"; +} +.glyphicon-transfer:before { + content: "\e178"; +} +.glyphicon-cutlery:before { + content: "\e179"; +} +.glyphicon-header:before { + content: "\e180"; +} +.glyphicon-compressed:before { + content: "\e181"; +} +.glyphicon-earphone:before { + content: "\e182"; +} +.glyphicon-phone-alt:before { + content: "\e183"; +} +.glyphicon-tower:before { + content: "\e184"; +} +.glyphicon-stats:before { + content: "\e185"; +} +.glyphicon-sd-video:before { + content: "\e186"; +} +.glyphicon-hd-video:before { + content: "\e187"; +} +.glyphicon-subtitles:before { + content: "\e188"; +} +.glyphicon-sound-stereo:before { + content: "\e189"; +} +.glyphicon-sound-dolby:before { + content: "\e190"; +} +.glyphicon-sound-5-1:before { + content: "\e191"; +} +.glyphicon-sound-6-1:before { + content: "\e192"; +} +.glyphicon-sound-7-1:before { + content: "\e193"; +} +.glyphicon-copyright-mark:before { + content: "\e194"; +} +.glyphicon-registration-mark:before { + content: "\e195"; +} +.glyphicon-cloud-download:before { + content: "\e197"; +} +.glyphicon-cloud-upload:before { + content: "\e198"; +} +.glyphicon-tree-conifer:before { + content: "\e199"; +} +.glyphicon-tree-deciduous:before { + content: "\e200"; +} +.glyphicon-cd:before { + content: "\e201"; +} +.glyphicon-save-file:before { + content: "\e202"; +} +.glyphicon-open-file:before { + content: "\e203"; +} +.glyphicon-level-up:before { + content: "\e204"; +} +.glyphicon-copy:before { + content: "\e205"; +} +.glyphicon-paste:before { + content: "\e206"; +} +.glyphicon-alert:before { + content: "\e209"; +} +.glyphicon-equalizer:before { + content: "\e210"; +} +.glyphicon-king:before { + content: "\e211"; +} +.glyphicon-queen:before { + content: "\e212"; +} +.glyphicon-pawn:before { + content: "\e213"; +} +.glyphicon-bishop:before { + content: "\e214"; +} +.glyphicon-knight:before { + content: "\e215"; +} +.glyphicon-baby-formula:before { + content: "\e216"; +} +.glyphicon-tent:before { + content: "\26fa"; +} +.glyphicon-blackboard:before { + content: "\e218"; +} +.glyphicon-bed:before { + content: "\e219"; +} +.glyphicon-apple:before { + content: "\f8ff"; +} +.glyphicon-erase:before { + content: "\e221"; +} +.glyphicon-hourglass:before { + content: "\231b"; +} +.glyphicon-lamp:before { + content: "\e223"; +} +.glyphicon-duplicate:before { + content: "\e224"; +} +.glyphicon-piggy-bank:before { + content: "\e225"; +} +.glyphicon-scissors:before { + content: "\e226"; +} +.glyphicon-bitcoin:before { + content: "\e227"; +} +.glyphicon-btc:before { + content: "\e227"; +} +.glyphicon-xbt:before { + content: "\e227"; +} +.glyphicon-yen:before { + content: "\00a5"; +} +.glyphicon-jpy:before { + content: "\00a5"; +} +.glyphicon-ruble:before { + content: "\20bd"; +} +.glyphicon-rub:before { + content: "\20bd"; +} +.glyphicon-scale:before { + content: "\e230"; +} +.glyphicon-ice-lolly:before { + content: "\e231"; +} +.glyphicon-ice-lolly-tasted:before { + content: "\e232"; +} +.glyphicon-education:before { + content: "\e233"; +} +.glyphicon-option-horizontal:before { + content: "\e234"; +} +.glyphicon-option-vertical:before { + content: "\e235"; +} +.glyphicon-menu-hamburger:before { + content: "\e236"; +} +.glyphicon-modal-window:before { + content: "\e237"; +} +.glyphicon-oil:before { + content: "\e238"; +} +.glyphicon-grain:before { + content: "\e239"; +} +.glyphicon-sunglasses:before { + content: "\e240"; +} +.glyphicon-text-size:before { + content: "\e241"; +} +.glyphicon-text-color:before { + content: "\e242"; +} +.glyphicon-text-background:before { + content: "\e243"; +} +.glyphicon-object-align-top:before { + content: "\e244"; +} +.glyphicon-object-align-bottom:before { + content: "\e245"; +} +.glyphicon-object-align-horizontal:before { + content: "\e246"; +} +.glyphicon-object-align-left:before { + content: "\e247"; +} +.glyphicon-object-align-vertical:before { + content: "\e248"; +} +.glyphicon-object-align-right:before { + content: "\e249"; +} +.glyphicon-triangle-right:before { + content: "\e250"; +} +.glyphicon-triangle-left:before { + content: "\e251"; +} +.glyphicon-triangle-bottom:before { + content: "\e252"; +} +.glyphicon-triangle-top:before { + content: "\e253"; +} +.glyphicon-console:before { + content: "\e254"; +} +.glyphicon-superscript:before { + content: "\e255"; +} +.glyphicon-subscript:before { + content: "\e256"; +} +.glyphicon-menu-left:before { + content: "\e257"; +} +.glyphicon-menu-right:before { + content: "\e258"; +} +.glyphicon-menu-down:before { + content: "\e259"; +} +.glyphicon-menu-up:before { + content: "\e260"; +} +* { + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; +} +*:before, +*:after { + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; +} +html { + font-size: 10px; + + -webkit-tap-highlight-color: rgba(0, 0, 0, 0); +} +body { + font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; + font-size: 14px; + line-height: 1.42857143; + color: #333; + background-color: #fff; +} +input, +button, +select, +textarea { + font-family: inherit; + font-size: inherit; + line-height: inherit; +} +a { + color: #337ab7; + text-decoration: none; +} +a:hover, +a:focus { + color: #23527c; + text-decoration: underline; +} +a:focus { + outline: thin dotted; + outline: 5px auto -webkit-focus-ring-color; + outline-offset: -2px; +} +figure { + margin: 0; +} +img { + vertical-align: middle; +} +.img-responsive, +.thumbnail > img, +.thumbnail a > img, +.carousel-inner > .item > img, +.carousel-inner > .item > a > img { + display: block; + max-width: 100%; + height: auto; +} +.img-rounded { + border-radius: 6px; +} +.img-thumbnail { + display: inline-block; + max-width: 100%; + height: auto; + padding: 4px; + line-height: 1.42857143; + background-color: #fff; + border: 1px solid #ddd; + border-radius: 4px; + -webkit-transition: all .2s ease-in-out; + -o-transition: all .2s ease-in-out; + transition: all .2s ease-in-out; +} +.img-circle { + border-radius: 50%; +} +hr { + margin-top: 20px; + margin-bottom: 20px; + border: 0; + border-top: 1px solid #eee; +} +.sr-only { + position: absolute; + width: 1px; + height: 1px; + padding: 0; + margin: -1px; + overflow: hidden; + clip: rect(0, 0, 0, 0); + border: 0; +} +.sr-only-focusable:active, +.sr-only-focusable:focus { + position: static; + width: auto; + height: auto; + margin: 0; + overflow: visible; + clip: auto; +} +[role="button"] { + cursor: pointer; +} +h1, +h2, +h3, +h4, +h5, +h6, +.h1, +.h2, +.h3, +.h4, +.h5, +.h6 { + font-family: inherit; + font-weight: 500; + line-height: 1.1; + color: inherit; +} +h1 small, +h2 small, +h3 small, +h4 small, +h5 small, +h6 small, +.h1 small, +.h2 small, +.h3 small, +.h4 small, +.h5 small, +.h6 small, +h1 .small, +h2 .small, +h3 .small, +h4 .small, +h5 .small, +h6 .small, +.h1 .small, +.h2 .small, +.h3 .small, +.h4 .small, +.h5 .small, +.h6 .small { + font-weight: normal; + line-height: 1; + color: #777; +} +h1, +.h1, +h2, +.h2, +h3, +.h3 { + margin-top: 20px; + margin-bottom: 10px; +} +h1 small, +.h1 small, +h2 small, +.h2 small, +h3 small, +.h3 small, +h1 .small, +.h1 .small, +h2 .small, +.h2 .small, +h3 .small, +.h3 .small { + font-size: 65%; +} +h4, +.h4, +h5, +.h5, +h6, +.h6 { + margin-top: 10px; + margin-bottom: 10px; +} +h4 small, +.h4 small, +h5 small, +.h5 small, +h6 small, +.h6 small, +h4 .small, +.h4 .small, +h5 .small, +.h5 .small, +h6 .small, +.h6 .small { + font-size: 75%; +} +h1, +.h1 { + font-size: 36px; +} +h2, +.h2 { + font-size: 30px; +} +h3, +.h3 { + font-size: 24px; +} +h4, +.h4 { + font-size: 18px; +} +h5, +.h5 { + font-size: 14px; +} +h6, +.h6 { + font-size: 12px; +} +p { + margin: 0 0 10px; +} +.lead { + margin-bottom: 20px; + font-size: 16px; + font-weight: 300; + line-height: 1.4; +} +@media (min-width: 768px) { + .lead { + font-size: 21px; + } +} +small, +.small { + font-size: 85%; +} +mark, +.mark { + padding: .2em; + background-color: #fcf8e3; +} +.text-left { + text-align: left; +} +.text-right { + text-align: right; +} +.text-center { + text-align: center; +} +.text-justify { + text-align: justify; +} +.text-nowrap { + white-space: nowrap; +} +.text-lowercase { + text-transform: lowercase; +} +.text-uppercase { + text-transform: uppercase; +} +.text-capitalize { + text-transform: capitalize; +} +.text-muted { + color: #777; +} +.text-primary { + color: #337ab7; +} +a.text-primary:hover, +a.text-primary:focus { + color: #286090; +} +.text-success { + color: #3c763d; +} +a.text-success:hover, +a.text-success:focus { + color: #2b542c; +} +.text-info { + color: #31708f; +} +a.text-info:hover, +a.text-info:focus { + color: #245269; +} +.text-warning { + color: #8a6d3b; +} +a.text-warning:hover, +a.text-warning:focus { + color: #66512c; +} +.text-danger { + color: #a94442; +} +a.text-danger:hover, +a.text-danger:focus { + color: #843534; +} +.bg-primary { + color: #fff; + background-color: #337ab7; +} +a.bg-primary:hover, +a.bg-primary:focus { + background-color: #286090; +} +.bg-success { + background-color: #dff0d8; +} +a.bg-success:hover, +a.bg-success:focus { + background-color: #c1e2b3; +} +.bg-info { + background-color: #d9edf7; +} +a.bg-info:hover, +a.bg-info:focus { + background-color: #afd9ee; +} +.bg-warning { + background-color: #fcf8e3; +} +a.bg-warning:hover, +a.bg-warning:focus { + background-color: #f7ecb5; +} +.bg-danger { + background-color: #f2dede; +} +a.bg-danger:hover, +a.bg-danger:focus { + background-color: #e4b9b9; +} +.page-header { + padding-bottom: 9px; + margin: 40px 0 20px; + border-bottom: 1px solid #eee; +} +ul, +ol { + margin-top: 0; + margin-bottom: 10px; +} +ul ul, +ol ul, +ul ol, +ol ol { + margin-bottom: 0; +} +.list-unstyled { + padding-left: 0; + list-style: none; +} +.list-inline { + padding-left: 0; + margin-left: -5px; + list-style: none; +} +.list-inline > li { + display: inline-block; + padding-right: 5px; + padding-left: 5px; +} +dl { + margin-top: 0; + margin-bottom: 20px; +} +dt, +dd { + line-height: 1.42857143; +} +dt { + font-weight: bold; +} +dd { + margin-left: 0; +} +@media (min-width: 768px) { + .dl-horizontal dt { + float: left; + width: 160px; + overflow: hidden; + clear: left; + text-align: right; + text-overflow: ellipsis; + white-space: nowrap; + } + .dl-horizontal dd { + margin-left: 180px; + } +} +abbr[title], +abbr[data-original-title] { + cursor: help; + border-bottom: 1px dotted #777; +} +.initialism { + font-size: 90%; + text-transform: uppercase; +} +blockquote { + padding: 10px 20px; + margin: 0 0 20px; + font-size: 17.5px; + border-left: 5px solid #eee; +} +blockquote p:last-child, +blockquote ul:last-child, +blockquote ol:last-child { + margin-bottom: 0; +} +blockquote footer, +blockquote small, +blockquote .small { + display: block; + font-size: 80%; + line-height: 1.42857143; + color: #777; +} +blockquote footer:before, +blockquote small:before, +blockquote .small:before { + content: '\2014 \00A0'; +} +.blockquote-reverse, +blockquote.pull-right { + padding-right: 15px; + padding-left: 0; + text-align: right; + border-right: 5px solid #eee; + border-left: 0; +} +.blockquote-reverse footer:before, +blockquote.pull-right footer:before, +.blockquote-reverse small:before, +blockquote.pull-right small:before, +.blockquote-reverse .small:before, +blockquote.pull-right .small:before { + content: ''; +} +.blockquote-reverse footer:after, +blockquote.pull-right footer:after, +.blockquote-reverse small:after, +blockquote.pull-right small:after, +.blockquote-reverse .small:after, +blockquote.pull-right .small:after { + content: '\00A0 \2014'; +} +address { + margin-bottom: 20px; + font-style: normal; + line-height: 1.42857143; +} +code, +kbd, +pre, +samp { + font-family: Menlo, Monaco, Consolas, "Courier New", monospace; +} +code { + padding: 2px 4px; + font-size: 90%; + color: #c7254e; + background-color: #f9f2f4; + border-radius: 4px; +} +kbd { + padding: 2px 4px; + font-size: 90%; + color: #fff; + background-color: #333; + border-radius: 3px; + -webkit-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, .25); + box-shadow: inset 0 -1px 0 rgba(0, 0, 0, .25); +} +kbd kbd { + padding: 0; + font-size: 100%; + font-weight: bold; + -webkit-box-shadow: none; + box-shadow: none; +} +pre { + display: block; + padding: 9.5px; + margin: 0 0 10px; + font-size: 13px; + line-height: 1.42857143; + color: #333; + word-break: break-all; + word-wrap: break-word; + background-color: #f5f5f5; + border: 1px solid #ccc; + border-radius: 4px; +} +pre code { + padding: 0; + font-size: inherit; + color: inherit; + white-space: pre-wrap; + background-color: transparent; + border-radius: 0; +} +.pre-scrollable { + max-height: 340px; + overflow-y: scroll; +} +.container { + padding-right: 15px; + padding-left: 15px; + margin-right: auto; + margin-left: auto; +} +@media (min-width: 768px) { + .container { + width: 750px; + } +} +@media (min-width: 992px) { + .container { + width: 970px; + } +} +@media (min-width: 1200px) { + .container { + width: 1170px; + } +} +.container-fluid { + padding-right: 15px; + padding-left: 15px; + margin-right: auto; + margin-left: auto; +} +.row { + margin-right: -15px; + margin-left: -15px; +} +.col-xs-1, .col-sm-1, .col-md-1, .col-lg-1, .col-xs-2, .col-sm-2, .col-md-2, .col-lg-2, .col-xs-3, .col-sm-3, .col-md-3, .col-lg-3, .col-xs-4, .col-sm-4, .col-md-4, .col-lg-4, .col-xs-5, .col-sm-5, .col-md-5, .col-lg-5, .col-xs-6, .col-sm-6, .col-md-6, .col-lg-6, .col-xs-7, .col-sm-7, .col-md-7, .col-lg-7, .col-xs-8, .col-sm-8, .col-md-8, .col-lg-8, .col-xs-9, .col-sm-9, .col-md-9, .col-lg-9, .col-xs-10, .col-sm-10, .col-md-10, .col-lg-10, .col-xs-11, .col-sm-11, .col-md-11, .col-lg-11, .col-xs-12, .col-sm-12, .col-md-12, .col-lg-12 { + position: relative; + min-height: 1px; + padding-right: 15px; + padding-left: 15px; +} +.col-xs-1, .col-xs-2, .col-xs-3, .col-xs-4, .col-xs-5, .col-xs-6, .col-xs-7, .col-xs-8, .col-xs-9, .col-xs-10, .col-xs-11, .col-xs-12 { + float: left; +} +.col-xs-12 { + width: 100%; +} +.col-xs-11 { + width: 91.66666667%; +} +.col-xs-10 { + width: 83.33333333%; +} +.col-xs-9 { + width: 75%; +} +.col-xs-8 { + width: 66.66666667%; +} +.col-xs-7 { + width: 58.33333333%; +} +.col-xs-6 { + width: 50%; +} +.col-xs-5 { + width: 41.66666667%; +} +.col-xs-4 { + width: 33.33333333%; +} +.col-xs-3 { + width: 25%; +} +.col-xs-2 { + width: 16.66666667%; +} +.col-xs-1 { + width: 8.33333333%; +} +.col-xs-pull-12 { + right: 100%; +} +.col-xs-pull-11 { + right: 91.66666667%; +} +.col-xs-pull-10 { + right: 83.33333333%; +} +.col-xs-pull-9 { + right: 75%; +} +.col-xs-pull-8 { + right: 66.66666667%; +} +.col-xs-pull-7 { + right: 58.33333333%; +} +.col-xs-pull-6 { + right: 50%; +} +.col-xs-pull-5 { + right: 41.66666667%; +} +.col-xs-pull-4 { + right: 33.33333333%; +} +.col-xs-pull-3 { + right: 25%; +} +.col-xs-pull-2 { + right: 16.66666667%; +} +.col-xs-pull-1 { + right: 8.33333333%; +} +.col-xs-pull-0 { + right: auto; +} +.col-xs-push-12 { + left: 100%; +} +.col-xs-push-11 { + left: 91.66666667%; +} +.col-xs-push-10 { + left: 83.33333333%; +} +.col-xs-push-9 { + left: 75%; +} +.col-xs-push-8 { + left: 66.66666667%; +} +.col-xs-push-7 { + left: 58.33333333%; +} +.col-xs-push-6 { + left: 50%; +} +.col-xs-push-5 { + left: 41.66666667%; +} +.col-xs-push-4 { + left: 33.33333333%; +} +.col-xs-push-3 { + left: 25%; +} +.col-xs-push-2 { + left: 16.66666667%; +} +.col-xs-push-1 { + left: 8.33333333%; +} +.col-xs-push-0 { + left: auto; +} +.col-xs-offset-12 { + margin-left: 100%; +} +.col-xs-offset-11 { + margin-left: 91.66666667%; +} +.col-xs-offset-10 { + margin-left: 83.33333333%; +} +.col-xs-offset-9 { + margin-left: 75%; +} +.col-xs-offset-8 { + margin-left: 66.66666667%; +} +.col-xs-offset-7 { + margin-left: 58.33333333%; +} +.col-xs-offset-6 { + margin-left: 50%; +} +.col-xs-offset-5 { + margin-left: 41.66666667%; +} +.col-xs-offset-4 { + margin-left: 33.33333333%; +} +.col-xs-offset-3 { + margin-left: 25%; +} +.col-xs-offset-2 { + margin-left: 16.66666667%; +} +.col-xs-offset-1 { + margin-left: 8.33333333%; +} +.col-xs-offset-0 { + margin-left: 0; +} +@media (min-width: 768px) { + .col-sm-1, .col-sm-2, .col-sm-3, .col-sm-4, .col-sm-5, .col-sm-6, .col-sm-7, .col-sm-8, .col-sm-9, .col-sm-10, .col-sm-11, .col-sm-12 { + float: left; + } + .col-sm-12 { + width: 100%; + } + .col-sm-11 { + width: 91.66666667%; + } + .col-sm-10 { + width: 83.33333333%; + } + .col-sm-9 { + width: 75%; + } + .col-sm-8 { + width: 66.66666667%; + } + .col-sm-7 { + width: 58.33333333%; + } + .col-sm-6 { + width: 50%; + } + .col-sm-5 { + width: 41.66666667%; + } + .col-sm-4 { + width: 33.33333333%; + } + .col-sm-3 { + width: 25%; + } + .col-sm-2 { + width: 16.66666667%; + } + .col-sm-1 { + width: 8.33333333%; + } + .col-sm-pull-12 { + right: 100%; + } + .col-sm-pull-11 { + right: 91.66666667%; + } + .col-sm-pull-10 { + right: 83.33333333%; + } + .col-sm-pull-9 { + right: 75%; + } + .col-sm-pull-8 { + right: 66.66666667%; + } + .col-sm-pull-7 { + right: 58.33333333%; + } + .col-sm-pull-6 { + right: 50%; + } + .col-sm-pull-5 { + right: 41.66666667%; + } + .col-sm-pull-4 { + right: 33.33333333%; + } + .col-sm-pull-3 { + right: 25%; + } + .col-sm-pull-2 { + right: 16.66666667%; + } + .col-sm-pull-1 { + right: 8.33333333%; + } + .col-sm-pull-0 { + right: auto; + } + .col-sm-push-12 { + left: 100%; + } + .col-sm-push-11 { + left: 91.66666667%; + } + .col-sm-push-10 { + left: 83.33333333%; + } + .col-sm-push-9 { + left: 75%; + } + .col-sm-push-8 { + left: 66.66666667%; + } + .col-sm-push-7 { + left: 58.33333333%; + } + .col-sm-push-6 { + left: 50%; + } + .col-sm-push-5 { + left: 41.66666667%; + } + .col-sm-push-4 { + left: 33.33333333%; + } + .col-sm-push-3 { + left: 25%; + } + .col-sm-push-2 { + left: 16.66666667%; + } + .col-sm-push-1 { + left: 8.33333333%; + } + .col-sm-push-0 { + left: auto; + } + .col-sm-offset-12 { + margin-left: 100%; + } + .col-sm-offset-11 { + margin-left: 91.66666667%; + } + .col-sm-offset-10 { + margin-left: 83.33333333%; + } + .col-sm-offset-9 { + margin-left: 75%; + } + .col-sm-offset-8 { + margin-left: 66.66666667%; + } + .col-sm-offset-7 { + margin-left: 58.33333333%; + } + .col-sm-offset-6 { + margin-left: 50%; + } + .col-sm-offset-5 { + margin-left: 41.66666667%; + } + .col-sm-offset-4 { + margin-left: 33.33333333%; + } + .col-sm-offset-3 { + margin-left: 25%; + } + .col-sm-offset-2 { + margin-left: 16.66666667%; + } + .col-sm-offset-1 { + margin-left: 8.33333333%; + } + .col-sm-offset-0 { + margin-left: 0; + } +} +@media (min-width: 992px) { + .col-md-1, .col-md-2, .col-md-3, .col-md-4, .col-md-5, .col-md-6, .col-md-7, .col-md-8, .col-md-9, .col-md-10, .col-md-11, .col-md-12 { + float: left; + } + .col-md-12 { + width: 100%; + } + .col-md-11 { + width: 91.66666667%; + } + .col-md-10 { + width: 83.33333333%; + } + .col-md-9 { + width: 75%; + } + .col-md-8 { + width: 66.66666667%; + } + .col-md-7 { + width: 58.33333333%; + } + .col-md-6 { + width: 50%; + } + .col-md-5 { + width: 41.66666667%; + } + .col-md-4 { + width: 33.33333333%; + } + .col-md-3 { + width: 25%; + } + .col-md-2 { + width: 16.66666667%; + } + .col-md-1 { + width: 8.33333333%; + } + .col-md-pull-12 { + right: 100%; + } + .col-md-pull-11 { + right: 91.66666667%; + } + .col-md-pull-10 { + right: 83.33333333%; + } + .col-md-pull-9 { + right: 75%; + } + .col-md-pull-8 { + right: 66.66666667%; + } + .col-md-pull-7 { + right: 58.33333333%; + } + .col-md-pull-6 { + right: 50%; + } + .col-md-pull-5 { + right: 41.66666667%; + } + .col-md-pull-4 { + right: 33.33333333%; + } + .col-md-pull-3 { + right: 25%; + } + .col-md-pull-2 { + right: 16.66666667%; + } + .col-md-pull-1 { + right: 8.33333333%; + } + .col-md-pull-0 { + right: auto; + } + .col-md-push-12 { + left: 100%; + } + .col-md-push-11 { + left: 91.66666667%; + } + .col-md-push-10 { + left: 83.33333333%; + } + .col-md-push-9 { + left: 75%; + } + .col-md-push-8 { + left: 66.66666667%; + } + .col-md-push-7 { + left: 58.33333333%; + } + .col-md-push-6 { + left: 50%; + } + .col-md-push-5 { + left: 41.66666667%; + } + .col-md-push-4 { + left: 33.33333333%; + } + .col-md-push-3 { + left: 25%; + } + .col-md-push-2 { + left: 16.66666667%; + } + .col-md-push-1 { + left: 8.33333333%; + } + .col-md-push-0 { + left: auto; + } + .col-md-offset-12 { + margin-left: 100%; + } + .col-md-offset-11 { + margin-left: 91.66666667%; + } + .col-md-offset-10 { + margin-left: 83.33333333%; + } + .col-md-offset-9 { + margin-left: 75%; + } + .col-md-offset-8 { + margin-left: 66.66666667%; + } + .col-md-offset-7 { + margin-left: 58.33333333%; + } + .col-md-offset-6 { + margin-left: 50%; + } + .col-md-offset-5 { + margin-left: 41.66666667%; + } + .col-md-offset-4 { + margin-left: 33.33333333%; + } + .col-md-offset-3 { + margin-left: 25%; + } + .col-md-offset-2 { + margin-left: 16.66666667%; + } + .col-md-offset-1 { + margin-left: 8.33333333%; + } + .col-md-offset-0 { + margin-left: 0; + } +} +@media (min-width: 1200px) { + .col-lg-1, .col-lg-2, .col-lg-3, .col-lg-4, .col-lg-5, .col-lg-6, .col-lg-7, .col-lg-8, .col-lg-9, .col-lg-10, .col-lg-11, .col-lg-12 { + float: left; + } + .col-lg-12 { + width: 100%; + } + .col-lg-11 { + width: 91.66666667%; + } + .col-lg-10 { + width: 83.33333333%; + } + .col-lg-9 { + width: 75%; + } + .col-lg-8 { + width: 66.66666667%; + } + .col-lg-7 { + width: 58.33333333%; + } + .col-lg-6 { + width: 50%; + } + .col-lg-5 { + width: 41.66666667%; + } + .col-lg-4 { + width: 33.33333333%; + } + .col-lg-3 { + width: 25%; + } + .col-lg-2 { + width: 16.66666667%; + } + .col-lg-1 { + width: 8.33333333%; + } + .col-lg-pull-12 { + right: 100%; + } + .col-lg-pull-11 { + right: 91.66666667%; + } + .col-lg-pull-10 { + right: 83.33333333%; + } + .col-lg-pull-9 { + right: 75%; + } + .col-lg-pull-8 { + right: 66.66666667%; + } + .col-lg-pull-7 { + right: 58.33333333%; + } + .col-lg-pull-6 { + right: 50%; + } + .col-lg-pull-5 { + right: 41.66666667%; + } + .col-lg-pull-4 { + right: 33.33333333%; + } + .col-lg-pull-3 { + right: 25%; + } + .col-lg-pull-2 { + right: 16.66666667%; + } + .col-lg-pull-1 { + right: 8.33333333%; + } + .col-lg-pull-0 { + right: auto; + } + .col-lg-push-12 { + left: 100%; + } + .col-lg-push-11 { + left: 91.66666667%; + } + .col-lg-push-10 { + left: 83.33333333%; + } + .col-lg-push-9 { + left: 75%; + } + .col-lg-push-8 { + left: 66.66666667%; + } + .col-lg-push-7 { + left: 58.33333333%; + } + .col-lg-push-6 { + left: 50%; + } + .col-lg-push-5 { + left: 41.66666667%; + } + .col-lg-push-4 { + left: 33.33333333%; + } + .col-lg-push-3 { + left: 25%; + } + .col-lg-push-2 { + left: 16.66666667%; + } + .col-lg-push-1 { + left: 8.33333333%; + } + .col-lg-push-0 { + left: auto; + } + .col-lg-offset-12 { + margin-left: 100%; + } + .col-lg-offset-11 { + margin-left: 91.66666667%; + } + .col-lg-offset-10 { + margin-left: 83.33333333%; + } + .col-lg-offset-9 { + margin-left: 75%; + } + .col-lg-offset-8 { + margin-left: 66.66666667%; + } + .col-lg-offset-7 { + margin-left: 58.33333333%; + } + .col-lg-offset-6 { + margin-left: 50%; + } + .col-lg-offset-5 { + margin-left: 41.66666667%; + } + .col-lg-offset-4 { + margin-left: 33.33333333%; + } + .col-lg-offset-3 { + margin-left: 25%; + } + .col-lg-offset-2 { + margin-left: 16.66666667%; + } + .col-lg-offset-1 { + margin-left: 8.33333333%; + } + .col-lg-offset-0 { + margin-left: 0; + } +} +table { + background-color: transparent; +} +caption { + padding-top: 8px; + padding-bottom: 8px; + color: #777; + text-align: left; +} +th { + text-align: left; +} +.table { + width: 100%; + max-width: 100%; + margin-bottom: 20px; +} +.table > thead > tr > th, +.table > tbody > tr > th, +.table > tfoot > tr > th, +.table > thead > tr > td, +.table > tbody > tr > td, +.table > tfoot > tr > td { + padding: 8px; + line-height: 1.42857143; + vertical-align: top; + border-top: 1px solid #ddd; +} +.table > thead > tr > th { + vertical-align: bottom; + border-bottom: 2px solid #ddd; +} +.table > caption + thead > tr:first-child > th, +.table > colgroup + thead > tr:first-child > th, +.table > thead:first-child > tr:first-child > th, +.table > caption + thead > tr:first-child > td, +.table > colgroup + thead > tr:first-child > td, +.table > thead:first-child > tr:first-child > td { + border-top: 0; +} +.table > tbody + tbody { + border-top: 2px solid #ddd; +} +.table .table { + background-color: #fff; +} +.table-condensed > thead > tr > th, +.table-condensed > tbody > tr > th, +.table-condensed > tfoot > tr > th, +.table-condensed > thead > tr > td, +.table-condensed > tbody > tr > td, +.table-condensed > tfoot > tr > td { + padding: 5px; +} +.table-bordered { + border: 1px solid #ddd; +} +.table-bordered > thead > tr > th, +.table-bordered > tbody > tr > th, +.table-bordered > tfoot > tr > th, +.table-bordered > thead > tr > td, +.table-bordered > tbody > tr > td, +.table-bordered > tfoot > tr > td { + border: 1px solid #ddd; +} +.table-bordered > thead > tr > th, +.table-bordered > thead > tr > td { + border-bottom-width: 2px; +} +.table-striped > tbody > tr:nth-of-type(odd) { + background-color: #f9f9f9; +} +.table-hover > tbody > tr:hover { + background-color: #f5f5f5; +} +table col[class*="col-"] { + position: static; + display: table-column; + float: none; +} +table td[class*="col-"], +table th[class*="col-"] { + position: static; + display: table-cell; + float: none; +} +.table > thead > tr > td.active, +.table > tbody > tr > td.active, +.table > tfoot > tr > td.active, +.table > thead > tr > th.active, +.table > tbody > tr > th.active, +.table > tfoot > tr > th.active, +.table > thead > tr.active > td, +.table > tbody > tr.active > td, +.table > tfoot > tr.active > td, +.table > thead > tr.active > th, +.table > tbody > tr.active > th, +.table > tfoot > tr.active > th { + background-color: #f5f5f5; +} +.table-hover > tbody > tr > td.active:hover, +.table-hover > tbody > tr > th.active:hover, +.table-hover > tbody > tr.active:hover > td, +.table-hover > tbody > tr:hover > .active, +.table-hover > tbody > tr.active:hover > th { + background-color: #e8e8e8; +} +.table > thead > tr > td.success, +.table > tbody > tr > td.success, +.table > tfoot > tr > td.success, +.table > thead > tr > th.success, +.table > tbody > tr > th.success, +.table > tfoot > tr > th.success, +.table > thead > tr.success > td, +.table > tbody > tr.success > td, +.table > tfoot > tr.success > td, +.table > thead > tr.success > th, +.table > tbody > tr.success > th, +.table > tfoot > tr.success > th { + background-color: #dff0d8; +} +.table-hover > tbody > tr > td.success:hover, +.table-hover > tbody > tr > th.success:hover, +.table-hover > tbody > tr.success:hover > td, +.table-hover > tbody > tr:hover > .success, +.table-hover > tbody > tr.success:hover > th { + background-color: #d0e9c6; +} +.table > thead > tr > td.info, +.table > tbody > tr > td.info, +.table > tfoot > tr > td.info, +.table > thead > tr > th.info, +.table > tbody > tr > th.info, +.table > tfoot > tr > th.info, +.table > thead > tr.info > td, +.table > tbody > tr.info > td, +.table > tfoot > tr.info > td, +.table > thead > tr.info > th, +.table > tbody > tr.info > th, +.table > tfoot > tr.info > th { + background-color: #d9edf7; +} +.table-hover > tbody > tr > td.info:hover, +.table-hover > tbody > tr > th.info:hover, +.table-hover > tbody > tr.info:hover > td, +.table-hover > tbody > tr:hover > .info, +.table-hover > tbody > tr.info:hover > th { + background-color: #c4e3f3; +} +.table > thead > tr > td.warning, +.table > tbody > tr > td.warning, +.table > tfoot > tr > td.warning, +.table > thead > tr > th.warning, +.table > tbody > tr > th.warning, +.table > tfoot > tr > th.warning, +.table > thead > tr.warning > td, +.table > tbody > tr.warning > td, +.table > tfoot > tr.warning > td, +.table > thead > tr.warning > th, +.table > tbody > tr.warning > th, +.table > tfoot > tr.warning > th { + background-color: #fcf8e3; +} +.table-hover > tbody > tr > td.warning:hover, +.table-hover > tbody > tr > th.warning:hover, +.table-hover > tbody > tr.warning:hover > td, +.table-hover > tbody > tr:hover > .warning, +.table-hover > tbody > tr.warning:hover > th { + background-color: #faf2cc; +} +.table > thead > tr > td.danger, +.table > tbody > tr > td.danger, +.table > tfoot > tr > td.danger, +.table > thead > tr > th.danger, +.table > tbody > tr > th.danger, +.table > tfoot > tr > th.danger, +.table > thead > tr.danger > td, +.table > tbody > tr.danger > td, +.table > tfoot > tr.danger > td, +.table > thead > tr.danger > th, +.table > tbody > tr.danger > th, +.table > tfoot > tr.danger > th { + background-color: #f2dede; +} +.table-hover > tbody > tr > td.danger:hover, +.table-hover > tbody > tr > th.danger:hover, +.table-hover > tbody > tr.danger:hover > td, +.table-hover > tbody > tr:hover > .danger, +.table-hover > tbody > tr.danger:hover > th { + background-color: #ebcccc; +} +.table-responsive { + min-height: .01%; + overflow-x: auto; +} +@media screen and (max-width: 767px) { + .table-responsive { + width: 100%; + margin-bottom: 15px; + overflow-y: hidden; + -ms-overflow-style: -ms-autohiding-scrollbar; + border: 1px solid #ddd; + } + .table-responsive > .table { + margin-bottom: 0; + } + .table-responsive > .table > thead > tr > th, + .table-responsive > .table > tbody > tr > th, + .table-responsive > .table > tfoot > tr > th, + .table-responsive > .table > thead > tr > td, + .table-responsive > .table > tbody > tr > td, + .table-responsive > .table > tfoot > tr > td { + white-space: nowrap; + } + .table-responsive > .table-bordered { + border: 0; + } + .table-responsive > .table-bordered > thead > tr > th:first-child, + .table-responsive > .table-bordered > tbody > tr > th:first-child, + .table-responsive > .table-bordered > tfoot > tr > th:first-child, + .table-responsive > .table-bordered > thead > tr > td:first-child, + .table-responsive > .table-bordered > tbody > tr > td:first-child, + .table-responsive > .table-bordered > tfoot > tr > td:first-child { + border-left: 0; + } + .table-responsive > .table-bordered > thead > tr > th:last-child, + .table-responsive > .table-bordered > tbody > tr > th:last-child, + .table-responsive > .table-bordered > tfoot > tr > th:last-child, + .table-responsive > .table-bordered > thead > tr > td:last-child, + .table-responsive > .table-bordered > tbody > tr > td:last-child, + .table-responsive > .table-bordered > tfoot > tr > td:last-child { + border-right: 0; + } + .table-responsive > .table-bordered > tbody > tr:last-child > th, + .table-responsive > .table-bordered > tfoot > tr:last-child > th, + .table-responsive > .table-bordered > tbody > tr:last-child > td, + .table-responsive > .table-bordered > tfoot > tr:last-child > td { + border-bottom: 0; + } +} +fieldset { + min-width: 0; + padding: 0; + margin: 0; + border: 0; +} +legend { + display: block; + width: 100%; + padding: 0; + margin-bottom: 20px; + font-size: 21px; + line-height: inherit; + color: #333; + border: 0; + border-bottom: 1px solid #e5e5e5; +} +label { + display: inline-block; + max-width: 100%; + margin-bottom: 5px; + font-weight: bold; +} +input[type="search"] { + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; +} +input[type="radio"], +input[type="checkbox"] { + margin: 4px 0 0; + margin-top: 1px \9; + line-height: normal; +} +input[type="file"] { + display: block; +} +input[type="range"] { + display: block; + width: 100%; +} +select[multiple], +select[size] { + height: auto; +} +input[type="file"]:focus, +input[type="radio"]:focus, +input[type="checkbox"]:focus { + outline: thin dotted; + outline: 5px auto -webkit-focus-ring-color; + outline-offset: -2px; +} +output { + display: block; + padding-top: 7px; + font-size: 14px; + line-height: 1.42857143; + color: #555; +} +.form-control { + display: block; + width: 100%; + height: 34px; + padding: 6px 12px; + font-size: 14px; + line-height: 1.42857143; + color: #555; + background-color: #fff; + background-image: none; + border: 1px solid #ccc; + border-radius: 4px; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); + -webkit-transition: border-color ease-in-out .15s, -webkit-box-shadow ease-in-out .15s; + -o-transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s; + transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s; +} +.form-control:focus { + border-color: #66afe9; + outline: 0; + -webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(102, 175, 233, .6); + box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(102, 175, 233, .6); +} +.form-control::-moz-placeholder { + color: #999; + opacity: 1; +} +.form-control:-ms-input-placeholder { + color: #999; +} +.form-control::-webkit-input-placeholder { + color: #999; +} +.form-control[disabled], +.form-control[readonly], +fieldset[disabled] .form-control { + background-color: #eee; + opacity: 1; +} +.form-control[disabled], +fieldset[disabled] .form-control { + cursor: not-allowed; +} +textarea.form-control { + height: auto; +} +input[type="search"] { + -webkit-appearance: none; +} +@media screen and (-webkit-min-device-pixel-ratio: 0) { + input[type="date"].form-control, + input[type="time"].form-control, + input[type="datetime-local"].form-control, + input[type="month"].form-control { + line-height: 34px; + } + input[type="date"].input-sm, + input[type="time"].input-sm, + input[type="datetime-local"].input-sm, + input[type="month"].input-sm, + .input-group-sm input[type="date"], + .input-group-sm input[type="time"], + .input-group-sm input[type="datetime-local"], + .input-group-sm input[type="month"] { + line-height: 30px; + } + input[type="date"].input-lg, + input[type="time"].input-lg, + input[type="datetime-local"].input-lg, + input[type="month"].input-lg, + .input-group-lg input[type="date"], + .input-group-lg input[type="time"], + .input-group-lg input[type="datetime-local"], + .input-group-lg input[type="month"] { + line-height: 46px; + } +} +.form-group { + margin-bottom: 15px; +} +.radio, +.checkbox { + position: relative; + display: block; + margin-top: 10px; + margin-bottom: 10px; +} +.radio label, +.checkbox label { + min-height: 20px; + padding-left: 20px; + margin-bottom: 0; + font-weight: normal; + cursor: pointer; +} +.radio input[type="radio"], +.radio-inline input[type="radio"], +.checkbox input[type="checkbox"], +.checkbox-inline input[type="checkbox"] { + position: absolute; + margin-top: 4px \9; + margin-left: -20px; +} +.radio + .radio, +.checkbox + .checkbox { + margin-top: -5px; +} +.radio-inline, +.checkbox-inline { + position: relative; + display: inline-block; + padding-left: 20px; + margin-bottom: 0; + font-weight: normal; + vertical-align: middle; + cursor: pointer; +} +.radio-inline + .radio-inline, +.checkbox-inline + .checkbox-inline { + margin-top: 0; + margin-left: 10px; +} +input[type="radio"][disabled], +input[type="checkbox"][disabled], +input[type="radio"].disabled, +input[type="checkbox"].disabled, +fieldset[disabled] input[type="radio"], +fieldset[disabled] input[type="checkbox"] { + cursor: not-allowed; +} +.radio-inline.disabled, +.checkbox-inline.disabled, +fieldset[disabled] .radio-inline, +fieldset[disabled] .checkbox-inline { + cursor: not-allowed; +} +.radio.disabled label, +.checkbox.disabled label, +fieldset[disabled] .radio label, +fieldset[disabled] .checkbox label { + cursor: not-allowed; +} +.form-control-static { + min-height: 34px; + padding-top: 7px; + padding-bottom: 7px; + margin-bottom: 0; +} +.form-control-static.input-lg, +.form-control-static.input-sm { + padding-right: 0; + padding-left: 0; +} +.input-sm { + height: 30px; + padding: 5px 10px; + font-size: 12px; + line-height: 1.5; + border-radius: 3px; +} +select.input-sm { + height: 30px; + line-height: 30px; +} +textarea.input-sm, +select[multiple].input-sm { + height: auto; +} +.form-group-sm .form-control { + height: 30px; + padding: 5px 10px; + font-size: 12px; + line-height: 1.5; + border-radius: 3px; +} +.form-group-sm select.form-control { + height: 30px; + line-height: 30px; +} +.form-group-sm textarea.form-control, +.form-group-sm select[multiple].form-control { + height: auto; +} +.form-group-sm .form-control-static { + height: 30px; + min-height: 32px; + padding: 6px 10px; + font-size: 12px; + line-height: 1.5; +} +.input-lg { + height: 46px; + padding: 10px 16px; + font-size: 18px; + line-height: 1.3333333; + border-radius: 6px; +} +select.input-lg { + height: 46px; + line-height: 46px; +} +textarea.input-lg, +select[multiple].input-lg { + height: auto; +} +.form-group-lg .form-control { + height: 46px; + padding: 10px 16px; + font-size: 18px; + line-height: 1.3333333; + border-radius: 6px; +} +.form-group-lg select.form-control { + height: 46px; + line-height: 46px; +} +.form-group-lg textarea.form-control, +.form-group-lg select[multiple].form-control { + height: auto; +} +.form-group-lg .form-control-static { + height: 46px; + min-height: 38px; + padding: 11px 16px; + font-size: 18px; + line-height: 1.3333333; +} +.has-feedback { + position: relative; +} +.has-feedback .form-control { + padding-right: 42.5px; +} +.form-control-feedback { + position: absolute; + top: 0; + right: 0; + z-index: 2; + display: block; + width: 34px; + height: 34px; + line-height: 34px; + text-align: center; + pointer-events: none; +} +.input-lg + .form-control-feedback, +.input-group-lg + .form-control-feedback, +.form-group-lg .form-control + .form-control-feedback { + width: 46px; + height: 46px; + line-height: 46px; +} +.input-sm + .form-control-feedback, +.input-group-sm + .form-control-feedback, +.form-group-sm .form-control + .form-control-feedback { + width: 30px; + height: 30px; + line-height: 30px; +} +.has-success .help-block, +.has-success .control-label, +.has-success .radio, +.has-success .checkbox, +.has-success .radio-inline, +.has-success .checkbox-inline, +.has-success.radio label, +.has-success.checkbox label, +.has-success.radio-inline label, +.has-success.checkbox-inline label { + color: #3c763d; +} +.has-success .form-control { + border-color: #3c763d; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); +} +.has-success .form-control:focus { + border-color: #2b542c; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px #67b168; + box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px #67b168; +} +.has-success .input-group-addon { + color: #3c763d; + background-color: #dff0d8; + border-color: #3c763d; +} +.has-success .form-control-feedback { + color: #3c763d; +} +.has-warning .help-block, +.has-warning .control-label, +.has-warning .radio, +.has-warning .checkbox, +.has-warning .radio-inline, +.has-warning .checkbox-inline, +.has-warning.radio label, +.has-warning.checkbox label, +.has-warning.radio-inline label, +.has-warning.checkbox-inline label { + color: #8a6d3b; +} +.has-warning .form-control { + border-color: #8a6d3b; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); +} +.has-warning .form-control:focus { + border-color: #66512c; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px #c0a16b; + box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px #c0a16b; +} +.has-warning .input-group-addon { + color: #8a6d3b; + background-color: #fcf8e3; + border-color: #8a6d3b; +} +.has-warning .form-control-feedback { + color: #8a6d3b; +} +.has-error .help-block, +.has-error .control-label, +.has-error .radio, +.has-error .checkbox, +.has-error .radio-inline, +.has-error .checkbox-inline, +.has-error.radio label, +.has-error.checkbox label, +.has-error.radio-inline label, +.has-error.checkbox-inline label { + color: #a94442; +} +.has-error .form-control { + border-color: #a94442; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); +} +.has-error .form-control:focus { + border-color: #843534; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px #ce8483; + box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px #ce8483; +} +.has-error .input-group-addon { + color: #a94442; + background-color: #f2dede; + border-color: #a94442; +} +.has-error .form-control-feedback { + color: #a94442; +} +.has-feedback label ~ .form-control-feedback { + top: 25px; +} +.has-feedback label.sr-only ~ .form-control-feedback { + top: 0; +} +.help-block { + display: block; + margin-top: 5px; + margin-bottom: 10px; + color: #737373; +} +@media (min-width: 768px) { + .form-inline .form-group { + display: inline-block; + margin-bottom: 0; + vertical-align: middle; + } + .form-inline .form-control { + display: inline-block; + width: auto; + vertical-align: middle; + } + .form-inline .form-control-static { + display: inline-block; + } + .form-inline .input-group { + display: inline-table; + vertical-align: middle; + } + .form-inline .input-group .input-group-addon, + .form-inline .input-group .input-group-btn, + .form-inline .input-group .form-control { + width: auto; + } + .form-inline .input-group > .form-control { + width: 100%; + } + .form-inline .control-label { + margin-bottom: 0; + vertical-align: middle; + } + .form-inline .radio, + .form-inline .checkbox { + display: inline-block; + margin-top: 0; + margin-bottom: 0; + vertical-align: middle; + } + .form-inline .radio label, + .form-inline .checkbox label { + padding-left: 0; + } + .form-inline .radio input[type="radio"], + .form-inline .checkbox input[type="checkbox"] { + position: relative; + margin-left: 0; + } + .form-inline .has-feedback .form-control-feedback { + top: 0; + } +} +.form-horizontal .radio, +.form-horizontal .checkbox, +.form-horizontal .radio-inline, +.form-horizontal .checkbox-inline { + padding-top: 7px; + margin-top: 0; + margin-bottom: 0; +} +.form-horizontal .radio, +.form-horizontal .checkbox { + min-height: 27px; +} +.form-horizontal .form-group { + margin-right: -15px; + margin-left: -15px; +} +@media (min-width: 768px) { + .form-horizontal .control-label { + padding-top: 7px; + margin-bottom: 0; + text-align: right; + } +} +.form-horizontal .has-feedback .form-control-feedback { + right: 15px; +} +@media (min-width: 768px) { + .form-horizontal .form-group-lg .control-label { + padding-top: 14.333333px; + font-size: 18px; + } +} +@media (min-width: 768px) { + .form-horizontal .form-group-sm .control-label { + padding-top: 6px; + font-size: 12px; + } +} +.btn { + display: inline-block; + padding: 6px 12px; + margin-bottom: 0; + font-size: 14px; + font-weight: normal; + line-height: 1.42857143; + text-align: center; + white-space: nowrap; + vertical-align: middle; + -ms-touch-action: manipulation; + touch-action: manipulation; + cursor: pointer; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + background-image: none; + border: 1px solid transparent; + border-radius: 4px; +} +.btn:focus, +.btn:active:focus, +.btn.active:focus, +.btn.focus, +.btn:active.focus, +.btn.active.focus { + outline: thin dotted; + outline: 5px auto -webkit-focus-ring-color; + outline-offset: -2px; +} +.btn:hover, +.btn:focus, +.btn.focus { + color: #333; + text-decoration: none; +} +.btn:active, +.btn.active { + background-image: none; + outline: 0; + -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125); + box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125); +} +.btn.disabled, +.btn[disabled], +fieldset[disabled] .btn { + cursor: not-allowed; + filter: alpha(opacity=65); + -webkit-box-shadow: none; + box-shadow: none; + opacity: .65; +} +a.btn.disabled, +fieldset[disabled] a.btn { + pointer-events: none; +} +.btn-default { + color: #333; + background-color: #fff; + border-color: #ccc; +} +.btn-default:focus, +.btn-default.focus { + color: #333; + background-color: #e6e6e6; + border-color: #8c8c8c; +} +.btn-default:hover { + color: #333; + background-color: #e6e6e6; + border-color: #adadad; +} +.btn-default:active, +.btn-default.active, +.open > .dropdown-toggle.btn-default { + color: #333; + background-color: #e6e6e6; + border-color: #adadad; +} +.btn-default:active:hover, +.btn-default.active:hover, +.open > .dropdown-toggle.btn-default:hover, +.btn-default:active:focus, +.btn-default.active:focus, +.open > .dropdown-toggle.btn-default:focus, +.btn-default:active.focus, +.btn-default.active.focus, +.open > .dropdown-toggle.btn-default.focus { + color: #333; + background-color: #d4d4d4; + border-color: #8c8c8c; +} +.btn-default:active, +.btn-default.active, +.open > .dropdown-toggle.btn-default { + background-image: none; +} +.btn-default.disabled, +.btn-default[disabled], +fieldset[disabled] .btn-default, +.btn-default.disabled:hover, +.btn-default[disabled]:hover, +fieldset[disabled] .btn-default:hover, +.btn-default.disabled:focus, +.btn-default[disabled]:focus, +fieldset[disabled] .btn-default:focus, +.btn-default.disabled.focus, +.btn-default[disabled].focus, +fieldset[disabled] .btn-default.focus, +.btn-default.disabled:active, +.btn-default[disabled]:active, +fieldset[disabled] .btn-default:active, +.btn-default.disabled.active, +.btn-default[disabled].active, +fieldset[disabled] .btn-default.active { + background-color: #fff; + border-color: #ccc; +} +.btn-default .badge { + color: #fff; + background-color: #333; +} +.btn-primary { + color: #fff; + background-color: #337ab7; + border-color: #2e6da4; +} +.btn-primary:focus, +.btn-primary.focus { + color: #fff; + background-color: #286090; + border-color: #122b40; +} +.btn-primary:hover { + color: #fff; + background-color: #286090; + border-color: #204d74; +} +.btn-primary:active, +.btn-primary.active, +.open > .dropdown-toggle.btn-primary { + color: #fff; + background-color: #286090; + border-color: #204d74; +} +.btn-primary:active:hover, +.btn-primary.active:hover, +.open > .dropdown-toggle.btn-primary:hover, +.btn-primary:active:focus, +.btn-primary.active:focus, +.open > .dropdown-toggle.btn-primary:focus, +.btn-primary:active.focus, +.btn-primary.active.focus, +.open > .dropdown-toggle.btn-primary.focus { + color: #fff; + background-color: #204d74; + border-color: #122b40; +} +.btn-primary:active, +.btn-primary.active, +.open > .dropdown-toggle.btn-primary { + background-image: none; +} +.btn-primary.disabled, +.btn-primary[disabled], +fieldset[disabled] .btn-primary, +.btn-primary.disabled:hover, +.btn-primary[disabled]:hover, +fieldset[disabled] .btn-primary:hover, +.btn-primary.disabled:focus, +.btn-primary[disabled]:focus, +fieldset[disabled] .btn-primary:focus, +.btn-primary.disabled.focus, +.btn-primary[disabled].focus, +fieldset[disabled] .btn-primary.focus, +.btn-primary.disabled:active, +.btn-primary[disabled]:active, +fieldset[disabled] .btn-primary:active, +.btn-primary.disabled.active, +.btn-primary[disabled].active, +fieldset[disabled] .btn-primary.active { + background-color: #337ab7; + border-color: #2e6da4; +} +.btn-primary .badge { + color: #337ab7; + background-color: #fff; +} +.btn-success { + color: #fff; + background-color: #5cb85c; + border-color: #4cae4c; +} +.btn-success:focus, +.btn-success.focus { + color: #fff; + background-color: #449d44; + border-color: #255625; +} +.btn-success:hover { + color: #fff; + background-color: #449d44; + border-color: #398439; +} +.btn-success:active, +.btn-success.active, +.open > .dropdown-toggle.btn-success { + color: #fff; + background-color: #449d44; + border-color: #398439; +} +.btn-success:active:hover, +.btn-success.active:hover, +.open > .dropdown-toggle.btn-success:hover, +.btn-success:active:focus, +.btn-success.active:focus, +.open > .dropdown-toggle.btn-success:focus, +.btn-success:active.focus, +.btn-success.active.focus, +.open > .dropdown-toggle.btn-success.focus { + color: #fff; + background-color: #398439; + border-color: #255625; +} +.btn-success:active, +.btn-success.active, +.open > .dropdown-toggle.btn-success { + background-image: none; +} +.btn-success.disabled, +.btn-success[disabled], +fieldset[disabled] .btn-success, +.btn-success.disabled:hover, +.btn-success[disabled]:hover, +fieldset[disabled] .btn-success:hover, +.btn-success.disabled:focus, +.btn-success[disabled]:focus, +fieldset[disabled] .btn-success:focus, +.btn-success.disabled.focus, +.btn-success[disabled].focus, +fieldset[disabled] .btn-success.focus, +.btn-success.disabled:active, +.btn-success[disabled]:active, +fieldset[disabled] .btn-success:active, +.btn-success.disabled.active, +.btn-success[disabled].active, +fieldset[disabled] .btn-success.active { + background-color: #5cb85c; + border-color: #4cae4c; +} +.btn-success .badge { + color: #5cb85c; + background-color: #fff; +} +.btn-info { + color: #fff; + background-color: #5bc0de; + border-color: #46b8da; +} +.btn-info:focus, +.btn-info.focus { + color: #fff; + background-color: #31b0d5; + border-color: #1b6d85; +} +.btn-info:hover { + color: #fff; + background-color: #31b0d5; + border-color: #269abc; +} +.btn-info:active, +.btn-info.active, +.open > .dropdown-toggle.btn-info { + color: #fff; + background-color: #31b0d5; + border-color: #269abc; +} +.btn-info:active:hover, +.btn-info.active:hover, +.open > .dropdown-toggle.btn-info:hover, +.btn-info:active:focus, +.btn-info.active:focus, +.open > .dropdown-toggle.btn-info:focus, +.btn-info:active.focus, +.btn-info.active.focus, +.open > .dropdown-toggle.btn-info.focus { + color: #fff; + background-color: #269abc; + border-color: #1b6d85; +} +.btn-info:active, +.btn-info.active, +.open > .dropdown-toggle.btn-info { + background-image: none; +} +.btn-info.disabled, +.btn-info[disabled], +fieldset[disabled] .btn-info, +.btn-info.disabled:hover, +.btn-info[disabled]:hover, +fieldset[disabled] .btn-info:hover, +.btn-info.disabled:focus, +.btn-info[disabled]:focus, +fieldset[disabled] .btn-info:focus, +.btn-info.disabled.focus, +.btn-info[disabled].focus, +fieldset[disabled] .btn-info.focus, +.btn-info.disabled:active, +.btn-info[disabled]:active, +fieldset[disabled] .btn-info:active, +.btn-info.disabled.active, +.btn-info[disabled].active, +fieldset[disabled] .btn-info.active { + background-color: #5bc0de; + border-color: #46b8da; +} +.btn-info .badge { + color: #5bc0de; + background-color: #fff; +} +.btn-warning { + color: #fff; + background-color: #f0ad4e; + border-color: #eea236; +} +.btn-warning:focus, +.btn-warning.focus { + color: #fff; + background-color: #ec971f; + border-color: #985f0d; +} +.btn-warning:hover { + color: #fff; + background-color: #ec971f; + border-color: #d58512; +} +.btn-warning:active, +.btn-warning.active, +.open > .dropdown-toggle.btn-warning { + color: #fff; + background-color: #ec971f; + border-color: #d58512; +} +.btn-warning:active:hover, +.btn-warning.active:hover, +.open > .dropdown-toggle.btn-warning:hover, +.btn-warning:active:focus, +.btn-warning.active:focus, +.open > .dropdown-toggle.btn-warning:focus, +.btn-warning:active.focus, +.btn-warning.active.focus, +.open > .dropdown-toggle.btn-warning.focus { + color: #fff; + background-color: #d58512; + border-color: #985f0d; +} +.btn-warning:active, +.btn-warning.active, +.open > .dropdown-toggle.btn-warning { + background-image: none; +} +.btn-warning.disabled, +.btn-warning[disabled], +fieldset[disabled] .btn-warning, +.btn-warning.disabled:hover, +.btn-warning[disabled]:hover, +fieldset[disabled] .btn-warning:hover, +.btn-warning.disabled:focus, +.btn-warning[disabled]:focus, +fieldset[disabled] .btn-warning:focus, +.btn-warning.disabled.focus, +.btn-warning[disabled].focus, +fieldset[disabled] .btn-warning.focus, +.btn-warning.disabled:active, +.btn-warning[disabled]:active, +fieldset[disabled] .btn-warning:active, +.btn-warning.disabled.active, +.btn-warning[disabled].active, +fieldset[disabled] .btn-warning.active { + background-color: #f0ad4e; + border-color: #eea236; +} +.btn-warning .badge { + color: #f0ad4e; + background-color: #fff; +} +.btn-danger { + color: #fff; + background-color: #d9534f; + border-color: #d43f3a; +} +.btn-danger:focus, +.btn-danger.focus { + color: #fff; + background-color: #c9302c; + border-color: #761c19; +} +.btn-danger:hover { + color: #fff; + background-color: #c9302c; + border-color: #ac2925; +} +.btn-danger:active, +.btn-danger.active, +.open > .dropdown-toggle.btn-danger { + color: #fff; + background-color: #c9302c; + border-color: #ac2925; +} +.btn-danger:active:hover, +.btn-danger.active:hover, +.open > .dropdown-toggle.btn-danger:hover, +.btn-danger:active:focus, +.btn-danger.active:focus, +.open > .dropdown-toggle.btn-danger:focus, +.btn-danger:active.focus, +.btn-danger.active.focus, +.open > .dropdown-toggle.btn-danger.focus { + color: #fff; + background-color: #ac2925; + border-color: #761c19; +} +.btn-danger:active, +.btn-danger.active, +.open > .dropdown-toggle.btn-danger { + background-image: none; +} +.btn-danger.disabled, +.btn-danger[disabled], +fieldset[disabled] .btn-danger, +.btn-danger.disabled:hover, +.btn-danger[disabled]:hover, +fieldset[disabled] .btn-danger:hover, +.btn-danger.disabled:focus, +.btn-danger[disabled]:focus, +fieldset[disabled] .btn-danger:focus, +.btn-danger.disabled.focus, +.btn-danger[disabled].focus, +fieldset[disabled] .btn-danger.focus, +.btn-danger.disabled:active, +.btn-danger[disabled]:active, +fieldset[disabled] .btn-danger:active, +.btn-danger.disabled.active, +.btn-danger[disabled].active, +fieldset[disabled] .btn-danger.active { + background-color: #d9534f; + border-color: #d43f3a; +} +.btn-danger .badge { + color: #d9534f; + background-color: #fff; +} +.btn-link { + font-weight: normal; + color: #337ab7; + border-radius: 0; +} +.btn-link, +.btn-link:active, +.btn-link.active, +.btn-link[disabled], +fieldset[disabled] .btn-link { + background-color: transparent; + -webkit-box-shadow: none; + box-shadow: none; +} +.btn-link, +.btn-link:hover, +.btn-link:focus, +.btn-link:active { + border-color: transparent; +} +.btn-link:hover, +.btn-link:focus { + color: #23527c; + text-decoration: underline; + background-color: transparent; +} +.btn-link[disabled]:hover, +fieldset[disabled] .btn-link:hover, +.btn-link[disabled]:focus, +fieldset[disabled] .btn-link:focus { + color: #777; + text-decoration: none; +} +.btn-lg, +.btn-group-lg > .btn { + padding: 10px 16px; + font-size: 18px; + line-height: 1.3333333; + border-radius: 6px; +} +.btn-sm, +.btn-group-sm > .btn { + padding: 5px 10px; + font-size: 12px; + line-height: 1.5; + border-radius: 3px; +} +.btn-xs, +.btn-group-xs > .btn { + padding: 1px 5px; + font-size: 12px; + line-height: 1.5; + border-radius: 3px; +} +.btn-block { + display: block; + width: 100%; +} +.btn-block + .btn-block { + margin-top: 5px; +} +input[type="submit"].btn-block, +input[type="reset"].btn-block, +input[type="button"].btn-block { + width: 100%; +} +.fade { + opacity: 0; + -webkit-transition: opacity .15s linear; + -o-transition: opacity .15s linear; + transition: opacity .15s linear; +} +.fade.in { + opacity: 1; +} +.collapse { + display: none; +} +.collapse.in { + display: block; +} +tr.collapse.in { + display: table-row; +} +tbody.collapse.in { + display: table-row-group; +} +.collapsing { + position: relative; + height: 0; + overflow: hidden; + -webkit-transition-timing-function: ease; + -o-transition-timing-function: ease; + transition-timing-function: ease; + -webkit-transition-duration: .35s; + -o-transition-duration: .35s; + transition-duration: .35s; + -webkit-transition-property: height, visibility; + -o-transition-property: height, visibility; + transition-property: height, visibility; +} +.caret { + display: inline-block; + width: 0; + height: 0; + margin-left: 2px; + vertical-align: middle; + border-top: 4px dashed; + border-top: 4px solid \9; + border-right: 4px solid transparent; + border-left: 4px solid transparent; +} +.dropup, +.dropdown { + position: relative; +} +.dropdown-toggle:focus { + outline: 0; +} +.dropdown-menu { + position: absolute; + top: 100%; + left: 0; + z-index: 1000; + display: none; + float: left; + min-width: 160px; + padding: 5px 0; + margin: 2px 0 0; + font-size: 14px; + text-align: left; + list-style: none; + background-color: #fff; + -webkit-background-clip: padding-box; + background-clip: padding-box; + border: 1px solid #ccc; + border: 1px solid rgba(0, 0, 0, .15); + border-radius: 4px; + -webkit-box-shadow: 0 6px 12px rgba(0, 0, 0, .175); + box-shadow: 0 6px 12px rgba(0, 0, 0, .175); +} +.dropdown-menu.pull-right { + right: 0; + left: auto; +} +.dropdown-menu .divider { + height: 1px; + margin: 9px 0; + overflow: hidden; + background-color: #e5e5e5; +} +.dropdown-menu > li > a { + display: block; + padding: 3px 20px; + clear: both; + font-weight: normal; + line-height: 1.42857143; + color: #333; + white-space: nowrap; +} +.dropdown-menu > li > a:hover, +.dropdown-menu > li > a:focus { + color: #262626; + text-decoration: none; + background-color: #f5f5f5; +} +.dropdown-menu > .active > a, +.dropdown-menu > .active > a:hover, +.dropdown-menu > .active > a:focus { + color: #fff; + text-decoration: none; + background-color: #337ab7; + outline: 0; +} +.dropdown-menu > .disabled > a, +.dropdown-menu > .disabled > a:hover, +.dropdown-menu > .disabled > a:focus { + color: #777; +} +.dropdown-menu > .disabled > a:hover, +.dropdown-menu > .disabled > a:focus { + text-decoration: none; + cursor: not-allowed; + background-color: transparent; + background-image: none; + filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); +} +.open > .dropdown-menu { + display: block; +} +.open > a { + outline: 0; +} +.dropdown-menu-right { + right: 0; + left: auto; +} +.dropdown-menu-left { + right: auto; + left: 0; +} +.dropdown-header { + display: block; + padding: 3px 20px; + font-size: 12px; + line-height: 1.42857143; + color: #777; + white-space: nowrap; +} +.dropdown-backdrop { + position: fixed; + top: 0; + right: 0; + bottom: 0; + left: 0; + z-index: 990; +} +.pull-right > .dropdown-menu { + right: 0; + left: auto; +} +.dropup .caret, +.navbar-fixed-bottom .dropdown .caret { + content: ""; + border-top: 0; + border-bottom: 4px dashed; + border-bottom: 4px solid \9; +} +.dropup .dropdown-menu, +.navbar-fixed-bottom .dropdown .dropdown-menu { + top: auto; + bottom: 100%; + margin-bottom: 2px; +} +@media (min-width: 768px) { + .navbar-right .dropdown-menu { + right: 0; + left: auto; + } + .navbar-right .dropdown-menu-left { + right: auto; + left: 0; + } +} +.btn-group, +.btn-group-vertical { + position: relative; + display: inline-block; + vertical-align: middle; +} +.btn-group > .btn, +.btn-group-vertical > .btn { + position: relative; + float: left; +} +.btn-group > .btn:hover, +.btn-group-vertical > .btn:hover, +.btn-group > .btn:focus, +.btn-group-vertical > .btn:focus, +.btn-group > .btn:active, +.btn-group-vertical > .btn:active, +.btn-group > .btn.active, +.btn-group-vertical > .btn.active { + z-index: 2; +} +.btn-group .btn + .btn, +.btn-group .btn + .btn-group, +.btn-group .btn-group + .btn, +.btn-group .btn-group + .btn-group { + margin-left: -1px; +} +.btn-toolbar { + margin-left: -5px; +} +.btn-toolbar .btn, +.btn-toolbar .btn-group, +.btn-toolbar .input-group { + float: left; +} +.btn-toolbar > .btn, +.btn-toolbar > .btn-group, +.btn-toolbar > .input-group { + margin-left: 5px; +} +.btn-group > .btn:not(:first-child):not(:last-child):not(.dropdown-toggle) { + border-radius: 0; +} +.btn-group > .btn:first-child { + margin-left: 0; +} +.btn-group > .btn:first-child:not(:last-child):not(.dropdown-toggle) { + border-top-right-radius: 0; + border-bottom-right-radius: 0; +} +.btn-group > .btn:last-child:not(:first-child), +.btn-group > .dropdown-toggle:not(:first-child) { + border-top-left-radius: 0; + border-bottom-left-radius: 0; +} +.btn-group > .btn-group { + float: left; +} +.btn-group > .btn-group:not(:first-child):not(:last-child) > .btn { + border-radius: 0; +} +.btn-group > .btn-group:first-child:not(:last-child) > .btn:last-child, +.btn-group > .btn-group:first-child:not(:last-child) > .dropdown-toggle { + border-top-right-radius: 0; + border-bottom-right-radius: 0; +} +.btn-group > .btn-group:last-child:not(:first-child) > .btn:first-child { + border-top-left-radius: 0; + border-bottom-left-radius: 0; +} +.btn-group .dropdown-toggle:active, +.btn-group.open .dropdown-toggle { + outline: 0; +} +.btn-group > .btn + .dropdown-toggle { + padding-right: 8px; + padding-left: 8px; +} +.btn-group > .btn-lg + .dropdown-toggle { + padding-right: 12px; + padding-left: 12px; +} +.btn-group.open .dropdown-toggle { + -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125); + box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125); +} +.btn-group.open .dropdown-toggle.btn-link { + -webkit-box-shadow: none; + box-shadow: none; +} +.btn .caret { + margin-left: 0; +} +.btn-lg .caret { + border-width: 5px 5px 0; + border-bottom-width: 0; +} +.dropup .btn-lg .caret { + border-width: 0 5px 5px; +} +.btn-group-vertical > .btn, +.btn-group-vertical > .btn-group, +.btn-group-vertical > .btn-group > .btn { + display: block; + float: none; + width: 100%; + max-width: 100%; +} +.btn-group-vertical > .btn-group > .btn { + float: none; +} +.btn-group-vertical > .btn + .btn, +.btn-group-vertical > .btn + .btn-group, +.btn-group-vertical > .btn-group + .btn, +.btn-group-vertical > .btn-group + .btn-group { + margin-top: -1px; + margin-left: 0; +} +.btn-group-vertical > .btn:not(:first-child):not(:last-child) { + border-radius: 0; +} +.btn-group-vertical > .btn:first-child:not(:last-child) { + border-top-right-radius: 4px; + border-bottom-right-radius: 0; + border-bottom-left-radius: 0; +} +.btn-group-vertical > .btn:last-child:not(:first-child) { + border-top-left-radius: 0; + border-top-right-radius: 0; + border-bottom-left-radius: 4px; +} +.btn-group-vertical > .btn-group:not(:first-child):not(:last-child) > .btn { + border-radius: 0; +} +.btn-group-vertical > .btn-group:first-child:not(:last-child) > .btn:last-child, +.btn-group-vertical > .btn-group:first-child:not(:last-child) > .dropdown-toggle { + border-bottom-right-radius: 0; + border-bottom-left-radius: 0; +} +.btn-group-vertical > .btn-group:last-child:not(:first-child) > .btn:first-child { + border-top-left-radius: 0; + border-top-right-radius: 0; +} +.btn-group-justified { + display: table; + width: 100%; + table-layout: fixed; + border-collapse: separate; +} +.btn-group-justified > .btn, +.btn-group-justified > .btn-group { + display: table-cell; + float: none; + width: 1%; +} +.btn-group-justified > .btn-group .btn { + width: 100%; +} +.btn-group-justified > .btn-group .dropdown-menu { + left: auto; +} +[data-toggle="buttons"] > .btn input[type="radio"], +[data-toggle="buttons"] > .btn-group > .btn input[type="radio"], +[data-toggle="buttons"] > .btn input[type="checkbox"], +[data-toggle="buttons"] > .btn-group > .btn input[type="checkbox"] { + position: absolute; + clip: rect(0, 0, 0, 0); + pointer-events: none; +} +.input-group { + position: relative; + display: table; + border-collapse: separate; +} +.input-group[class*="col-"] { + float: none; + padding-right: 0; + padding-left: 0; +} +.input-group .form-control { + position: relative; + z-index: 2; + float: left; + width: 100%; + margin-bottom: 0; +} +.input-group-lg > .form-control, +.input-group-lg > .input-group-addon, +.input-group-lg > .input-group-btn > .btn { + height: 46px; + padding: 10px 16px; + font-size: 18px; + line-height: 1.3333333; + border-radius: 6px; +} +select.input-group-lg > .form-control, +select.input-group-lg > .input-group-addon, +select.input-group-lg > .input-group-btn > .btn { + height: 46px; + line-height: 46px; +} +textarea.input-group-lg > .form-control, +textarea.input-group-lg > .input-group-addon, +textarea.input-group-lg > .input-group-btn > .btn, +select[multiple].input-group-lg > .form-control, +select[multiple].input-group-lg > .input-group-addon, +select[multiple].input-group-lg > .input-group-btn > .btn { + height: auto; +} +.input-group-sm > .form-control, +.input-group-sm > .input-group-addon, +.input-group-sm > .input-group-btn > .btn { + height: 30px; + padding: 5px 10px; + font-size: 12px; + line-height: 1.5; + border-radius: 3px; +} +select.input-group-sm > .form-control, +select.input-group-sm > .input-group-addon, +select.input-group-sm > .input-group-btn > .btn { + height: 30px; + line-height: 30px; +} +textarea.input-group-sm > .form-control, +textarea.input-group-sm > .input-group-addon, +textarea.input-group-sm > .input-group-btn > .btn, +select[multiple].input-group-sm > .form-control, +select[multiple].input-group-sm > .input-group-addon, +select[multiple].input-group-sm > .input-group-btn > .btn { + height: auto; +} +.input-group-addon, +.input-group-btn, +.input-group .form-control { + display: table-cell; +} +.input-group-addon:not(:first-child):not(:last-child), +.input-group-btn:not(:first-child):not(:last-child), +.input-group .form-control:not(:first-child):not(:last-child) { + border-radius: 0; +} +.input-group-addon, +.input-group-btn { + width: 1%; + white-space: nowrap; + vertical-align: middle; +} +.input-group-addon { + padding: 6px 12px; + font-size: 14px; + font-weight: normal; + line-height: 1; + color: #555; + text-align: center; + background-color: #eee; + border: 1px solid #ccc; + border-radius: 4px; +} +.input-group-addon.input-sm { + padding: 5px 10px; + font-size: 12px; + border-radius: 3px; +} +.input-group-addon.input-lg { + padding: 10px 16px; + font-size: 18px; + border-radius: 6px; +} +.input-group-addon input[type="radio"], +.input-group-addon input[type="checkbox"] { + margin-top: 0; +} +.input-group .form-control:first-child, +.input-group-addon:first-child, +.input-group-btn:first-child > .btn, +.input-group-btn:first-child > .btn-group > .btn, +.input-group-btn:first-child > .dropdown-toggle, +.input-group-btn:last-child > .btn:not(:last-child):not(.dropdown-toggle), +.input-group-btn:last-child > .btn-group:not(:last-child) > .btn { + border-top-right-radius: 0; + border-bottom-right-radius: 0; +} +.input-group-addon:first-child { + border-right: 0; +} +.input-group .form-control:last-child, +.input-group-addon:last-child, +.input-group-btn:last-child > .btn, +.input-group-btn:last-child > .btn-group > .btn, +.input-group-btn:last-child > .dropdown-toggle, +.input-group-btn:first-child > .btn:not(:first-child), +.input-group-btn:first-child > .btn-group:not(:first-child) > .btn { + border-top-left-radius: 0; + border-bottom-left-radius: 0; +} +.input-group-addon:last-child { + border-left: 0; +} +.input-group-btn { + position: relative; + font-size: 0; + white-space: nowrap; +} +.input-group-btn > .btn { + position: relative; +} +.input-group-btn > .btn + .btn { + margin-left: -1px; +} +.input-group-btn > .btn:hover, +.input-group-btn > .btn:focus, +.input-group-btn > .btn:active { + z-index: 2; +} +.input-group-btn:first-child > .btn, +.input-group-btn:first-child > .btn-group { + margin-right: -1px; +} +.input-group-btn:last-child > .btn, +.input-group-btn:last-child > .btn-group { + z-index: 2; + margin-left: -1px; +} +.nav { + padding-left: 0; + margin-bottom: 0; + list-style: none; +} +.nav > li { + position: relative; + display: block; +} +.nav > li > a { + position: relative; + display: block; + padding: 10px 15px; +} +.nav > li > a:hover, +.nav > li > a:focus { + text-decoration: none; + background-color: #eee; +} +.nav > li.disabled > a { + color: #777; +} +.nav > li.disabled > a:hover, +.nav > li.disabled > a:focus { + color: #777; + text-decoration: none; + cursor: not-allowed; + background-color: transparent; +} +.nav .open > a, +.nav .open > a:hover, +.nav .open > a:focus { + background-color: #eee; + border-color: #337ab7; +} +.nav .nav-divider { + height: 1px; + margin: 9px 0; + overflow: hidden; + background-color: #e5e5e5; +} +.nav > li > a > img { + max-width: none; +} +.nav-tabs { + border-bottom: 1px solid #ddd; +} +.nav-tabs > li { + float: left; + margin-bottom: -1px; +} +.nav-tabs > li > a { + margin-right: 2px; + line-height: 1.42857143; + border: 1px solid transparent; + border-radius: 4px 4px 0 0; +} +.nav-tabs > li > a:hover { + border-color: #eee #eee #ddd; +} +.nav-tabs > li.active > a, +.nav-tabs > li.active > a:hover, +.nav-tabs > li.active > a:focus { + color: #555; + cursor: default; + background-color: #fff; + border: 1px solid #ddd; + border-bottom-color: transparent; +} +.nav-tabs.nav-justified { + width: 100%; + border-bottom: 0; +} +.nav-tabs.nav-justified > li { + float: none; +} +.nav-tabs.nav-justified > li > a { + margin-bottom: 5px; + text-align: center; +} +.nav-tabs.nav-justified > .dropdown .dropdown-menu { + top: auto; + left: auto; +} +@media (min-width: 768px) { + .nav-tabs.nav-justified > li { + display: table-cell; + width: 1%; + } + .nav-tabs.nav-justified > li > a { + margin-bottom: 0; + } +} +.nav-tabs.nav-justified > li > a { + margin-right: 0; + border-radius: 4px; +} +.nav-tabs.nav-justified > .active > a, +.nav-tabs.nav-justified > .active > a:hover, +.nav-tabs.nav-justified > .active > a:focus { + border: 1px solid #ddd; +} +@media (min-width: 768px) { + .nav-tabs.nav-justified > li > a { + border-bottom: 1px solid #ddd; + border-radius: 4px 4px 0 0; + } + .nav-tabs.nav-justified > .active > a, + .nav-tabs.nav-justified > .active > a:hover, + .nav-tabs.nav-justified > .active > a:focus { + border-bottom-color: #fff; + } +} +.nav-pills > li { + float: left; +} +.nav-pills > li > a { + border-radius: 4px; +} +.nav-pills > li + li { + margin-left: 2px; +} +.nav-pills > li.active > a, +.nav-pills > li.active > a:hover, +.nav-pills > li.active > a:focus { + color: #fff; + background-color: #337ab7; +} +.nav-stacked > li { + float: none; +} +.nav-stacked > li + li { + margin-top: 2px; + margin-left: 0; +} +.nav-justified { + width: 100%; +} +.nav-justified > li { + float: none; +} +.nav-justified > li > a { + margin-bottom: 5px; + text-align: center; +} +.nav-justified > .dropdown .dropdown-menu { + top: auto; + left: auto; +} +@media (min-width: 768px) { + .nav-justified > li { + display: table-cell; + width: 1%; + } + .nav-justified > li > a { + margin-bottom: 0; + } +} +.nav-tabs-justified { + border-bottom: 0; +} +.nav-tabs-justified > li > a { + margin-right: 0; + border-radius: 4px; +} +.nav-tabs-justified > .active > a, +.nav-tabs-justified > .active > a:hover, +.nav-tabs-justified > .active > a:focus { + border: 1px solid #ddd; +} +@media (min-width: 768px) { + .nav-tabs-justified > li > a { + border-bottom: 1px solid #ddd; + border-radius: 4px 4px 0 0; + } + .nav-tabs-justified > .active > a, + .nav-tabs-justified > .active > a:hover, + .nav-tabs-justified > .active > a:focus { + border-bottom-color: #fff; + } +} +.tab-content > .tab-pane { + display: none; +} +.tab-content > .active { + display: block; +} +.nav-tabs .dropdown-menu { + margin-top: -1px; + border-top-left-radius: 0; + border-top-right-radius: 0; +} +.navbar { + position: relative; + min-height: 50px; + margin-bottom: 20px; + border: 1px solid transparent; +} +@media (min-width: 768px) { + .navbar { + border-radius: 4px; + } +} +@media (min-width: 768px) { + .navbar-header { + float: left; + } +} +.navbar-collapse { + padding-right: 15px; + padding-left: 15px; + overflow-x: visible; + -webkit-overflow-scrolling: touch; + border-top: 1px solid transparent; + -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .1); + box-shadow: inset 0 1px 0 rgba(255, 255, 255, .1); +} +.navbar-collapse.in { + overflow-y: auto; +} +@media (min-width: 768px) { + .navbar-collapse { + width: auto; + border-top: 0; + -webkit-box-shadow: none; + box-shadow: none; + } + .navbar-collapse.collapse { + display: block !important; + height: auto !important; + padding-bottom: 0; + overflow: visible !important; + } + .navbar-collapse.in { + overflow-y: visible; + } + .navbar-fixed-top .navbar-collapse, + .navbar-static-top .navbar-collapse, + .navbar-fixed-bottom .navbar-collapse { + padding-right: 0; + padding-left: 0; + } +} +.navbar-fixed-top .navbar-collapse, +.navbar-fixed-bottom .navbar-collapse { + max-height: 340px; +} +@media (max-device-width: 480px) and (orientation: landscape) { + .navbar-fixed-top .navbar-collapse, + .navbar-fixed-bottom .navbar-collapse { + max-height: 200px; + } +} +.container > .navbar-header, +.container-fluid > .navbar-header, +.container > .navbar-collapse, +.container-fluid > .navbar-collapse { + margin-right: -15px; + margin-left: -15px; +} +@media (min-width: 768px) { + .container > .navbar-header, + .container-fluid > .navbar-header, + .container > .navbar-collapse, + .container-fluid > .navbar-collapse { + margin-right: 0; + margin-left: 0; + } +} +.navbar-static-top { + z-index: 1000; + border-width: 0 0 1px; +} +@media (min-width: 768px) { + .navbar-static-top { + border-radius: 0; + } +} +.navbar-fixed-top, +.navbar-fixed-bottom { + position: fixed; + right: 0; + left: 0; + z-index: 1030; +} +@media (min-width: 768px) { + .navbar-fixed-top, + .navbar-fixed-bottom { + border-radius: 0; + } +} +.navbar-fixed-top { + top: 0; + border-width: 0 0 1px; +} +.navbar-fixed-bottom { + bottom: 0; + margin-bottom: 0; + border-width: 1px 0 0; +} +.navbar-brand { + float: left; + height: 50px; + padding: 15px 15px; + font-size: 18px; + line-height: 20px; +} +.navbar-brand:hover, +.navbar-brand:focus { + text-decoration: none; +} +.navbar-brand > img { + display: block; +} +@media (min-width: 768px) { + .navbar > .container .navbar-brand, + .navbar > .container-fluid .navbar-brand { + margin-left: -15px; + } +} +.navbar-toggle { + position: relative; + float: right; + padding: 9px 10px; + margin-top: 8px; + margin-right: 15px; + margin-bottom: 8px; + background-color: transparent; + background-image: none; + border: 1px solid transparent; + border-radius: 4px; +} +.navbar-toggle:focus { + outline: 0; +} +.navbar-toggle .icon-bar { + display: block; + width: 22px; + height: 2px; + border-radius: 1px; +} +.navbar-toggle .icon-bar + .icon-bar { + margin-top: 4px; +} +@media (min-width: 768px) { + .navbar-toggle { + display: none; + } +} +.navbar-nav { + margin: 7.5px -15px; +} +.navbar-nav > li > a { + padding-top: 10px; + padding-bottom: 10px; + line-height: 20px; +} +@media (max-width: 767px) { + .navbar-nav .open .dropdown-menu { + position: static; + float: none; + width: auto; + margin-top: 0; + background-color: transparent; + border: 0; + -webkit-box-shadow: none; + box-shadow: none; + } + .navbar-nav .open .dropdown-menu > li > a, + .navbar-nav .open .dropdown-menu .dropdown-header { + padding: 5px 15px 5px 25px; + } + .navbar-nav .open .dropdown-menu > li > a { + line-height: 20px; + } + .navbar-nav .open .dropdown-menu > li > a:hover, + .navbar-nav .open .dropdown-menu > li > a:focus { + background-image: none; + } +} +@media (min-width: 768px) { + .navbar-nav { + float: left; + margin: 0; + } + .navbar-nav > li { + float: left; + } + .navbar-nav > li > a { + padding-top: 15px; + padding-bottom: 15px; + } +} +.navbar-form { + padding: 10px 15px; + margin-top: 8px; + margin-right: -15px; + margin-bottom: 8px; + margin-left: -15px; + border-top: 1px solid transparent; + border-bottom: 1px solid transparent; + -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .1), 0 1px 0 rgba(255, 255, 255, .1); + box-shadow: inset 0 1px 0 rgba(255, 255, 255, .1), 0 1px 0 rgba(255, 255, 255, .1); +} +@media (min-width: 768px) { + .navbar-form .form-group { + display: inline-block; + margin-bottom: 0; + vertical-align: middle; + } + .navbar-form .form-control { + display: inline-block; + width: auto; + vertical-align: middle; + } + .navbar-form .form-control-static { + display: inline-block; + } + .navbar-form .input-group { + display: inline-table; + vertical-align: middle; + } + .navbar-form .input-group .input-group-addon, + .navbar-form .input-group .input-group-btn, + .navbar-form .input-group .form-control { + width: auto; + } + .navbar-form .input-group > .form-control { + width: 100%; + } + .navbar-form .control-label { + margin-bottom: 0; + vertical-align: middle; + } + .navbar-form .radio, + .navbar-form .checkbox { + display: inline-block; + margin-top: 0; + margin-bottom: 0; + vertical-align: middle; + } + .navbar-form .radio label, + .navbar-form .checkbox label { + padding-left: 0; + } + .navbar-form .radio input[type="radio"], + .navbar-form .checkbox input[type="checkbox"] { + position: relative; + margin-left: 0; + } + .navbar-form .has-feedback .form-control-feedback { + top: 0; + } +} +@media (max-width: 767px) { + .navbar-form .form-group { + margin-bottom: 5px; + } + .navbar-form .form-group:last-child { + margin-bottom: 0; + } +} +@media (min-width: 768px) { + .navbar-form { + width: auto; + padding-top: 0; + padding-bottom: 0; + margin-right: 0; + margin-left: 0; + border: 0; + -webkit-box-shadow: none; + box-shadow: none; + } +} +.navbar-nav > li > .dropdown-menu { + margin-top: 0; + border-top-left-radius: 0; + border-top-right-radius: 0; +} +.navbar-fixed-bottom .navbar-nav > li > .dropdown-menu { + margin-bottom: 0; + border-top-left-radius: 4px; + border-top-right-radius: 4px; + border-bottom-right-radius: 0; + border-bottom-left-radius: 0; +} +.navbar-btn { + margin-top: 8px; + margin-bottom: 8px; +} +.navbar-btn.btn-sm { + margin-top: 10px; + margin-bottom: 10px; +} +.navbar-btn.btn-xs { + margin-top: 14px; + margin-bottom: 14px; +} +.navbar-text { + margin-top: 15px; + margin-bottom: 15px; +} +@media (min-width: 768px) { + .navbar-text { + float: left; + margin-right: 15px; + margin-left: 15px; + } +} +@media (min-width: 768px) { + .navbar-left { + float: left !important; + } + .navbar-right { + float: right !important; + margin-right: -15px; + } + .navbar-right ~ .navbar-right { + margin-right: 0; + } +} +.navbar-default { + background-color: #f8f8f8; + border-color: #e7e7e7; +} +.navbar-default .navbar-brand { + color: #777; +} +.navbar-default .navbar-brand:hover, +.navbar-default .navbar-brand:focus { + color: #5e5e5e; + background-color: transparent; +} +.navbar-default .navbar-text { + color: #777; +} +.navbar-default .navbar-nav > li > a { + color: #777; +} +.navbar-default .navbar-nav > li > a:hover, +.navbar-default .navbar-nav > li > a:focus { + color: #333; + background-color: transparent; +} +.navbar-default .navbar-nav > .active > a, +.navbar-default .navbar-nav > .active > a:hover, +.navbar-default .navbar-nav > .active > a:focus { + color: #555; + background-color: #e7e7e7; +} +.navbar-default .navbar-nav > .disabled > a, +.navbar-default .navbar-nav > .disabled > a:hover, +.navbar-default .navbar-nav > .disabled > a:focus { + color: #ccc; + background-color: transparent; +} +.navbar-default .navbar-toggle { + border-color: #ddd; +} +.navbar-default .navbar-toggle:hover, +.navbar-default .navbar-toggle:focus { + background-color: #ddd; +} +.navbar-default .navbar-toggle .icon-bar { + background-color: #888; +} +.navbar-default .navbar-collapse, +.navbar-default .navbar-form { + border-color: #e7e7e7; +} +.navbar-default .navbar-nav > .open > a, +.navbar-default .navbar-nav > .open > a:hover, +.navbar-default .navbar-nav > .open > a:focus { + color: #555; + background-color: #e7e7e7; +} +@media (max-width: 767px) { + .navbar-default .navbar-nav .open .dropdown-menu > li > a { + color: #777; + } + .navbar-default .navbar-nav .open .dropdown-menu > li > a:hover, + .navbar-default .navbar-nav .open .dropdown-menu > li > a:focus { + color: #333; + background-color: transparent; + } + .navbar-default .navbar-nav .open .dropdown-menu > .active > a, + .navbar-default .navbar-nav .open .dropdown-menu > .active > a:hover, + .navbar-default .navbar-nav .open .dropdown-menu > .active > a:focus { + color: #555; + background-color: #e7e7e7; + } + .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a, + .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a:hover, + .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a:focus { + color: #ccc; + background-color: transparent; + } +} +.navbar-default .navbar-link { + color: #777; +} +.navbar-default .navbar-link:hover { + color: #333; +} +.navbar-default .btn-link { + color: #777; +} +.navbar-default .btn-link:hover, +.navbar-default .btn-link:focus { + color: #333; +} +.navbar-default .btn-link[disabled]:hover, +fieldset[disabled] .navbar-default .btn-link:hover, +.navbar-default .btn-link[disabled]:focus, +fieldset[disabled] .navbar-default .btn-link:focus { + color: #ccc; +} +.navbar-inverse { + background-color: #222; + border-color: #080808; +} +.navbar-inverse .navbar-brand { + color: #9d9d9d; +} +.navbar-inverse .navbar-brand:hover, +.navbar-inverse .navbar-brand:focus { + color: #fff; + background-color: transparent; +} +.navbar-inverse .navbar-text { + color: #9d9d9d; +} +.navbar-inverse .navbar-nav > li > a { + color: #9d9d9d; +} +.navbar-inverse .navbar-nav > li > a:hover, +.navbar-inverse .navbar-nav > li > a:focus { + color: #fff; + background-color: transparent; +} +.navbar-inverse .navbar-nav > .active > a, +.navbar-inverse .navbar-nav > .active > a:hover, +.navbar-inverse .navbar-nav > .active > a:focus { + color: #fff; + background-color: #080808; +} +.navbar-inverse .navbar-nav > .disabled > a, +.navbar-inverse .navbar-nav > .disabled > a:hover, +.navbar-inverse .navbar-nav > .disabled > a:focus { + color: #444; + background-color: transparent; +} +.navbar-inverse .navbar-toggle { + border-color: #333; +} +.navbar-inverse .navbar-toggle:hover, +.navbar-inverse .navbar-toggle:focus { + background-color: #333; +} +.navbar-inverse .navbar-toggle .icon-bar { + background-color: #fff; +} +.navbar-inverse .navbar-collapse, +.navbar-inverse .navbar-form { + border-color: #101010; +} +.navbar-inverse .navbar-nav > .open > a, +.navbar-inverse .navbar-nav > .open > a:hover, +.navbar-inverse .navbar-nav > .open > a:focus { + color: #fff; + background-color: #080808; +} +@media (max-width: 767px) { + .navbar-inverse .navbar-nav .open .dropdown-menu > .dropdown-header { + border-color: #080808; + } + .navbar-inverse .navbar-nav .open .dropdown-menu .divider { + background-color: #080808; + } + .navbar-inverse .navbar-nav .open .dropdown-menu > li > a { + color: #9d9d9d; + } + .navbar-inverse .navbar-nav .open .dropdown-menu > li > a:hover, + .navbar-inverse .navbar-nav .open .dropdown-menu > li > a:focus { + color: #fff; + background-color: transparent; + } + .navbar-inverse .navbar-nav .open .dropdown-menu > .active > a, + .navbar-inverse .navbar-nav .open .dropdown-menu > .active > a:hover, + .navbar-inverse .navbar-nav .open .dropdown-menu > .active > a:focus { + color: #fff; + background-color: #080808; + } + .navbar-inverse .navbar-nav .open .dropdown-menu > .disabled > a, + .navbar-inverse .navbar-nav .open .dropdown-menu > .disabled > a:hover, + .navbar-inverse .navbar-nav .open .dropdown-menu > .disabled > a:focus { + color: #444; + background-color: transparent; + } +} +.navbar-inverse .navbar-link { + color: #9d9d9d; +} +.navbar-inverse .navbar-link:hover { + color: #fff; +} +.navbar-inverse .btn-link { + color: #9d9d9d; +} +.navbar-inverse .btn-link:hover, +.navbar-inverse .btn-link:focus { + color: #fff; +} +.navbar-inverse .btn-link[disabled]:hover, +fieldset[disabled] .navbar-inverse .btn-link:hover, +.navbar-inverse .btn-link[disabled]:focus, +fieldset[disabled] .navbar-inverse .btn-link:focus { + color: #444; +} +.breadcrumb { + padding: 8px 15px; + margin-bottom: 20px; + list-style: none; + background-color: #f5f5f5; + border-radius: 4px; +} +.breadcrumb > li { + display: inline-block; +} +.breadcrumb > li + li:before { + padding: 0 5px; + color: #ccc; + content: "/\00a0"; +} +.breadcrumb > .active { + color: #777; +} +.pagination { + display: inline-block; + padding-left: 0; + margin: 20px 0; + border-radius: 4px; +} +.pagination > li { + display: inline; +} +.pagination > li > a, +.pagination > li > span { + position: relative; + float: left; + padding: 6px 12px; + margin-left: -1px; + line-height: 1.42857143; + color: #337ab7; + text-decoration: none; + background-color: #fff; + border: 1px solid #ddd; +} +.pagination > li:first-child > a, +.pagination > li:first-child > span { + margin-left: 0; + border-top-left-radius: 4px; + border-bottom-left-radius: 4px; +} +.pagination > li:last-child > a, +.pagination > li:last-child > span { + border-top-right-radius: 4px; + border-bottom-right-radius: 4px; +} +.pagination > li > a:hover, +.pagination > li > span:hover, +.pagination > li > a:focus, +.pagination > li > span:focus { + z-index: 3; + color: #23527c; + background-color: #eee; + border-color: #ddd; +} +.pagination > .active > a, +.pagination > .active > span, +.pagination > .active > a:hover, +.pagination > .active > span:hover, +.pagination > .active > a:focus, +.pagination > .active > span:focus { + z-index: 2; + color: #fff; + cursor: default; + background-color: #337ab7; + border-color: #337ab7; +} +.pagination > .disabled > span, +.pagination > .disabled > span:hover, +.pagination > .disabled > span:focus, +.pagination > .disabled > a, +.pagination > .disabled > a:hover, +.pagination > .disabled > a:focus { + color: #777; + cursor: not-allowed; + background-color: #fff; + border-color: #ddd; +} +.pagination-lg > li > a, +.pagination-lg > li > span { + padding: 10px 16px; + font-size: 18px; + line-height: 1.3333333; +} +.pagination-lg > li:first-child > a, +.pagination-lg > li:first-child > span { + border-top-left-radius: 6px; + border-bottom-left-radius: 6px; +} +.pagination-lg > li:last-child > a, +.pagination-lg > li:last-child > span { + border-top-right-radius: 6px; + border-bottom-right-radius: 6px; +} +.pagination-sm > li > a, +.pagination-sm > li > span { + padding: 5px 10px; + font-size: 12px; + line-height: 1.5; +} +.pagination-sm > li:first-child > a, +.pagination-sm > li:first-child > span { + border-top-left-radius: 3px; + border-bottom-left-radius: 3px; +} +.pagination-sm > li:last-child > a, +.pagination-sm > li:last-child > span { + border-top-right-radius: 3px; + border-bottom-right-radius: 3px; +} +.pager { + padding-left: 0; + margin: 20px 0; + text-align: center; + list-style: none; +} +.pager li { + display: inline; +} +.pager li > a, +.pager li > span { + display: inline-block; + padding: 5px 14px; + background-color: #fff; + border: 1px solid #ddd; + border-radius: 15px; +} +.pager li > a:hover, +.pager li > a:focus { + text-decoration: none; + background-color: #eee; +} +.pager .next > a, +.pager .next > span { + float: right; +} +.pager .previous > a, +.pager .previous > span { + float: left; +} +.pager .disabled > a, +.pager .disabled > a:hover, +.pager .disabled > a:focus, +.pager .disabled > span { + color: #777; + cursor: not-allowed; + background-color: #fff; +} +.label { + display: inline; + padding: .2em .6em .3em; + font-size: 75%; + font-weight: bold; + line-height: 1; + color: #fff; + text-align: center; + white-space: nowrap; + vertical-align: baseline; + border-radius: .25em; +} +a.label:hover, +a.label:focus { + color: #fff; + text-decoration: none; + cursor: pointer; +} +.label:empty { + display: none; +} +.btn .label { + position: relative; + top: -1px; +} +.label-default { + background-color: #777; +} +.label-default[href]:hover, +.label-default[href]:focus { + background-color: #5e5e5e; +} +.label-primary { + background-color: #337ab7; +} +.label-primary[href]:hover, +.label-primary[href]:focus { + background-color: #286090; +} +.label-success { + background-color: #5cb85c; +} +.label-success[href]:hover, +.label-success[href]:focus { + background-color: #449d44; +} +.label-info { + background-color: #5bc0de; +} +.label-info[href]:hover, +.label-info[href]:focus { + background-color: #31b0d5; +} +.label-warning { + background-color: #f0ad4e; +} +.label-warning[href]:hover, +.label-warning[href]:focus { + background-color: #ec971f; +} +.label-danger { + background-color: #d9534f; +} +.label-danger[href]:hover, +.label-danger[href]:focus { + background-color: #c9302c; +} +.badge { + display: inline-block; + min-width: 10px; + padding: 3px 7px; + font-size: 12px; + font-weight: bold; + line-height: 1; + color: #fff; + text-align: center; + white-space: nowrap; + vertical-align: middle; + background-color: #777; + border-radius: 10px; +} +.badge:empty { + display: none; +} +.btn .badge { + position: relative; + top: -1px; +} +.btn-xs .badge, +.btn-group-xs > .btn .badge { + top: 0; + padding: 1px 5px; +} +a.badge:hover, +a.badge:focus { + color: #fff; + text-decoration: none; + cursor: pointer; +} +.list-group-item.active > .badge, +.nav-pills > .active > a > .badge { + color: #337ab7; + background-color: #fff; +} +.list-group-item > .badge { + float: right; +} +.list-group-item > .badge + .badge { + margin-right: 5px; +} +.nav-pills > li > a > .badge { + margin-left: 3px; +} +.jumbotron { + padding-top: 30px; + padding-bottom: 30px; + margin-bottom: 30px; + color: inherit; + background-color: #eee; +} +.jumbotron h1, +.jumbotron .h1 { + color: inherit; +} +.jumbotron p { + margin-bottom: 15px; + font-size: 21px; + font-weight: 200; +} +.jumbotron > hr { + border-top-color: #d5d5d5; +} +.container .jumbotron, +.container-fluid .jumbotron { + border-radius: 6px; +} +.jumbotron .container { + max-width: 100%; +} +@media screen and (min-width: 768px) { + .jumbotron { + padding-top: 48px; + padding-bottom: 48px; + } + .container .jumbotron, + .container-fluid .jumbotron { + padding-right: 60px; + padding-left: 60px; + } + .jumbotron h1, + .jumbotron .h1 { + font-size: 63px; + } +} +.thumbnail { + display: block; + padding: 4px; + margin-bottom: 20px; + line-height: 1.42857143; + background-color: #fff; + border: 1px solid #ddd; + border-radius: 4px; + -webkit-transition: border .2s ease-in-out; + -o-transition: border .2s ease-in-out; + transition: border .2s ease-in-out; +} +.thumbnail > img, +.thumbnail a > img { + margin-right: auto; + margin-left: auto; +} +a.thumbnail:hover, +a.thumbnail:focus, +a.thumbnail.active { + border-color: #337ab7; +} +.thumbnail .caption { + padding: 9px; + color: #333; +} +.alert { + padding: 15px; + margin-bottom: 20px; + border: 1px solid transparent; + border-radius: 4px; +} +.alert h4 { + margin-top: 0; + color: inherit; +} +.alert .alert-link { + font-weight: bold; +} +.alert > p, +.alert > ul { + margin-bottom: 0; +} +.alert > p + p { + margin-top: 5px; +} +.alert-dismissable, +.alert-dismissible { + padding-right: 35px; +} +.alert-dismissable .close, +.alert-dismissible .close { + position: relative; + top: -2px; + right: -21px; + color: inherit; +} +.alert-success { + color: #3c763d; + background-color: #dff0d8; + border-color: #d6e9c6; +} +.alert-success hr { + border-top-color: #c9e2b3; +} +.alert-success .alert-link { + color: #2b542c; +} +.alert-info { + color: #31708f; + background-color: #d9edf7; + border-color: #bce8f1; +} +.alert-info hr { + border-top-color: #a6e1ec; +} +.alert-info .alert-link { + color: #245269; +} +.alert-warning { + color: #8a6d3b; + background-color: #fcf8e3; + border-color: #faebcc; +} +.alert-warning hr { + border-top-color: #f7e1b5; +} +.alert-warning .alert-link { + color: #66512c; +} +.alert-danger { + color: #a94442; + background-color: #f2dede; + border-color: #ebccd1; +} +.alert-danger hr { + border-top-color: #e4b9c0; +} +.alert-danger .alert-link { + color: #843534; +} +@-webkit-keyframes progress-bar-stripes { + from { + background-position: 40px 0; + } + to { + background-position: 0 0; + } +} +@-o-keyframes progress-bar-stripes { + from { + background-position: 40px 0; + } + to { + background-position: 0 0; + } +} +@keyframes progress-bar-stripes { + from { + background-position: 40px 0; + } + to { + background-position: 0 0; + } +} +.progress { + height: 20px; + margin-bottom: 20px; + overflow: hidden; + background-color: #f5f5f5; + border-radius: 4px; + -webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, .1); + box-shadow: inset 0 1px 2px rgba(0, 0, 0, .1); +} +.progress-bar { + float: left; + width: 0; + height: 100%; + font-size: 12px; + line-height: 20px; + color: #fff; + text-align: center; + background-color: #337ab7; + -webkit-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, .15); + box-shadow: inset 0 -1px 0 rgba(0, 0, 0, .15); + -webkit-transition: width .6s ease; + -o-transition: width .6s ease; + transition: width .6s ease; +} +.progress-striped .progress-bar, +.progress-bar-striped { + background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); + background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); + background-image: linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); + -webkit-background-size: 40px 40px; + background-size: 40px 40px; +} +.progress.active .progress-bar, +.progress-bar.active { + -webkit-animation: progress-bar-stripes 2s linear infinite; + -o-animation: progress-bar-stripes 2s linear infinite; + animation: progress-bar-stripes 2s linear infinite; +} +.progress-bar-success { + background-color: #5cb85c; +} +.progress-striped .progress-bar-success { + background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); + background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); + background-image: linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); +} +.progress-bar-info { + background-color: #5bc0de; +} +.progress-striped .progress-bar-info { + background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); + background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); + background-image: linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); +} +.progress-bar-warning { + background-color: #f0ad4e; +} +.progress-striped .progress-bar-warning { + background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); + background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); + background-image: linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); +} +.progress-bar-danger { + background-color: #d9534f; +} +.progress-striped .progress-bar-danger { + background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); + background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); + background-image: linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); +} +.media { + margin-top: 15px; +} +.media:first-child { + margin-top: 0; +} +.media, +.media-body { + overflow: hidden; + zoom: 1; +} +.media-body { + width: 10000px; +} +.media-object { + display: block; +} +.media-object.img-thumbnail { + max-width: none; +} +.media-right, +.media > .pull-right { + padding-left: 10px; +} +.media-left, +.media > .pull-left { + padding-right: 10px; +} +.media-left, +.media-right, +.media-body { + display: table-cell; + vertical-align: top; +} +.media-middle { + vertical-align: middle; +} +.media-bottom { + vertical-align: bottom; +} +.media-heading { + margin-top: 0; + margin-bottom: 5px; +} +.media-list { + padding-left: 0; + list-style: none; +} +.list-group { + padding-left: 0; + margin-bottom: 20px; +} +.list-group-item { + position: relative; + display: block; + padding: 10px 15px; + margin-bottom: -1px; + background-color: #fff; + border: 1px solid #ddd; +} +.list-group-item:first-child { + border-top-left-radius: 4px; + border-top-right-radius: 4px; +} +.list-group-item:last-child { + margin-bottom: 0; + border-bottom-right-radius: 4px; + border-bottom-left-radius: 4px; +} +a.list-group-item, +button.list-group-item { + color: #555; +} +a.list-group-item .list-group-item-heading, +button.list-group-item .list-group-item-heading { + color: #333; +} +a.list-group-item:hover, +button.list-group-item:hover, +a.list-group-item:focus, +button.list-group-item:focus { + color: #555; + text-decoration: none; + background-color: #f5f5f5; +} +button.list-group-item { + width: 100%; + text-align: left; +} +.list-group-item.disabled, +.list-group-item.disabled:hover, +.list-group-item.disabled:focus { + color: #777; + cursor: not-allowed; + background-color: #eee; +} +.list-group-item.disabled .list-group-item-heading, +.list-group-item.disabled:hover .list-group-item-heading, +.list-group-item.disabled:focus .list-group-item-heading { + color: inherit; +} +.list-group-item.disabled .list-group-item-text, +.list-group-item.disabled:hover .list-group-item-text, +.list-group-item.disabled:focus .list-group-item-text { + color: #777; +} +.list-group-item.active, +.list-group-item.active:hover, +.list-group-item.active:focus { + z-index: 2; + color: #fff; + background-color: #337ab7; + border-color: #337ab7; +} +.list-group-item.active .list-group-item-heading, +.list-group-item.active:hover .list-group-item-heading, +.list-group-item.active:focus .list-group-item-heading, +.list-group-item.active .list-group-item-heading > small, +.list-group-item.active:hover .list-group-item-heading > small, +.list-group-item.active:focus .list-group-item-heading > small, +.list-group-item.active .list-group-item-heading > .small, +.list-group-item.active:hover .list-group-item-heading > .small, +.list-group-item.active:focus .list-group-item-heading > .small { + color: inherit; +} +.list-group-item.active .list-group-item-text, +.list-group-item.active:hover .list-group-item-text, +.list-group-item.active:focus .list-group-item-text { + color: #c7ddef; +} +.list-group-item-success { + color: #3c763d; + background-color: #dff0d8; +} +a.list-group-item-success, +button.list-group-item-success { + color: #3c763d; +} +a.list-group-item-success .list-group-item-heading, +button.list-group-item-success .list-group-item-heading { + color: inherit; +} +a.list-group-item-success:hover, +button.list-group-item-success:hover, +a.list-group-item-success:focus, +button.list-group-item-success:focus { + color: #3c763d; + background-color: #d0e9c6; +} +a.list-group-item-success.active, +button.list-group-item-success.active, +a.list-group-item-success.active:hover, +button.list-group-item-success.active:hover, +a.list-group-item-success.active:focus, +button.list-group-item-success.active:focus { + color: #fff; + background-color: #3c763d; + border-color: #3c763d; +} +.list-group-item-info { + color: #31708f; + background-color: #d9edf7; +} +a.list-group-item-info, +button.list-group-item-info { + color: #31708f; +} +a.list-group-item-info .list-group-item-heading, +button.list-group-item-info .list-group-item-heading { + color: inherit; +} +a.list-group-item-info:hover, +button.list-group-item-info:hover, +a.list-group-item-info:focus, +button.list-group-item-info:focus { + color: #31708f; + background-color: #c4e3f3; +} +a.list-group-item-info.active, +button.list-group-item-info.active, +a.list-group-item-info.active:hover, +button.list-group-item-info.active:hover, +a.list-group-item-info.active:focus, +button.list-group-item-info.active:focus { + color: #fff; + background-color: #31708f; + border-color: #31708f; +} +.list-group-item-warning { + color: #8a6d3b; + background-color: #fcf8e3; +} +a.list-group-item-warning, +button.list-group-item-warning { + color: #8a6d3b; +} +a.list-group-item-warning .list-group-item-heading, +button.list-group-item-warning .list-group-item-heading { + color: inherit; +} +a.list-group-item-warning:hover, +button.list-group-item-warning:hover, +a.list-group-item-warning:focus, +button.list-group-item-warning:focus { + color: #8a6d3b; + background-color: #faf2cc; +} +a.list-group-item-warning.active, +button.list-group-item-warning.active, +a.list-group-item-warning.active:hover, +button.list-group-item-warning.active:hover, +a.list-group-item-warning.active:focus, +button.list-group-item-warning.active:focus { + color: #fff; + background-color: #8a6d3b; + border-color: #8a6d3b; +} +.list-group-item-danger { + color: #a94442; + background-color: #f2dede; +} +a.list-group-item-danger, +button.list-group-item-danger { + color: #a94442; +} +a.list-group-item-danger .list-group-item-heading, +button.list-group-item-danger .list-group-item-heading { + color: inherit; +} +a.list-group-item-danger:hover, +button.list-group-item-danger:hover, +a.list-group-item-danger:focus, +button.list-group-item-danger:focus { + color: #a94442; + background-color: #ebcccc; +} +a.list-group-item-danger.active, +button.list-group-item-danger.active, +a.list-group-item-danger.active:hover, +button.list-group-item-danger.active:hover, +a.list-group-item-danger.active:focus, +button.list-group-item-danger.active:focus { + color: #fff; + background-color: #a94442; + border-color: #a94442; +} +.list-group-item-heading { + margin-top: 0; + margin-bottom: 5px; +} +.list-group-item-text { + margin-bottom: 0; + line-height: 1.3; +} +.panel { + margin-bottom: 20px; + background-color: #fff; + border: 1px solid transparent; + border-radius: 4px; + -webkit-box-shadow: 0 1px 1px rgba(0, 0, 0, .05); + box-shadow: 0 1px 1px rgba(0, 0, 0, .05); +} +.panel-body { + padding: 15px; +} +.panel-heading { + padding: 10px 15px; + border-bottom: 1px solid transparent; + border-top-left-radius: 3px; + border-top-right-radius: 3px; +} +.panel-heading > .dropdown .dropdown-toggle { + color: inherit; +} +.panel-title { + margin-top: 0; + margin-bottom: 0; + font-size: 16px; + color: inherit; +} +.panel-title > a, +.panel-title > small, +.panel-title > .small, +.panel-title > small > a, +.panel-title > .small > a { + color: inherit; +} +.panel-footer { + padding: 10px 15px; + background-color: #f5f5f5; + border-top: 1px solid #ddd; + border-bottom-right-radius: 3px; + border-bottom-left-radius: 3px; +} +.panel > .list-group, +.panel > .panel-collapse > .list-group { + margin-bottom: 0; +} +.panel > .list-group .list-group-item, +.panel > .panel-collapse > .list-group .list-group-item { + border-width: 1px 0; + border-radius: 0; +} +.panel > .list-group:first-child .list-group-item:first-child, +.panel > .panel-collapse > .list-group:first-child .list-group-item:first-child { + border-top: 0; + border-top-left-radius: 3px; + border-top-right-radius: 3px; +} +.panel > .list-group:last-child .list-group-item:last-child, +.panel > .panel-collapse > .list-group:last-child .list-group-item:last-child { + border-bottom: 0; + border-bottom-right-radius: 3px; + border-bottom-left-radius: 3px; +} +.panel > .panel-heading + .panel-collapse > .list-group .list-group-item:first-child { + border-top-left-radius: 0; + border-top-right-radius: 0; +} +.panel-heading + .list-group .list-group-item:first-child { + border-top-width: 0; +} +.list-group + .panel-footer { + border-top-width: 0; +} +.panel > .table, +.panel > .table-responsive > .table, +.panel > .panel-collapse > .table { + margin-bottom: 0; +} +.panel > .table caption, +.panel > .table-responsive > .table caption, +.panel > .panel-collapse > .table caption { + padding-right: 15px; + padding-left: 15px; +} +.panel > .table:first-child, +.panel > .table-responsive:first-child > .table:first-child { + border-top-left-radius: 3px; + border-top-right-radius: 3px; +} +.panel > .table:first-child > thead:first-child > tr:first-child, +.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child, +.panel > .table:first-child > tbody:first-child > tr:first-child, +.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child { + border-top-left-radius: 3px; + border-top-right-radius: 3px; +} +.panel > .table:first-child > thead:first-child > tr:first-child td:first-child, +.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child td:first-child, +.panel > .table:first-child > tbody:first-child > tr:first-child td:first-child, +.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child td:first-child, +.panel > .table:first-child > thead:first-child > tr:first-child th:first-child, +.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child th:first-child, +.panel > .table:first-child > tbody:first-child > tr:first-child th:first-child, +.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child th:first-child { + border-top-left-radius: 3px; +} +.panel > .table:first-child > thead:first-child > tr:first-child td:last-child, +.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child td:last-child, +.panel > .table:first-child > tbody:first-child > tr:first-child td:last-child, +.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child td:last-child, +.panel > .table:first-child > thead:first-child > tr:first-child th:last-child, +.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child th:last-child, +.panel > .table:first-child > tbody:first-child > tr:first-child th:last-child, +.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child th:last-child { + border-top-right-radius: 3px; +} +.panel > .table:last-child, +.panel > .table-responsive:last-child > .table:last-child { + border-bottom-right-radius: 3px; + border-bottom-left-radius: 3px; +} +.panel > .table:last-child > tbody:last-child > tr:last-child, +.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child, +.panel > .table:last-child > tfoot:last-child > tr:last-child, +.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child { + border-bottom-right-radius: 3px; + border-bottom-left-radius: 3px; +} +.panel > .table:last-child > tbody:last-child > tr:last-child td:first-child, +.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child td:first-child, +.panel > .table:last-child > tfoot:last-child > tr:last-child td:first-child, +.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child td:first-child, +.panel > .table:last-child > tbody:last-child > tr:last-child th:first-child, +.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child th:first-child, +.panel > .table:last-child > tfoot:last-child > tr:last-child th:first-child, +.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child th:first-child { + border-bottom-left-radius: 3px; +} +.panel > .table:last-child > tbody:last-child > tr:last-child td:last-child, +.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child td:last-child, +.panel > .table:last-child > tfoot:last-child > tr:last-child td:last-child, +.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child td:last-child, +.panel > .table:last-child > tbody:last-child > tr:last-child th:last-child, +.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child th:last-child, +.panel > .table:last-child > tfoot:last-child > tr:last-child th:last-child, +.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child th:last-child { + border-bottom-right-radius: 3px; +} +.panel > .panel-body + .table, +.panel > .panel-body + .table-responsive, +.panel > .table + .panel-body, +.panel > .table-responsive + .panel-body { + border-top: 1px solid #ddd; +} +.panel > .table > tbody:first-child > tr:first-child th, +.panel > .table > tbody:first-child > tr:first-child td { + border-top: 0; +} +.panel > .table-bordered, +.panel > .table-responsive > .table-bordered { + border: 0; +} +.panel > .table-bordered > thead > tr > th:first-child, +.panel > .table-responsive > .table-bordered > thead > tr > th:first-child, +.panel > .table-bordered > tbody > tr > th:first-child, +.panel > .table-responsive > .table-bordered > tbody > tr > th:first-child, +.panel > .table-bordered > tfoot > tr > th:first-child, +.panel > .table-responsive > .table-bordered > tfoot > tr > th:first-child, +.panel > .table-bordered > thead > tr > td:first-child, +.panel > .table-responsive > .table-bordered > thead > tr > td:first-child, +.panel > .table-bordered > tbody > tr > td:first-child, +.panel > .table-responsive > .table-bordered > tbody > tr > td:first-child, +.panel > .table-bordered > tfoot > tr > td:first-child, +.panel > .table-responsive > .table-bordered > tfoot > tr > td:first-child { + border-left: 0; +} +.panel > .table-bordered > thead > tr > th:last-child, +.panel > .table-responsive > .table-bordered > thead > tr > th:last-child, +.panel > .table-bordered > tbody > tr > th:last-child, +.panel > .table-responsive > .table-bordered > tbody > tr > th:last-child, +.panel > .table-bordered > tfoot > tr > th:last-child, +.panel > .table-responsive > .table-bordered > tfoot > tr > th:last-child, +.panel > .table-bordered > thead > tr > td:last-child, +.panel > .table-responsive > .table-bordered > thead > tr > td:last-child, +.panel > .table-bordered > tbody > tr > td:last-child, +.panel > .table-responsive > .table-bordered > tbody > tr > td:last-child, +.panel > .table-bordered > tfoot > tr > td:last-child, +.panel > .table-responsive > .table-bordered > tfoot > tr > td:last-child { + border-right: 0; +} +.panel > .table-bordered > thead > tr:first-child > td, +.panel > .table-responsive > .table-bordered > thead > tr:first-child > td, +.panel > .table-bordered > tbody > tr:first-child > td, +.panel > .table-responsive > .table-bordered > tbody > tr:first-child > td, +.panel > .table-bordered > thead > tr:first-child > th, +.panel > .table-responsive > .table-bordered > thead > tr:first-child > th, +.panel > .table-bordered > tbody > tr:first-child > th, +.panel > .table-responsive > .table-bordered > tbody > tr:first-child > th { + border-bottom: 0; +} +.panel > .table-bordered > tbody > tr:last-child > td, +.panel > .table-responsive > .table-bordered > tbody > tr:last-child > td, +.panel > .table-bordered > tfoot > tr:last-child > td, +.panel > .table-responsive > .table-bordered > tfoot > tr:last-child > td, +.panel > .table-bordered > tbody > tr:last-child > th, +.panel > .table-responsive > .table-bordered > tbody > tr:last-child > th, +.panel > .table-bordered > tfoot > tr:last-child > th, +.panel > .table-responsive > .table-bordered > tfoot > tr:last-child > th { + border-bottom: 0; +} +.panel > .table-responsive { + margin-bottom: 0; + border: 0; +} +.panel-group { + margin-bottom: 20px; +} +.panel-group .panel { + margin-bottom: 0; + border-radius: 4px; +} +.panel-group .panel + .panel { + margin-top: 5px; +} +.panel-group .panel-heading { + border-bottom: 0; +} +.panel-group .panel-heading + .panel-collapse > .panel-body, +.panel-group .panel-heading + .panel-collapse > .list-group { + border-top: 1px solid #ddd; +} +.panel-group .panel-footer { + border-top: 0; +} +.panel-group .panel-footer + .panel-collapse .panel-body { + border-bottom: 1px solid #ddd; +} +.panel-default { + border-color: #ddd; +} +.panel-default > .panel-heading { + color: #333; + background-color: #f5f5f5; + border-color: #ddd; +} +.panel-default > .panel-heading + .panel-collapse > .panel-body { + border-top-color: #ddd; +} +.panel-default > .panel-heading .badge { + color: #f5f5f5; + background-color: #333; +} +.panel-default > .panel-footer + .panel-collapse > .panel-body { + border-bottom-color: #ddd; +} +.panel-primary { + border-color: #337ab7; +} +.panel-primary > .panel-heading { + color: #fff; + background-color: #337ab7; + border-color: #337ab7; +} +.panel-primary > .panel-heading + .panel-collapse > .panel-body { + border-top-color: #337ab7; +} +.panel-primary > .panel-heading .badge { + color: #337ab7; + background-color: #fff; +} +.panel-primary > .panel-footer + .panel-collapse > .panel-body { + border-bottom-color: #337ab7; +} +.panel-success { + border-color: #d6e9c6; +} +.panel-success > .panel-heading { + color: #3c763d; + background-color: #dff0d8; + border-color: #d6e9c6; +} +.panel-success > .panel-heading + .panel-collapse > .panel-body { + border-top-color: #d6e9c6; +} +.panel-success > .panel-heading .badge { + color: #dff0d8; + background-color: #3c763d; +} +.panel-success > .panel-footer + .panel-collapse > .panel-body { + border-bottom-color: #d6e9c6; +} +.panel-info { + border-color: #bce8f1; +} +.panel-info > .panel-heading { + color: #31708f; + background-color: #d9edf7; + border-color: #bce8f1; +} +.panel-info > .panel-heading + .panel-collapse > .panel-body { + border-top-color: #bce8f1; +} +.panel-info > .panel-heading .badge { + color: #d9edf7; + background-color: #31708f; +} +.panel-info > .panel-footer + .panel-collapse > .panel-body { + border-bottom-color: #bce8f1; +} +.panel-warning { + border-color: #faebcc; +} +.panel-warning > .panel-heading { + color: #8a6d3b; + background-color: #fcf8e3; + border-color: #faebcc; +} +.panel-warning > .panel-heading + .panel-collapse > .panel-body { + border-top-color: #faebcc; +} +.panel-warning > .panel-heading .badge { + color: #fcf8e3; + background-color: #8a6d3b; +} +.panel-warning > .panel-footer + .panel-collapse > .panel-body { + border-bottom-color: #faebcc; +} +.panel-danger { + border-color: #ebccd1; +} +.panel-danger > .panel-heading { + color: #a94442; + background-color: #f2dede; + border-color: #ebccd1; +} +.panel-danger > .panel-heading + .panel-collapse > .panel-body { + border-top-color: #ebccd1; +} +.panel-danger > .panel-heading .badge { + color: #f2dede; + background-color: #a94442; +} +.panel-danger > .panel-footer + .panel-collapse > .panel-body { + border-bottom-color: #ebccd1; +} +.embed-responsive { + position: relative; + display: block; + height: 0; + padding: 0; + overflow: hidden; +} +.embed-responsive .embed-responsive-item, +.embed-responsive iframe, +.embed-responsive embed, +.embed-responsive object, +.embed-responsive video { + position: absolute; + top: 0; + bottom: 0; + left: 0; + width: 100%; + height: 100%; + border: 0; +} +.embed-responsive-16by9 { + padding-bottom: 56.25%; +} +.embed-responsive-4by3 { + padding-bottom: 75%; +} +.well { + min-height: 20px; + padding: 19px; + margin-bottom: 20px; + background-color: #f5f5f5; + border: 1px solid #e3e3e3; + border-radius: 4px; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .05); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, .05); +} +.well blockquote { + border-color: #ddd; + border-color: rgba(0, 0, 0, .15); +} +.well-lg { + padding: 24px; + border-radius: 6px; +} +.well-sm { + padding: 9px; + border-radius: 3px; +} +.close { + float: right; + font-size: 21px; + font-weight: bold; + line-height: 1; + color: #000; + text-shadow: 0 1px 0 #fff; + filter: alpha(opacity=20); + opacity: .2; +} +.close:hover, +.close:focus { + color: #000; + text-decoration: none; + cursor: pointer; + filter: alpha(opacity=50); + opacity: .5; +} +button.close { + -webkit-appearance: none; + padding: 0; + cursor: pointer; + background: transparent; + border: 0; +} +.modal-open { + overflow: hidden; +} +.modal { + position: fixed; + top: 0; + right: 0; + bottom: 0; + left: 0; + z-index: 1050; + display: none; + overflow: hidden; + -webkit-overflow-scrolling: touch; + outline: 0; +} +.modal.fade .modal-dialog { + -webkit-transition: -webkit-transform .3s ease-out; + -o-transition: -o-transform .3s ease-out; + transition: transform .3s ease-out; + -webkit-transform: translate(0, -25%); + -ms-transform: translate(0, -25%); + -o-transform: translate(0, -25%); + transform: translate(0, -25%); +} +.modal.in .modal-dialog { + -webkit-transform: translate(0, 0); + -ms-transform: translate(0, 0); + -o-transform: translate(0, 0); + transform: translate(0, 0); +} +.modal-open .modal { + overflow-x: hidden; + overflow-y: auto; +} +.modal-dialog { + position: relative; + width: auto; + margin: 10px; +} +.modal-content { + position: relative; + background-color: #fff; + -webkit-background-clip: padding-box; + background-clip: padding-box; + border: 1px solid #999; + border: 1px solid rgba(0, 0, 0, .2); + border-radius: 6px; + outline: 0; + -webkit-box-shadow: 0 3px 9px rgba(0, 0, 0, .5); + box-shadow: 0 3px 9px rgba(0, 0, 0, .5); +} +.modal-backdrop { + position: fixed; + top: 0; + right: 0; + bottom: 0; + left: 0; + z-index: 1040; + background-color: #000; +} +.modal-backdrop.fade { + filter: alpha(opacity=0); + opacity: 0; +} +.modal-backdrop.in { + filter: alpha(opacity=50); + opacity: .5; +} +.modal-header { + min-height: 16.42857143px; + padding: 15px; + border-bottom: 1px solid #e5e5e5; +} +.modal-header .close { + margin-top: -2px; +} +.modal-title { + margin: 0; + line-height: 1.42857143; +} +.modal-body { + position: relative; + padding: 15px; +} +.modal-footer { + padding: 15px; + text-align: right; + border-top: 1px solid #e5e5e5; +} +.modal-footer .btn + .btn { + margin-bottom: 0; + margin-left: 5px; +} +.modal-footer .btn-group .btn + .btn { + margin-left: -1px; +} +.modal-footer .btn-block + .btn-block { + margin-left: 0; +} +.modal-scrollbar-measure { + position: absolute; + top: -9999px; + width: 50px; + height: 50px; + overflow: scroll; +} +@media (min-width: 768px) { + .modal-dialog { + width: 600px; + margin: 30px auto; + } + .modal-content { + -webkit-box-shadow: 0 5px 15px rgba(0, 0, 0, .5); + box-shadow: 0 5px 15px rgba(0, 0, 0, .5); + } + .modal-sm { + width: 300px; + } +} +@media (min-width: 992px) { + .modal-lg { + width: 900px; + } +} +.tooltip { + position: absolute; + z-index: 1070; + display: block; + font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; + font-size: 12px; + font-style: normal; + font-weight: normal; + line-height: 1.42857143; + text-align: left; + text-align: start; + text-decoration: none; + text-shadow: none; + text-transform: none; + letter-spacing: normal; + word-break: normal; + word-spacing: normal; + word-wrap: normal; + white-space: normal; + filter: alpha(opacity=0); + opacity: 0; + + line-break: auto; +} +.tooltip.in { + filter: alpha(opacity=90); + opacity: .9; +} +.tooltip.top { + padding: 5px 0; + margin-top: -3px; +} +.tooltip.right { + padding: 0 5px; + margin-left: 3px; +} +.tooltip.bottom { + padding: 5px 0; + margin-top: 3px; +} +.tooltip.left { + padding: 0 5px; + margin-left: -3px; +} +.tooltip-inner { + max-width: 200px; + padding: 3px 8px; + color: #fff; + text-align: center; + background-color: #000; + border-radius: 4px; +} +.tooltip-arrow { + position: absolute; + width: 0; + height: 0; + border-color: transparent; + border-style: solid; +} +.tooltip.top .tooltip-arrow { + bottom: 0; + left: 50%; + margin-left: -5px; + border-width: 5px 5px 0; + border-top-color: #000; +} +.tooltip.top-left .tooltip-arrow { + right: 5px; + bottom: 0; + margin-bottom: -5px; + border-width: 5px 5px 0; + border-top-color: #000; +} +.tooltip.top-right .tooltip-arrow { + bottom: 0; + left: 5px; + margin-bottom: -5px; + border-width: 5px 5px 0; + border-top-color: #000; +} +.tooltip.right .tooltip-arrow { + top: 50%; + left: 0; + margin-top: -5px; + border-width: 5px 5px 5px 0; + border-right-color: #000; +} +.tooltip.left .tooltip-arrow { + top: 50%; + right: 0; + margin-top: -5px; + border-width: 5px 0 5px 5px; + border-left-color: #000; +} +.tooltip.bottom .tooltip-arrow { + top: 0; + left: 50%; + margin-left: -5px; + border-width: 0 5px 5px; + border-bottom-color: #000; +} +.tooltip.bottom-left .tooltip-arrow { + top: 0; + right: 5px; + margin-top: -5px; + border-width: 0 5px 5px; + border-bottom-color: #000; +} +.tooltip.bottom-right .tooltip-arrow { + top: 0; + left: 5px; + margin-top: -5px; + border-width: 0 5px 5px; + border-bottom-color: #000; +} +.popover { + position: absolute; + top: 0; + left: 0; + z-index: 1060; + display: none; + max-width: 276px; + padding: 1px; + font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; + font-size: 14px; + font-style: normal; + font-weight: normal; + line-height: 1.42857143; + text-align: left; + text-align: start; + text-decoration: none; + text-shadow: none; + text-transform: none; + letter-spacing: normal; + word-break: normal; + word-spacing: normal; + word-wrap: normal; + white-space: normal; + background-color: #fff; + -webkit-background-clip: padding-box; + background-clip: padding-box; + border: 1px solid #ccc; + border: 1px solid rgba(0, 0, 0, .2); + border-radius: 6px; + -webkit-box-shadow: 0 5px 10px rgba(0, 0, 0, .2); + box-shadow: 0 5px 10px rgba(0, 0, 0, .2); + + line-break: auto; +} +.popover.top { + margin-top: -10px; +} +.popover.right { + margin-left: 10px; +} +.popover.bottom { + margin-top: 10px; +} +.popover.left { + margin-left: -10px; +} +.popover-title { + padding: 8px 14px; + margin: 0; + font-size: 14px; + background-color: #f7f7f7; + border-bottom: 1px solid #ebebeb; + border-radius: 5px 5px 0 0; +} +.popover-content { + padding: 9px 14px; +} +.popover > .arrow, +.popover > .arrow:after { + position: absolute; + display: block; + width: 0; + height: 0; + border-color: transparent; + border-style: solid; +} +.popover > .arrow { + border-width: 11px; +} +.popover > .arrow:after { + content: ""; + border-width: 10px; +} +.popover.top > .arrow { + bottom: -11px; + left: 50%; + margin-left: -11px; + border-top-color: #999; + border-top-color: rgba(0, 0, 0, .25); + border-bottom-width: 0; +} +.popover.top > .arrow:after { + bottom: 1px; + margin-left: -10px; + content: " "; + border-top-color: #fff; + border-bottom-width: 0; +} +.popover.right > .arrow { + top: 50%; + left: -11px; + margin-top: -11px; + border-right-color: #999; + border-right-color: rgba(0, 0, 0, .25); + border-left-width: 0; +} +.popover.right > .arrow:after { + bottom: -10px; + left: 1px; + content: " "; + border-right-color: #fff; + border-left-width: 0; +} +.popover.bottom > .arrow { + top: -11px; + left: 50%; + margin-left: -11px; + border-top-width: 0; + border-bottom-color: #999; + border-bottom-color: rgba(0, 0, 0, .25); +} +.popover.bottom > .arrow:after { + top: 1px; + margin-left: -10px; + content: " "; + border-top-width: 0; + border-bottom-color: #fff; +} +.popover.left > .arrow { + top: 50%; + right: -11px; + margin-top: -11px; + border-right-width: 0; + border-left-color: #999; + border-left-color: rgba(0, 0, 0, .25); +} +.popover.left > .arrow:after { + right: 1px; + bottom: -10px; + content: " "; + border-right-width: 0; + border-left-color: #fff; +} +.carousel { + position: relative; +} +.carousel-inner { + position: relative; + width: 100%; + overflow: hidden; +} +.carousel-inner > .item { + position: relative; + display: none; + -webkit-transition: .6s ease-in-out left; + -o-transition: .6s ease-in-out left; + transition: .6s ease-in-out left; +} +.carousel-inner > .item > img, +.carousel-inner > .item > a > img { + line-height: 1; +} +@media all and (transform-3d), (-webkit-transform-3d) { + .carousel-inner > .item { + -webkit-transition: -webkit-transform .6s ease-in-out; + -o-transition: -o-transform .6s ease-in-out; + transition: transform .6s ease-in-out; + + -webkit-backface-visibility: hidden; + backface-visibility: hidden; + -webkit-perspective: 1000px; + perspective: 1000px; + } + .carousel-inner > .item.next, + .carousel-inner > .item.active.right { + left: 0; + -webkit-transform: translate3d(100%, 0, 0); + transform: translate3d(100%, 0, 0); + } + .carousel-inner > .item.prev, + .carousel-inner > .item.active.left { + left: 0; + -webkit-transform: translate3d(-100%, 0, 0); + transform: translate3d(-100%, 0, 0); + } + .carousel-inner > .item.next.left, + .carousel-inner > .item.prev.right, + .carousel-inner > .item.active { + left: 0; + -webkit-transform: translate3d(0, 0, 0); + transform: translate3d(0, 0, 0); + } +} +.carousel-inner > .active, +.carousel-inner > .next, +.carousel-inner > .prev { + display: block; +} +.carousel-inner > .active { + left: 0; +} +.carousel-inner > .next, +.carousel-inner > .prev { + position: absolute; + top: 0; + width: 100%; +} +.carousel-inner > .next { + left: 100%; +} +.carousel-inner > .prev { + left: -100%; +} +.carousel-inner > .next.left, +.carousel-inner > .prev.right { + left: 0; +} +.carousel-inner > .active.left { + left: -100%; +} +.carousel-inner > .active.right { + left: 100%; +} +.carousel-control { + position: absolute; + top: 0; + bottom: 0; + left: 0; + width: 15%; + font-size: 20px; + color: #fff; + text-align: center; + text-shadow: 0 1px 2px rgba(0, 0, 0, .6); + filter: alpha(opacity=50); + opacity: .5; +} +.carousel-control.left { + background-image: -webkit-linear-gradient(left, rgba(0, 0, 0, .5) 0%, rgba(0, 0, 0, .0001) 100%); + background-image: -o-linear-gradient(left, rgba(0, 0, 0, .5) 0%, rgba(0, 0, 0, .0001) 100%); + background-image: -webkit-gradient(linear, left top, right top, from(rgba(0, 0, 0, .5)), to(rgba(0, 0, 0, .0001))); + background-image: linear-gradient(to right, rgba(0, 0, 0, .5) 0%, rgba(0, 0, 0, .0001) 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#80000000', endColorstr='#00000000', GradientType=1); + background-repeat: repeat-x; +} +.carousel-control.right { + right: 0; + left: auto; + background-image: -webkit-linear-gradient(left, rgba(0, 0, 0, .0001) 0%, rgba(0, 0, 0, .5) 100%); + background-image: -o-linear-gradient(left, rgba(0, 0, 0, .0001) 0%, rgba(0, 0, 0, .5) 100%); + background-image: -webkit-gradient(linear, left top, right top, from(rgba(0, 0, 0, .0001)), to(rgba(0, 0, 0, .5))); + background-image: linear-gradient(to right, rgba(0, 0, 0, .0001) 0%, rgba(0, 0, 0, .5) 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#00000000', endColorstr='#80000000', GradientType=1); + background-repeat: repeat-x; +} +.carousel-control:hover, +.carousel-control:focus { + color: #fff; + text-decoration: none; + filter: alpha(opacity=90); + outline: 0; + opacity: .9; +} +.carousel-control .icon-prev, +.carousel-control .icon-next, +.carousel-control .glyphicon-chevron-left, +.carousel-control .glyphicon-chevron-right { + position: absolute; + top: 50%; + z-index: 5; + display: inline-block; + margin-top: -10px; +} +.carousel-control .icon-prev, +.carousel-control .glyphicon-chevron-left { + left: 50%; + margin-left: -10px; +} +.carousel-control .icon-next, +.carousel-control .glyphicon-chevron-right { + right: 50%; + margin-right: -10px; +} +.carousel-control .icon-prev, +.carousel-control .icon-next { + width: 20px; + height: 20px; + font-family: serif; + line-height: 1; +} +.carousel-control .icon-prev:before { + content: '\2039'; +} +.carousel-control .icon-next:before { + content: '\203a'; +} +.carousel-indicators { + position: absolute; + bottom: 10px; + left: 50%; + z-index: 15; + width: 60%; + padding-left: 0; + margin-left: -30%; + text-align: center; + list-style: none; +} +.carousel-indicators li { + display: inline-block; + width: 10px; + height: 10px; + margin: 1px; + text-indent: -999px; + cursor: pointer; + background-color: #000 \9; + background-color: rgba(0, 0, 0, 0); + border: 1px solid #fff; + border-radius: 10px; +} +.carousel-indicators .active { + width: 12px; + height: 12px; + margin: 0; + background-color: #fff; +} +.carousel-caption { + position: absolute; + right: 15%; + bottom: 20px; + left: 15%; + z-index: 10; + padding-top: 20px; + padding-bottom: 20px; + color: #fff; + text-align: center; + text-shadow: 0 1px 2px rgba(0, 0, 0, .6); +} +.carousel-caption .btn { + text-shadow: none; +} +@media screen and (min-width: 768px) { + .carousel-control .glyphicon-chevron-left, + .carousel-control .glyphicon-chevron-right, + .carousel-control .icon-prev, + .carousel-control .icon-next { + width: 30px; + height: 30px; + margin-top: -15px; + font-size: 30px; + } + .carousel-control .glyphicon-chevron-left, + .carousel-control .icon-prev { + margin-left: -15px; + } + .carousel-control .glyphicon-chevron-right, + .carousel-control .icon-next { + margin-right: -15px; + } + .carousel-caption { + right: 20%; + left: 20%; + padding-bottom: 30px; + } + .carousel-indicators { + bottom: 20px; + } +} +.clearfix:before, +.clearfix:after, +.dl-horizontal dd:before, +.dl-horizontal dd:after, +.container:before, +.container:after, +.container-fluid:before, +.container-fluid:after, +.row:before, +.row:after, +.form-horizontal .form-group:before, +.form-horizontal .form-group:after, +.btn-toolbar:before, +.btn-toolbar:after, +.btn-group-vertical > .btn-group:before, +.btn-group-vertical > .btn-group:after, +.nav:before, +.nav:after, +.navbar:before, +.navbar:after, +.navbar-header:before, +.navbar-header:after, +.navbar-collapse:before, +.navbar-collapse:after, +.pager:before, +.pager:after, +.panel-body:before, +.panel-body:after, +.modal-footer:before, +.modal-footer:after { + display: table; + content: " "; +} +.clearfix:after, +.dl-horizontal dd:after, +.container:after, +.container-fluid:after, +.row:after, +.form-horizontal .form-group:after, +.btn-toolbar:after, +.btn-group-vertical > .btn-group:after, +.nav:after, +.navbar:after, +.navbar-header:after, +.navbar-collapse:after, +.pager:after, +.panel-body:after, +.modal-footer:after { + clear: both; +} +.center-block { + display: block; + margin-right: auto; + margin-left: auto; +} +.pull-right { + float: right !important; +} +.pull-left { + float: left !important; +} +.hide { + display: none !important; +} +.show { + display: block !important; +} +.invisible { + visibility: hidden; +} +.text-hide { + font: 0/0 a; + color: transparent; + text-shadow: none; + background-color: transparent; + border: 0; +} +.hidden { + display: none !important; +} +.affix { + position: fixed; +} +@-ms-viewport { + width: device-width; +} +.visible-xs, +.visible-sm, +.visible-md, +.visible-lg { + display: none !important; +} +.visible-xs-block, +.visible-xs-inline, +.visible-xs-inline-block, +.visible-sm-block, +.visible-sm-inline, +.visible-sm-inline-block, +.visible-md-block, +.visible-md-inline, +.visible-md-inline-block, +.visible-lg-block, +.visible-lg-inline, +.visible-lg-inline-block { + display: none !important; +} +@media (max-width: 767px) { + .visible-xs { + display: block !important; + } + table.visible-xs { + display: table !important; + } + tr.visible-xs { + display: table-row !important; + } + th.visible-xs, + td.visible-xs { + display: table-cell !important; + } +} +@media (max-width: 767px) { + .visible-xs-block { + display: block !important; + } +} +@media (max-width: 767px) { + .visible-xs-inline { + display: inline !important; + } +} +@media (max-width: 767px) { + .visible-xs-inline-block { + display: inline-block !important; + } +} +@media (min-width: 768px) and (max-width: 991px) { + .visible-sm { + display: block !important; + } + table.visible-sm { + display: table !important; + } + tr.visible-sm { + display: table-row !important; + } + th.visible-sm, + td.visible-sm { + display: table-cell !important; + } +} +@media (min-width: 768px) and (max-width: 991px) { + .visible-sm-block { + display: block !important; + } +} +@media (min-width: 768px) and (max-width: 991px) { + .visible-sm-inline { + display: inline !important; + } +} +@media (min-width: 768px) and (max-width: 991px) { + .visible-sm-inline-block { + display: inline-block !important; + } +} +@media (min-width: 992px) and (max-width: 1199px) { + .visible-md { + display: block !important; + } + table.visible-md { + display: table !important; + } + tr.visible-md { + display: table-row !important; + } + th.visible-md, + td.visible-md { + display: table-cell !important; + } +} +@media (min-width: 992px) and (max-width: 1199px) { + .visible-md-block { + display: block !important; + } +} +@media (min-width: 992px) and (max-width: 1199px) { + .visible-md-inline { + display: inline !important; + } +} +@media (min-width: 992px) and (max-width: 1199px) { + .visible-md-inline-block { + display: inline-block !important; + } +} +@media (min-width: 1200px) { + .visible-lg { + display: block !important; + } + table.visible-lg { + display: table !important; + } + tr.visible-lg { + display: table-row !important; + } + th.visible-lg, + td.visible-lg { + display: table-cell !important; + } +} +@media (min-width: 1200px) { + .visible-lg-block { + display: block !important; + } +} +@media (min-width: 1200px) { + .visible-lg-inline { + display: inline !important; + } +} +@media (min-width: 1200px) { + .visible-lg-inline-block { + display: inline-block !important; + } +} +@media (max-width: 767px) { + .hidden-xs { + display: none !important; + } +} +@media (min-width: 768px) and (max-width: 991px) { + .hidden-sm { + display: none !important; + } +} +@media (min-width: 992px) and (max-width: 1199px) { + .hidden-md { + display: none !important; + } +} +@media (min-width: 1200px) { + .hidden-lg { + display: none !important; + } +} +.visible-print { + display: none !important; +} +@media print { + .visible-print { + display: block !important; + } + table.visible-print { + display: table !important; + } + tr.visible-print { + display: table-row !important; + } + th.visible-print, + td.visible-print { + display: table-cell !important; + } +} +.visible-print-block { + display: none !important; +} +@media print { + .visible-print-block { + display: block !important; + } +} +.visible-print-inline { + display: none !important; +} +@media print { + .visible-print-inline { + display: inline !important; + } +} +.visible-print-inline-block { + display: none !important; +} +@media print { + .visible-print-inline-block { + display: inline-block !important; + } +} +@media print { + .hidden-print { + display: none !important; + } +} +/*# sourceMappingURL=bootstrap.css.map */ diff --git a/OpenAuth.Identity/wwwroot/lib/bootstrap/css/bootstrap.css.map b/OpenAuth.Identity/wwwroot/lib/bootstrap/css/bootstrap.css.map new file mode 100644 index 0000000..9f60ed2 --- /dev/null +++ b/OpenAuth.Identity/wwwroot/lib/bootstrap/css/bootstrap.css.map @@ -0,0 +1 @@ +{"version":3,"sources":["bootstrap.css","less/normalize.less","less/print.less","less/glyphicons.less","less/scaffolding.less","less/mixins/vendor-prefixes.less","less/mixins/tab-focus.less","less/mixins/image.less","less/type.less","less/mixins/text-emphasis.less","less/mixins/background-variant.less","less/mixins/text-overflow.less","less/code.less","less/grid.less","less/mixins/grid.less","less/mixins/grid-framework.less","less/tables.less","less/mixins/table-row.less","less/forms.less","less/mixins/forms.less","less/buttons.less","less/mixins/buttons.less","less/mixins/opacity.less","less/component-animations.less","less/dropdowns.less","less/mixins/nav-divider.less","less/mixins/reset-filter.less","less/button-groups.less","less/mixins/border-radius.less","less/input-groups.less","less/navs.less","less/navbar.less","less/mixins/nav-vertical-align.less","less/utilities.less","less/breadcrumbs.less","less/pagination.less","less/mixins/pagination.less","less/pager.less","less/labels.less","less/mixins/labels.less","less/badges.less","less/jumbotron.less","less/thumbnails.less","less/alerts.less","less/mixins/alerts.less","less/progress-bars.less","less/mixins/gradients.less","less/mixins/progress-bar.less","less/media.less","less/list-group.less","less/mixins/list-group.less","less/panels.less","less/mixins/panels.less","less/responsive-embed.less","less/wells.less","less/close.less","less/modals.less","less/tooltip.less","less/mixins/reset-text.less","less/popovers.less","less/carousel.less","less/mixins/clearfix.less","less/mixins/center-block.less","less/mixins/hide-text.less","less/responsive-utilities.less","less/mixins/responsive-visibility.less"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,4EAA4E;ACG5E;EACE,wBAAA;EACA,2BAAA;EACA,+BAAA;CDDD;ACQD;EACE,UAAA;CDND;ACmBD;;;;;;;;;;;;;EAaE,eAAA;CDjBD;ACyBD;;;;EAIE,sBAAA;EACA,yBAAA;CDvBD;AC+BD;EACE,cAAA;EACA,UAAA;CD7BD;ACqCD;;EAEE,cAAA;CDnCD;AC6CD;EACE,8BAAA;CD3CD;ACmDD;;EAEE,WAAA;CDjDD;AC2DD;EACE,0BAAA;CDzDD;ACgED;;EAEE,kBAAA;CD9DD;ACqED;EACE,mBAAA;CDnED;AC2ED;EACE,eAAA;EACA,iBAAA;CDzED;ACgFD;EACE,iBAAA;EACA,YAAA;CD9ED;ACqFD;EACE,eAAA;CDnFD;AC0FD;;EAEE,eAAA;EACA,eAAA;EACA,mBAAA;EACA,yBAAA;CDxFD;AC2FD;EACE,YAAA;CDzFD;AC4FD;EACE,gBAAA;CD1FD;ACoGD;EACE,UAAA;CDlGD;ACyGD;EACE,iBAAA;CDvGD;ACiHD;EACE,iBAAA;CD/GD;ACsHD;EACE,gCAAA;KAAA,6BAAA;UAAA,wBAAA;EACA,UAAA;CDpHD;AC2HD;EACE,eAAA;CDzHD;ACgID;;;;EAIE,kCAAA;EACA,eAAA;CD9HD;ACgJD;;;;;EAKE,eAAA;EACA,cAAA;EACA,UAAA;CD9ID;ACqJD;EACE,kBAAA;CDnJD;AC6JD;;EAEE,qBAAA;CD3JD;ACsKD;;;;EAIE,2BAAA;EACA,gBAAA;CDpKD;AC2KD;;EAEE,gBAAA;CDzKD;ACgLD;;EAEE,UAAA;EACA,WAAA;CD9KD;ACsLD;EACE,oBAAA;CDpLD;AC+LD;;EAEE,+BAAA;KAAA,4BAAA;UAAA,uBAAA;EACA,WAAA;CD7LD;ACsMD;;EAEE,aAAA;CDpMD;AC4MD;EACE,8BAAA;EACA,gCAAA;KAAA,6BAAA;UAAA,wBAAA;CD1MD;ACmND;;EAEE,yBAAA;CDjND;ACwND;EACE,0BAAA;EACA,cAAA;EACA,+BAAA;CDtND;AC8ND;EACE,UAAA;EACA,WAAA;CD5ND;ACmOD;EACE,eAAA;CDjOD;ACyOD;EACE,kBAAA;CDvOD;ACiPD;EACE,0BAAA;EACA,kBAAA;CD/OD;ACkPD;;EAEE,WAAA;CDhPD;AACD,qFAAqF;AElFrF;EA7FI;;;IAGI,mCAAA;IACA,uBAAA;IACA,oCAAA;YAAA,4BAAA;IACA,6BAAA;GFkLL;EE/KC;;IAEI,2BAAA;GFiLL;EE9KC;IACI,6BAAA;GFgLL;EE7KC;IACI,8BAAA;GF+KL;EE1KC;;IAEI,YAAA;GF4KL;EEzKC;;IAEI,uBAAA;IACA,yBAAA;GF2KL;EExKC;IACI,4BAAA;GF0KL;EEvKC;;IAEI,yBAAA;GFyKL;EEtKC;IACI,2BAAA;GFwKL;EErKC;;;IAGI,WAAA;IACA,UAAA;GFuKL;EEpKC;;IAEI,wBAAA;GFsKL;EEhKC;IACI,cAAA;GFkKL;EEhKC;;IAGQ,kCAAA;GFiKT;EE9JC;IACI,uBAAA;GFgKL;EE7JC;IACI,qCAAA;GF+JL;EEhKC;;IAKQ,kCAAA;GF+JT;EE5JC;;IAGQ,kCAAA;GF6JT;CACF;AGnPD;EACE,oCAAA;EACA,sDAAA;EACA,gYAAA;CHqPD;AG7OD;EACE,mBAAA;EACA,SAAA;EACA,sBAAA;EACA,oCAAA;EACA,mBAAA;EACA,oBAAA;EACA,eAAA;EACA,oCAAA;EACA,mCAAA;CH+OD;AG3OmC;EAAW,eAAA;CH8O9C;AG7OmC;EAAW,eAAA;CHgP9C;AG9OmC;;EAAW,iBAAA;CHkP9C;AGjPmC;EAAW,iBAAA;CHoP9C;AGnPmC;EAAW,iBAAA;CHsP9C;AGrPmC;EAAW,iBAAA;CHwP9C;AGvPmC;EAAW,iBAAA;CH0P9C;AGzPmC;EAAW,iBAAA;CH4P9C;AG3PmC;EAAW,iBAAA;CH8P9C;AG7PmC;EAAW,iBAAA;CHgQ9C;AG/PmC;EAAW,iBAAA;CHkQ9C;AGjQmC;EAAW,iBAAA;CHoQ9C;AGnQmC;EAAW,iBAAA;CHsQ9C;AGrQmC;EAAW,iBAAA;CHwQ9C;AGvQmC;EAAW,iBAAA;CH0Q9C;AGzQmC;EAAW,iBAAA;CH4Q9C;AG3QmC;EAAW,iBAAA;CH8Q9C;AG7QmC;EAAW,iBAAA;CHgR9C;AG/QmC;EAAW,iBAAA;CHkR9C;AGjRmC;EAAW,iBAAA;CHoR9C;AGnRmC;EAAW,iBAAA;CHsR9C;AGrRmC;EAAW,iBAAA;CHwR9C;AGvRmC;EAAW,iBAAA;CH0R9C;AGzRmC;EAAW,iBAAA;CH4R9C;AG3RmC;EAAW,iBAAA;CH8R9C;AG7RmC;EAAW,iBAAA;CHgS9C;AG/RmC;EAAW,iBAAA;CHkS9C;AGjSmC;EAAW,iBAAA;CHoS9C;AGnSmC;EAAW,iBAAA;CHsS9C;AGrSmC;EAAW,iBAAA;CHwS9C;AGvSmC;EAAW,iBAAA;CH0S9C;AGzSmC;EAAW,iBAAA;CH4S9C;AG3SmC;EAAW,iBAAA;CH8S9C;AG7SmC;EAAW,iBAAA;CHgT9C;AG/SmC;EAAW,iBAAA;CHkT9C;AGjTmC;EAAW,iBAAA;CHoT9C;AGnTmC;EAAW,iBAAA;CHsT9C;AGrTmC;EAAW,iBAAA;CHwT9C;AGvTmC;EAAW,iBAAA;CH0T9C;AGzTmC;EAAW,iBAAA;CH4T9C;AG3TmC;EAAW,iBAAA;CH8T9C;AG7TmC;EAAW,iBAAA;CHgU9C;AG/TmC;EAAW,iBAAA;CHkU9C;AGjUmC;EAAW,iBAAA;CHoU9C;AGnUmC;EAAW,iBAAA;CHsU9C;AGrUmC;EAAW,iBAAA;CHwU9C;AGvUmC;EAAW,iBAAA;CH0U9C;AGzUmC;EAAW,iBAAA;CH4U9C;AG3UmC;EAAW,iBAAA;CH8U9C;AG7UmC;EAAW,iBAAA;CHgV9C;AG/UmC;EAAW,iBAAA;CHkV9C;AGjVmC;EAAW,iBAAA;CHoV9C;AGnVmC;EAAW,iBAAA;CHsV9C;AGrVmC;EAAW,iBAAA;CHwV9C;AGvVmC;EAAW,iBAAA;CH0V9C;AGzVmC;EAAW,iBAAA;CH4V9C;AG3VmC;EAAW,iBAAA;CH8V9C;AG7VmC;EAAW,iBAAA;CHgW9C;AG/VmC;EAAW,iBAAA;CHkW9C;AGjWmC;EAAW,iBAAA;CHoW9C;AGnWmC;EAAW,iBAAA;CHsW9C;AGrWmC;EAAW,iBAAA;CHwW9C;AGvWmC;EAAW,iBAAA;CH0W9C;AGzWmC;EAAW,iBAAA;CH4W9C;AG3WmC;EAAW,iBAAA;CH8W9C;AG7WmC;EAAW,iBAAA;CHgX9C;AG/WmC;EAAW,iBAAA;CHkX9C;AGjXmC;EAAW,iBAAA;CHoX9C;AGnXmC;EAAW,iBAAA;CHsX9C;AGrXmC;EAAW,iBAAA;CHwX9C;AGvXmC;EAAW,iBAAA;CH0X9C;AGzXmC;EAAW,iBAAA;CH4X9C;AG3XmC;EAAW,iBAAA;CH8X9C;AG7XmC;EAAW,iBAAA;CHgY9C;AG/XmC;EAAW,iBAAA;CHkY9C;AGjYmC;EAAW,iBAAA;CHoY9C;AGnYmC;EAAW,iBAAA;CHsY9C;AGrYmC;EAAW,iBAAA;CHwY9C;AGvYmC;EAAW,iBAAA;CH0Y9C;AGzYmC;EAAW,iBAAA;CH4Y9C;AG3YmC;EAAW,iBAAA;CH8Y9C;AG7YmC;EAAW,iBAAA;CHgZ9C;AG/YmC;EAAW,iBAAA;CHkZ9C;AGjZmC;EAAW,iBAAA;CHoZ9C;AGnZmC;EAAW,iBAAA;CHsZ9C;AGrZmC;EAAW,iBAAA;CHwZ9C;AGvZmC;EAAW,iBAAA;CH0Z9C;AGzZmC;EAAW,iBAAA;CH4Z9C;AG3ZmC;EAAW,iBAAA;CH8Z9C;AG7ZmC;EAAW,iBAAA;CHga9C;AG/ZmC;EAAW,iBAAA;CHka9C;AGjamC;EAAW,iBAAA;CHoa9C;AGnamC;EAAW,iBAAA;CHsa9C;AGramC;EAAW,iBAAA;CHwa9C;AGvamC;EAAW,iBAAA;CH0a9C;AGzamC;EAAW,iBAAA;CH4a9C;AG3amC;EAAW,iBAAA;CH8a9C;AG7amC;EAAW,iBAAA;CHgb9C;AG/amC;EAAW,iBAAA;CHkb9C;AGjbmC;EAAW,iBAAA;CHob9C;AGnbmC;EAAW,iBAAA;CHsb9C;AGrbmC;EAAW,iBAAA;CHwb9C;AGvbmC;EAAW,iBAAA;CH0b9C;AGzbmC;EAAW,iBAAA;CH4b9C;AG3bmC;EAAW,iBAAA;CH8b9C;AG7bmC;EAAW,iBAAA;CHgc9C;AG/bmC;EAAW,iBAAA;CHkc9C;AGjcmC;EAAW,iBAAA;CHoc9C;AGncmC;EAAW,iBAAA;CHsc9C;AGrcmC;EAAW,iBAAA;CHwc9C;AGvcmC;EAAW,iBAAA;CH0c9C;AGzcmC;EAAW,iBAAA;CH4c9C;AG3cmC;EAAW,iBAAA;CH8c9C;AG7cmC;EAAW,iBAAA;CHgd9C;AG/cmC;EAAW,iBAAA;CHkd9C;AGjdmC;EAAW,iBAAA;CHod9C;AGndmC;EAAW,iBAAA;CHsd9C;AGrdmC;EAAW,iBAAA;CHwd9C;AGvdmC;EAAW,iBAAA;CH0d9C;AGzdmC;EAAW,iBAAA;CH4d9C;AG3dmC;EAAW,iBAAA;CH8d9C;AG7dmC;EAAW,iBAAA;CHge9C;AG/dmC;EAAW,iBAAA;CHke9C;AGjemC;EAAW,iBAAA;CHoe9C;AGnemC;EAAW,iBAAA;CHse9C;AGremC;EAAW,iBAAA;CHwe9C;AGvemC;EAAW,iBAAA;CH0e9C;AGzemC;EAAW,iBAAA;CH4e9C;AG3emC;EAAW,iBAAA;CH8e9C;AG7emC;EAAW,iBAAA;CHgf9C;AG/emC;EAAW,iBAAA;CHkf9C;AGjfmC;EAAW,iBAAA;CHof9C;AGnfmC;EAAW,iBAAA;CHsf9C;AGrfmC;EAAW,iBAAA;CHwf9C;AGvfmC;EAAW,iBAAA;CH0f9C;AGzfmC;EAAW,iBAAA;CH4f9C;AG3fmC;EAAW,iBAAA;CH8f9C;AG7fmC;EAAW,iBAAA;CHggB9C;AG/fmC;EAAW,iBAAA;CHkgB9C;AGjgBmC;EAAW,iBAAA;CHogB9C;AGngBmC;EAAW,iBAAA;CHsgB9C;AGrgBmC;EAAW,iBAAA;CHwgB9C;AGvgBmC;EAAW,iBAAA;CH0gB9C;AGzgBmC;EAAW,iBAAA;CH4gB9C;AG3gBmC;EAAW,iBAAA;CH8gB9C;AG7gBmC;EAAW,iBAAA;CHghB9C;AG/gBmC;EAAW,iBAAA;CHkhB9C;AGjhBmC;EAAW,iBAAA;CHohB9C;AGnhBmC;EAAW,iBAAA;CHshB9C;AGrhBmC;EAAW,iBAAA;CHwhB9C;AGvhBmC;EAAW,iBAAA;CH0hB9C;AGzhBmC;EAAW,iBAAA;CH4hB9C;AG3hBmC;EAAW,iBAAA;CH8hB9C;AG7hBmC;EAAW,iBAAA;CHgiB9C;AG/hBmC;EAAW,iBAAA;CHkiB9C;AGjiBmC;EAAW,iBAAA;CHoiB9C;AGniBmC;EAAW,iBAAA;CHsiB9C;AGriBmC;EAAW,iBAAA;CHwiB9C;AGviBmC;EAAW,iBAAA;CH0iB9C;AGziBmC;EAAW,iBAAA;CH4iB9C;AG3iBmC;EAAW,iBAAA;CH8iB9C;AG7iBmC;EAAW,iBAAA;CHgjB9C;AG/iBmC;EAAW,iBAAA;CHkjB9C;AGjjBmC;EAAW,iBAAA;CHojB9C;AGnjBmC;EAAW,iBAAA;CHsjB9C;AGrjBmC;EAAW,iBAAA;CHwjB9C;AGvjBmC;EAAW,iBAAA;CH0jB9C;AGzjBmC;EAAW,iBAAA;CH4jB9C;AG3jBmC;EAAW,iBAAA;CH8jB9C;AG7jBmC;EAAW,iBAAA;CHgkB9C;AG/jBmC;EAAW,iBAAA;CHkkB9C;AGjkBmC;EAAW,iBAAA;CHokB9C;AGnkBmC;EAAW,iBAAA;CHskB9C;AGrkBmC;EAAW,iBAAA;CHwkB9C;AGvkBmC;EAAW,iBAAA;CH0kB9C;AGzkBmC;EAAW,iBAAA;CH4kB9C;AG3kBmC;EAAW,iBAAA;CH8kB9C;AG7kBmC;EAAW,iBAAA;CHglB9C;AG/kBmC;EAAW,iBAAA;CHklB9C;AGjlBmC;EAAW,iBAAA;CHolB9C;AGnlBmC;EAAW,iBAAA;CHslB9C;AGrlBmC;EAAW,iBAAA;CHwlB9C;AGvlBmC;EAAW,iBAAA;CH0lB9C;AGzlBmC;EAAW,iBAAA;CH4lB9C;AG3lBmC;EAAW,iBAAA;CH8lB9C;AG7lBmC;EAAW,iBAAA;CHgmB9C;AG/lBmC;EAAW,iBAAA;CHkmB9C;AGjmBmC;EAAW,iBAAA;CHomB9C;AGnmBmC;EAAW,iBAAA;CHsmB9C;AGrmBmC;EAAW,iBAAA;CHwmB9C;AGvmBmC;EAAW,iBAAA;CH0mB9C;AGzmBmC;EAAW,iBAAA;CH4mB9C;AG3mBmC;EAAW,iBAAA;CH8mB9C;AG7mBmC;EAAW,iBAAA;CHgnB9C;AG/mBmC;EAAW,iBAAA;CHknB9C;AGjnBmC;EAAW,iBAAA;CHonB9C;AGnnBmC;EAAW,iBAAA;CHsnB9C;AGrnBmC;EAAW,iBAAA;CHwnB9C;AGvnBmC;EAAW,iBAAA;CH0nB9C;AGznBmC;EAAW,iBAAA;CH4nB9C;AG3nBmC;EAAW,iBAAA;CH8nB9C;AG7nBmC;EAAW,iBAAA;CHgoB9C;AG/nBmC;EAAW,iBAAA;CHkoB9C;AGjoBmC;EAAW,iBAAA;CHooB9C;AGnoBmC;EAAW,iBAAA;CHsoB9C;AGroBmC;EAAW,iBAAA;CHwoB9C;AG/nBmC;EAAW,iBAAA;CHkoB9C;AGjoBmC;EAAW,iBAAA;CHooB9C;AGnoBmC;EAAW,iBAAA;CHsoB9C;AGroBmC;EAAW,iBAAA;CHwoB9C;AGvoBmC;EAAW,iBAAA;CH0oB9C;AGzoBmC;EAAW,iBAAA;CH4oB9C;AG3oBmC;EAAW,iBAAA;CH8oB9C;AG7oBmC;EAAW,iBAAA;CHgpB9C;AG/oBmC;EAAW,iBAAA;CHkpB9C;AGjpBmC;EAAW,iBAAA;CHopB9C;AGnpBmC;EAAW,iBAAA;CHspB9C;AGrpBmC;EAAW,iBAAA;CHwpB9C;AGvpBmC;EAAW,iBAAA;CH0pB9C;AGzpBmC;EAAW,iBAAA;CH4pB9C;AG3pBmC;EAAW,iBAAA;CH8pB9C;AG7pBmC;EAAW,iBAAA;CHgqB9C;AG/pBmC;EAAW,iBAAA;CHkqB9C;AGjqBmC;EAAW,iBAAA;CHoqB9C;AGnqBmC;EAAW,iBAAA;CHsqB9C;AGrqBmC;EAAW,iBAAA;CHwqB9C;AGvqBmC;EAAW,iBAAA;CH0qB9C;AGzqBmC;EAAW,iBAAA;CH4qB9C;AG3qBmC;EAAW,iBAAA;CH8qB9C;AG7qBmC;EAAW,iBAAA;CHgrB9C;AG/qBmC;EAAW,iBAAA;CHkrB9C;AGjrBmC;EAAW,iBAAA;CHorB9C;AGnrBmC;EAAW,iBAAA;CHsrB9C;AGrrBmC;EAAW,iBAAA;CHwrB9C;AGvrBmC;EAAW,iBAAA;CH0rB9C;AGzrBmC;EAAW,iBAAA;CH4rB9C;AG3rBmC;EAAW,iBAAA;CH8rB9C;AG7rBmC;EAAW,iBAAA;CHgsB9C;AG/rBmC;EAAW,iBAAA;CHksB9C;AGjsBmC;EAAW,iBAAA;CHosB9C;AGnsBmC;EAAW,iBAAA;CHssB9C;AGrsBmC;EAAW,iBAAA;CHwsB9C;AGvsBmC;EAAW,iBAAA;CH0sB9C;AGzsBmC;EAAW,iBAAA;CH4sB9C;AG3sBmC;EAAW,iBAAA;CH8sB9C;AG7sBmC;EAAW,iBAAA;CHgtB9C;AG/sBmC;EAAW,iBAAA;CHktB9C;AGjtBmC;EAAW,iBAAA;CHotB9C;AGntBmC;EAAW,iBAAA;CHstB9C;AGrtBmC;EAAW,iBAAA;CHwtB9C;AGvtBmC;EAAW,iBAAA;CH0tB9C;AGztBmC;EAAW,iBAAA;CH4tB9C;AG3tBmC;EAAW,iBAAA;CH8tB9C;AG7tBmC;EAAW,iBAAA;CHguB9C;AG/tBmC;EAAW,iBAAA;CHkuB9C;AGjuBmC;EAAW,iBAAA;CHouB9C;AGnuBmC;EAAW,iBAAA;CHsuB9C;AGruBmC;EAAW,iBAAA;CHwuB9C;AGvuBmC;EAAW,iBAAA;CH0uB9C;AGzuBmC;EAAW,iBAAA;CH4uB9C;AG3uBmC;EAAW,iBAAA;CH8uB9C;AG7uBmC;EAAW,iBAAA;CHgvB9C;AIthCD;ECgEE,+BAAA;EACG,4BAAA;EACK,uBAAA;CLy9BT;AIxhCD;;EC6DE,+BAAA;EACG,4BAAA;EACK,uBAAA;CL+9BT;AIthCD;EACE,gBAAA;EACA,8CAAA;CJwhCD;AIrhCD;EACE,4DAAA;EACA,gBAAA;EACA,wBAAA;EACA,eAAA;EACA,0BAAA;CJuhCD;AInhCD;;;;EAIE,qBAAA;EACA,mBAAA;EACA,qBAAA;CJqhCD;AI/gCD;EACE,eAAA;EACA,sBAAA;CJihCD;AI/gCC;;EAEE,eAAA;EACA,2BAAA;CJihCH;AI9gCC;EErDA,qBAAA;EAEA,2CAAA;EACA,qBAAA;CNqkCD;AIxgCD;EACE,UAAA;CJ0gCD;AIpgCD;EACE,uBAAA;CJsgCD;AIlgCD;;;;;EGvEE,eAAA;EACA,gBAAA;EACA,aAAA;CPglCD;AItgCD;EACE,mBAAA;CJwgCD;AIlgCD;EACE,aAAA;EACA,wBAAA;EACA,0BAAA;EACA,0BAAA;EACA,mBAAA;EC6FA,yCAAA;EACK,oCAAA;EACG,iCAAA;EEvLR,sBAAA;EACA,gBAAA;EACA,aAAA;CPgmCD;AIlgCD;EACE,mBAAA;CJogCD;AI9/BD;EACE,iBAAA;EACA,oBAAA;EACA,UAAA;EACA,8BAAA;CJggCD;AIx/BD;EACE,mBAAA;EACA,WAAA;EACA,YAAA;EACA,aAAA;EACA,WAAA;EACA,iBAAA;EACA,uBAAA;EACA,UAAA;CJ0/BD;AIl/BC;;EAEE,iBAAA;EACA,YAAA;EACA,aAAA;EACA,UAAA;EACA,kBAAA;EACA,WAAA;CJo/BH;AIz+BD;EACE,gBAAA;CJ2+BD;AQloCD;;;;;;;;;;;;EAEE,qBAAA;EACA,iBAAA;EACA,iBAAA;EACA,eAAA;CR8oCD;AQnpCD;;;;;;;;;;;;;;;;;;;;;;;;EASI,oBAAA;EACA,eAAA;EACA,eAAA;CRoqCH;AQhqCD;;;;;;EAGE,iBAAA;EACA,oBAAA;CRqqCD;AQzqCD;;;;;;;;;;;;EAQI,eAAA;CR+qCH;AQ5qCD;;;;;;EAGE,iBAAA;EACA,oBAAA;CRirCD;AQrrCD;;;;;;;;;;;;EAQI,eAAA;CR2rCH;AQvrCD;;EAAU,gBAAA;CR2rCT;AQ1rCD;;EAAU,gBAAA;CR8rCT;AQ7rCD;;EAAU,gBAAA;CRisCT;AQhsCD;;EAAU,gBAAA;CRosCT;AQnsCD;;EAAU,gBAAA;CRusCT;AQtsCD;;EAAU,gBAAA;CR0sCT;AQpsCD;EACE,iBAAA;CRssCD;AQnsCD;EACE,oBAAA;EACA,gBAAA;EACA,iBAAA;EACA,iBAAA;CRqsCD;AQhsCD;EAAA;IAFI,gBAAA;GRssCD;CACF;AQ9rCD;;EAEE,eAAA;CRgsCD;AQ7rCD;;EAEE,0BAAA;EACA,cAAA;CR+rCD;AQ3rCD;EAAuB,iBAAA;CR8rCtB;AQ7rCD;EAAuB,kBAAA;CRgsCtB;AQ/rCD;EAAuB,mBAAA;CRksCtB;AQjsCD;EAAuB,oBAAA;CRosCtB;AQnsCD;EAAuB,oBAAA;CRssCtB;AQnsCD;EAAuB,0BAAA;CRssCtB;AQrsCD;EAAuB,0BAAA;CRwsCtB;AQvsCD;EAAuB,2BAAA;CR0sCtB;AQvsCD;EACE,eAAA;CRysCD;AQvsCD;ECrGE,eAAA;CT+yCD;AS9yCC;;EAEE,eAAA;CTgzCH;AQ3sCD;ECxGE,eAAA;CTszCD;ASrzCC;;EAEE,eAAA;CTuzCH;AQ/sCD;EC3GE,eAAA;CT6zCD;AS5zCC;;EAEE,eAAA;CT8zCH;AQntCD;EC9GE,eAAA;CTo0CD;ASn0CC;;EAEE,eAAA;CTq0CH;AQvtCD;ECjHE,eAAA;CT20CD;AS10CC;;EAEE,eAAA;CT40CH;AQvtCD;EAGE,YAAA;EE3HA,0BAAA;CVm1CD;AUl1CC;;EAEE,0BAAA;CVo1CH;AQztCD;EE9HE,0BAAA;CV01CD;AUz1CC;;EAEE,0BAAA;CV21CH;AQ7tCD;EEjIE,0BAAA;CVi2CD;AUh2CC;;EAEE,0BAAA;CVk2CH;AQjuCD;EEpIE,0BAAA;CVw2CD;AUv2CC;;EAEE,0BAAA;CVy2CH;AQruCD;EEvIE,0BAAA;CV+2CD;AU92CC;;EAEE,0BAAA;CVg3CH;AQpuCD;EACE,oBAAA;EACA,oBAAA;EACA,iCAAA;CRsuCD;AQ9tCD;;EAEE,cAAA;EACA,oBAAA;CRguCD;AQnuCD;;;;EAMI,iBAAA;CRmuCH;AQ5tCD;EACE,gBAAA;EACA,iBAAA;CR8tCD;AQ1tCD;EALE,gBAAA;EACA,iBAAA;EAMA,kBAAA;CR6tCD;AQ/tCD;EAKI,sBAAA;EACA,kBAAA;EACA,mBAAA;CR6tCH;AQxtCD;EACE,cAAA;EACA,oBAAA;CR0tCD;AQxtCD;;EAEE,wBAAA;CR0tCD;AQxtCD;EACE,kBAAA;CR0tCD;AQxtCD;EACE,eAAA;CR0tCD;AQjsCD;EAAA;IAVM,YAAA;IACA,aAAA;IACA,YAAA;IACA,kBAAA;IGtNJ,iBAAA;IACA,wBAAA;IACA,oBAAA;GXs6CC;EQ3sCH;IAHM,mBAAA;GRitCH;CACF;AQxsCD;;EAGE,aAAA;EACA,kCAAA;CRysCD;AQvsCD;EACE,eAAA;EA9IqB,0BAAA;CRw1CtB;AQrsCD;EACE,mBAAA;EACA,iBAAA;EACA,kBAAA;EACA,+BAAA;CRusCD;AQlsCG;;;EACE,iBAAA;CRssCL;AQhtCD;;;EAmBI,eAAA;EACA,eAAA;EACA,wBAAA;EACA,eAAA;CRksCH;AQhsCG;;;EACE,uBAAA;CRosCL;AQ5rCD;;EAEE,oBAAA;EACA,gBAAA;EACA,gCAAA;EACA,eAAA;EACA,kBAAA;CR8rCD;AQxrCG;;;;;;EAAW,YAAA;CRgsCd;AQ/rCG;;;;;;EACE,uBAAA;CRssCL;AQhsCD;EACE,oBAAA;EACA,mBAAA;EACA,wBAAA;CRksCD;AYx+CD;;;;EAIE,+DAAA;CZ0+CD;AYt+CD;EACE,iBAAA;EACA,eAAA;EACA,eAAA;EACA,0BAAA;EACA,mBAAA;CZw+CD;AYp+CD;EACE,iBAAA;EACA,eAAA;EACA,eAAA;EACA,0BAAA;EACA,mBAAA;EACA,uDAAA;UAAA,+CAAA;CZs+CD;AY5+CD;EASI,WAAA;EACA,gBAAA;EACA,kBAAA;EACA,yBAAA;UAAA,iBAAA;CZs+CH;AYj+CD;EACE,eAAA;EACA,eAAA;EACA,iBAAA;EACA,gBAAA;EACA,wBAAA;EACA,sBAAA;EACA,sBAAA;EACA,eAAA;EACA,0BAAA;EACA,0BAAA;EACA,mBAAA;CZm+CD;AY9+CD;EAeI,WAAA;EACA,mBAAA;EACA,eAAA;EACA,sBAAA;EACA,8BAAA;EACA,iBAAA;CZk+CH;AY79CD;EACE,kBAAA;EACA,mBAAA;CZ+9CD;AazhDD;ECHE,mBAAA;EACA,kBAAA;EACA,mBAAA;EACA,oBAAA;Cd+hDD;AazhDC;EAAA;IAFE,aAAA;Gb+hDD;CACF;Aa3hDC;EAAA;IAFE,aAAA;GbiiDD;CACF;Aa7hDD;EAAA;IAFI,cAAA;GbmiDD;CACF;Aa1hDD;ECvBE,mBAAA;EACA,kBAAA;EACA,mBAAA;EACA,oBAAA;CdojDD;AavhDD;ECvBE,mBAAA;EACA,oBAAA;CdijDD;AejjDG;EACE,mBAAA;EAEA,gBAAA;EAEA,mBAAA;EACA,oBAAA;CfijDL;AejiDG;EACE,YAAA;CfmiDL;Ae5hDC;EACE,YAAA;Cf8hDH;Ae/hDC;EACE,oBAAA;CfiiDH;AeliDC;EACE,oBAAA;CfoiDH;AeriDC;EACE,WAAA;CfuiDH;AexiDC;EACE,oBAAA;Cf0iDH;Ae3iDC;EACE,oBAAA;Cf6iDH;Ae9iDC;EACE,WAAA;CfgjDH;AejjDC;EACE,oBAAA;CfmjDH;AepjDC;EACE,oBAAA;CfsjDH;AevjDC;EACE,WAAA;CfyjDH;Ae1jDC;EACE,oBAAA;Cf4jDH;Ae7jDC;EACE,mBAAA;Cf+jDH;AejjDC;EACE,YAAA;CfmjDH;AepjDC;EACE,oBAAA;CfsjDH;AevjDC;EACE,oBAAA;CfyjDH;Ae1jDC;EACE,WAAA;Cf4jDH;Ae7jDC;EACE,oBAAA;Cf+jDH;AehkDC;EACE,oBAAA;CfkkDH;AenkDC;EACE,WAAA;CfqkDH;AetkDC;EACE,oBAAA;CfwkDH;AezkDC;EACE,oBAAA;Cf2kDH;Ae5kDC;EACE,WAAA;Cf8kDH;Ae/kDC;EACE,oBAAA;CfilDH;AellDC;EACE,mBAAA;CfolDH;AehlDC;EACE,YAAA;CfklDH;AelmDC;EACE,WAAA;CfomDH;AermDC;EACE,mBAAA;CfumDH;AexmDC;EACE,mBAAA;Cf0mDH;Ae3mDC;EACE,UAAA;Cf6mDH;Ae9mDC;EACE,mBAAA;CfgnDH;AejnDC;EACE,mBAAA;CfmnDH;AepnDC;EACE,UAAA;CfsnDH;AevnDC;EACE,mBAAA;CfynDH;Ae1nDC;EACE,mBAAA;Cf4nDH;Ae7nDC;EACE,UAAA;Cf+nDH;AehoDC;EACE,mBAAA;CfkoDH;AenoDC;EACE,kBAAA;CfqoDH;AejoDC;EACE,WAAA;CfmoDH;AernDC;EACE,kBAAA;CfunDH;AexnDC;EACE,0BAAA;Cf0nDH;Ae3nDC;EACE,0BAAA;Cf6nDH;Ae9nDC;EACE,iBAAA;CfgoDH;AejoDC;EACE,0BAAA;CfmoDH;AepoDC;EACE,0BAAA;CfsoDH;AevoDC;EACE,iBAAA;CfyoDH;Ae1oDC;EACE,0BAAA;Cf4oDH;Ae7oDC;EACE,0BAAA;Cf+oDH;AehpDC;EACE,iBAAA;CfkpDH;AenpDC;EACE,0BAAA;CfqpDH;AetpDC;EACE,yBAAA;CfwpDH;AezpDC;EACE,gBAAA;Cf2pDH;Aa3pDD;EElCI;IACE,YAAA;GfgsDH;EezrDD;IACE,YAAA;Gf2rDD;Ee5rDD;IACE,oBAAA;Gf8rDD;Ee/rDD;IACE,oBAAA;GfisDD;EelsDD;IACE,WAAA;GfosDD;EersDD;IACE,oBAAA;GfusDD;EexsDD;IACE,oBAAA;Gf0sDD;Ee3sDD;IACE,WAAA;Gf6sDD;Ee9sDD;IACE,oBAAA;GfgtDD;EejtDD;IACE,oBAAA;GfmtDD;EeptDD;IACE,WAAA;GfstDD;EevtDD;IACE,oBAAA;GfytDD;Ee1tDD;IACE,mBAAA;Gf4tDD;Ee9sDD;IACE,YAAA;GfgtDD;EejtDD;IACE,oBAAA;GfmtDD;EeptDD;IACE,oBAAA;GfstDD;EevtDD;IACE,WAAA;GfytDD;Ee1tDD;IACE,oBAAA;Gf4tDD;Ee7tDD;IACE,oBAAA;Gf+tDD;EehuDD;IACE,WAAA;GfkuDD;EenuDD;IACE,oBAAA;GfquDD;EetuDD;IACE,oBAAA;GfwuDD;EezuDD;IACE,WAAA;Gf2uDD;Ee5uDD;IACE,oBAAA;Gf8uDD;Ee/uDD;IACE,mBAAA;GfivDD;Ee7uDD;IACE,YAAA;Gf+uDD;Ee/vDD;IACE,WAAA;GfiwDD;EelwDD;IACE,mBAAA;GfowDD;EerwDD;IACE,mBAAA;GfuwDD;EexwDD;IACE,UAAA;Gf0wDD;Ee3wDD;IACE,mBAAA;Gf6wDD;Ee9wDD;IACE,mBAAA;GfgxDD;EejxDD;IACE,UAAA;GfmxDD;EepxDD;IACE,mBAAA;GfsxDD;EevxDD;IACE,mBAAA;GfyxDD;Ee1xDD;IACE,UAAA;Gf4xDD;Ee7xDD;IACE,mBAAA;Gf+xDD;EehyDD;IACE,kBAAA;GfkyDD;Ee9xDD;IACE,WAAA;GfgyDD;EelxDD;IACE,kBAAA;GfoxDD;EerxDD;IACE,0BAAA;GfuxDD;EexxDD;IACE,0BAAA;Gf0xDD;Ee3xDD;IACE,iBAAA;Gf6xDD;Ee9xDD;IACE,0BAAA;GfgyDD;EejyDD;IACE,0BAAA;GfmyDD;EepyDD;IACE,iBAAA;GfsyDD;EevyDD;IACE,0BAAA;GfyyDD;Ee1yDD;IACE,0BAAA;Gf4yDD;Ee7yDD;IACE,iBAAA;Gf+yDD;EehzDD;IACE,0BAAA;GfkzDD;EenzDD;IACE,yBAAA;GfqzDD;EetzDD;IACE,gBAAA;GfwzDD;CACF;AahzDD;EE3CI;IACE,YAAA;Gf81DH;Eev1DD;IACE,YAAA;Gfy1DD;Ee11DD;IACE,oBAAA;Gf41DD;Ee71DD;IACE,oBAAA;Gf+1DD;Eeh2DD;IACE,WAAA;Gfk2DD;Een2DD;IACE,oBAAA;Gfq2DD;Eet2DD;IACE,oBAAA;Gfw2DD;Eez2DD;IACE,WAAA;Gf22DD;Ee52DD;IACE,oBAAA;Gf82DD;Ee/2DD;IACE,oBAAA;Gfi3DD;Eel3DD;IACE,WAAA;Gfo3DD;Eer3DD;IACE,oBAAA;Gfu3DD;Eex3DD;IACE,mBAAA;Gf03DD;Ee52DD;IACE,YAAA;Gf82DD;Ee/2DD;IACE,oBAAA;Gfi3DD;Eel3DD;IACE,oBAAA;Gfo3DD;Eer3DD;IACE,WAAA;Gfu3DD;Eex3DD;IACE,oBAAA;Gf03DD;Ee33DD;IACE,oBAAA;Gf63DD;Ee93DD;IACE,WAAA;Gfg4DD;Eej4DD;IACE,oBAAA;Gfm4DD;Eep4DD;IACE,oBAAA;Gfs4DD;Eev4DD;IACE,WAAA;Gfy4DD;Ee14DD;IACE,oBAAA;Gf44DD;Ee74DD;IACE,mBAAA;Gf+4DD;Ee34DD;IACE,YAAA;Gf64DD;Ee75DD;IACE,WAAA;Gf+5DD;Eeh6DD;IACE,mBAAA;Gfk6DD;Een6DD;IACE,mBAAA;Gfq6DD;Eet6DD;IACE,UAAA;Gfw6DD;Eez6DD;IACE,mBAAA;Gf26DD;Ee56DD;IACE,mBAAA;Gf86DD;Ee/6DD;IACE,UAAA;Gfi7DD;Eel7DD;IACE,mBAAA;Gfo7DD;Eer7DD;IACE,mBAAA;Gfu7DD;Eex7DD;IACE,UAAA;Gf07DD;Ee37DD;IACE,mBAAA;Gf67DD;Ee97DD;IACE,kBAAA;Gfg8DD;Ee57DD;IACE,WAAA;Gf87DD;Eeh7DD;IACE,kBAAA;Gfk7DD;Een7DD;IACE,0BAAA;Gfq7DD;Eet7DD;IACE,0BAAA;Gfw7DD;Eez7DD;IACE,iBAAA;Gf27DD;Ee57DD;IACE,0BAAA;Gf87DD;Ee/7DD;IACE,0BAAA;Gfi8DD;Eel8DD;IACE,iBAAA;Gfo8DD;Eer8DD;IACE,0BAAA;Gfu8DD;Eex8DD;IACE,0BAAA;Gf08DD;Ee38DD;IACE,iBAAA;Gf68DD;Ee98DD;IACE,0BAAA;Gfg9DD;Eej9DD;IACE,yBAAA;Gfm9DD;Eep9DD;IACE,gBAAA;Gfs9DD;CACF;Aa38DD;EE9CI;IACE,YAAA;Gf4/DH;Eer/DD;IACE,YAAA;Gfu/DD;Eex/DD;IACE,oBAAA;Gf0/DD;Ee3/DD;IACE,oBAAA;Gf6/DD;Ee9/DD;IACE,WAAA;GfggED;EejgED;IACE,oBAAA;GfmgED;EepgED;IACE,oBAAA;GfsgED;EevgED;IACE,WAAA;GfygED;Ee1gED;IACE,oBAAA;Gf4gED;Ee7gED;IACE,oBAAA;Gf+gED;EehhED;IACE,WAAA;GfkhED;EenhED;IACE,oBAAA;GfqhED;EethED;IACE,mBAAA;GfwhED;Ee1gED;IACE,YAAA;Gf4gED;Ee7gED;IACE,oBAAA;Gf+gED;EehhED;IACE,oBAAA;GfkhED;EenhED;IACE,WAAA;GfqhED;EethED;IACE,oBAAA;GfwhED;EezhED;IACE,oBAAA;Gf2hED;Ee5hED;IACE,WAAA;Gf8hED;Ee/hED;IACE,oBAAA;GfiiED;EeliED;IACE,oBAAA;GfoiED;EeriED;IACE,WAAA;GfuiED;EexiED;IACE,oBAAA;Gf0iED;Ee3iED;IACE,mBAAA;Gf6iED;EeziED;IACE,YAAA;Gf2iED;Ee3jED;IACE,WAAA;Gf6jED;Ee9jED;IACE,mBAAA;GfgkED;EejkED;IACE,mBAAA;GfmkED;EepkED;IACE,UAAA;GfskED;EevkED;IACE,mBAAA;GfykED;Ee1kED;IACE,mBAAA;Gf4kED;Ee7kED;IACE,UAAA;Gf+kED;EehlED;IACE,mBAAA;GfklED;EenlED;IACE,mBAAA;GfqlED;EetlED;IACE,UAAA;GfwlED;EezlED;IACE,mBAAA;Gf2lED;Ee5lED;IACE,kBAAA;Gf8lED;Ee1lED;IACE,WAAA;Gf4lED;Ee9kED;IACE,kBAAA;GfglED;EejlED;IACE,0BAAA;GfmlED;EeplED;IACE,0BAAA;GfslED;EevlED;IACE,iBAAA;GfylED;Ee1lED;IACE,0BAAA;Gf4lED;Ee7lED;IACE,0BAAA;Gf+lED;EehmED;IACE,iBAAA;GfkmED;EenmED;IACE,0BAAA;GfqmED;EetmED;IACE,0BAAA;GfwmED;EezmED;IACE,iBAAA;Gf2mED;Ee5mED;IACE,0BAAA;Gf8mED;Ee/mED;IACE,yBAAA;GfinED;EelnED;IACE,gBAAA;GfonED;CACF;AgBxrED;EACE,8BAAA;ChB0rED;AgBxrED;EACE,iBAAA;EACA,oBAAA;EACA,eAAA;EACA,iBAAA;ChB0rED;AgBxrED;EACE,iBAAA;ChB0rED;AgBprED;EACE,YAAA;EACA,gBAAA;EACA,oBAAA;ChBsrED;AgBzrED;;;;;;EAWQ,aAAA;EACA,wBAAA;EACA,oBAAA;EACA,8BAAA;ChBsrEP;AgBpsED;EAoBI,uBAAA;EACA,iCAAA;ChBmrEH;AgBxsED;;;;;;EA8BQ,cAAA;ChBkrEP;AgBhtED;EAoCI,8BAAA;ChB+qEH;AgBntED;EAyCI,0BAAA;ChB6qEH;AgBtqED;;;;;;EAOQ,aAAA;ChBuqEP;AgB5pED;EACE,0BAAA;ChB8pED;AgB/pED;;;;;;EAQQ,0BAAA;ChB+pEP;AgBvqED;;EAeM,yBAAA;ChB4pEL;AgBlpED;EAEI,0BAAA;ChBmpEH;AgB1oED;EAEI,0BAAA;ChB2oEH;AgBloED;EACE,iBAAA;EACA,YAAA;EACA,sBAAA;ChBooED;AgB/nEG;;EACE,iBAAA;EACA,YAAA;EACA,oBAAA;ChBkoEL;AiB9wEC;;;;;;;;;;;;EAOI,0BAAA;CjBqxEL;AiB/wEC;;;;;EAMI,0BAAA;CjBgxEL;AiBnyEC;;;;;;;;;;;;EAOI,0BAAA;CjB0yEL;AiBpyEC;;;;;EAMI,0BAAA;CjBqyEL;AiBxzEC;;;;;;;;;;;;EAOI,0BAAA;CjB+zEL;AiBzzEC;;;;;EAMI,0BAAA;CjB0zEL;AiB70EC;;;;;;;;;;;;EAOI,0BAAA;CjBo1EL;AiB90EC;;;;;EAMI,0BAAA;CjB+0EL;AiBl2EC;;;;;;;;;;;;EAOI,0BAAA;CjBy2EL;AiBn2EC;;;;;EAMI,0BAAA;CjBo2EL;AgBltED;EACE,iBAAA;EACA,kBAAA;ChBotED;AgBvpED;EAAA;IA1DI,YAAA;IACA,oBAAA;IACA,mBAAA;IACA,6CAAA;IACA,0BAAA;GhBqtED;EgB/pEH;IAlDM,iBAAA;GhBotEH;EgBlqEH;;;;;;IAzCY,oBAAA;GhBmtET;EgB1qEH;IAjCM,UAAA;GhB8sEH;EgB7qEH;;;;;;IAxBY,eAAA;GhB6sET;EgBrrEH;;;;;;IApBY,gBAAA;GhBitET;EgB7rEH;;;;IAPY,iBAAA;GhB0sET;CACF;AkBp6ED;EACE,WAAA;EACA,UAAA;EACA,UAAA;EAIA,aAAA;ClBm6ED;AkBh6ED;EACE,eAAA;EACA,YAAA;EACA,WAAA;EACA,oBAAA;EACA,gBAAA;EACA,qBAAA;EACA,eAAA;EACA,UAAA;EACA,iCAAA;ClBk6ED;AkB/5ED;EACE,sBAAA;EACA,gBAAA;EACA,mBAAA;EACA,kBAAA;ClBi6ED;AkBt5ED;Eb4BE,+BAAA;EACG,4BAAA;EACK,uBAAA;CL63ET;AkBt5ED;;EAEE,gBAAA;EACA,mBAAA;EACA,oBAAA;ClBw5ED;AkBr5ED;EACE,eAAA;ClBu5ED;AkBn5ED;EACE,eAAA;EACA,YAAA;ClBq5ED;AkBj5ED;;EAEE,aAAA;ClBm5ED;AkB/4ED;;;EZvEE,qBAAA;EAEA,2CAAA;EACA,qBAAA;CN09ED;AkB/4ED;EACE,eAAA;EACA,iBAAA;EACA,gBAAA;EACA,wBAAA;EACA,eAAA;ClBi5ED;AkBv3ED;EACE,eAAA;EACA,YAAA;EACA,aAAA;EACA,kBAAA;EACA,gBAAA;EACA,wBAAA;EACA,eAAA;EACA,0BAAA;EACA,uBAAA;EACA,0BAAA;EACA,mBAAA;EbxDA,yDAAA;EACQ,iDAAA;EAyHR,uFAAA;EACK,0EAAA;EACG,uEAAA;CL0zET;AmBl8EC;EACE,sBAAA;EACA,WAAA;EdUF,uFAAA;EACQ,+EAAA;CL27ET;AK15EC;EACE,eAAA;EACA,WAAA;CL45EH;AK15EC;EAA0B,eAAA;CL65E3B;AK55EC;EAAgC,eAAA;CL+5EjC;AkB/3EC;;;EAGE,0BAAA;EACA,WAAA;ClBi4EH;AkB93EC;;EAEE,oBAAA;ClBg4EH;AkB53EC;EACE,aAAA;ClB83EH;AkBl3ED;EACE,yBAAA;ClBo3ED;AkB50ED;EAtBI;;;;IACE,kBAAA;GlBw2EH;EkBr2EC;;;;;;;;IAEE,kBAAA;GlB62EH;EkB12EC;;;;;;;;IAEE,kBAAA;GlBk3EH;CACF;AkBx2ED;EACE,oBAAA;ClB02ED;AkBl2ED;;EAEE,mBAAA;EACA,eAAA;EACA,iBAAA;EACA,oBAAA;ClBo2ED;AkBz2ED;;EAQI,iBAAA;EACA,mBAAA;EACA,iBAAA;EACA,oBAAA;EACA,gBAAA;ClBq2EH;AkBl2ED;;;;EAIE,mBAAA;EACA,mBAAA;EACA,mBAAA;ClBo2ED;AkBj2ED;;EAEE,iBAAA;ClBm2ED;AkB/1ED;;EAEE,mBAAA;EACA,sBAAA;EACA,mBAAA;EACA,iBAAA;EACA,uBAAA;EACA,oBAAA;EACA,gBAAA;ClBi2ED;AkB/1ED;;EAEE,cAAA;EACA,kBAAA;ClBi2ED;AkBx1EC;;;;;;EAGE,oBAAA;ClB61EH;AkBv1EC;;;;EAEE,oBAAA;ClB21EH;AkBr1EC;;;;EAGI,oBAAA;ClBw1EL;AkB70ED;EAEE,iBAAA;EACA,oBAAA;EAEA,iBAAA;EACA,iBAAA;ClB60ED;AkB30EC;;EAEE,gBAAA;EACA,iBAAA;ClB60EH;AkBh0ED;EC7PE,aAAA;EACA,kBAAA;EACA,gBAAA;EACA,iBAAA;EACA,mBAAA;CnBgkFD;AmB9jFC;EACE,aAAA;EACA,kBAAA;CnBgkFH;AmB7jFC;;EAEE,aAAA;CnB+jFH;AkB50ED;EAEI,aAAA;EACA,kBAAA;EACA,gBAAA;EACA,iBAAA;EACA,mBAAA;ClB60EH;AkBn1ED;EASI,aAAA;EACA,kBAAA;ClB60EH;AkBv1ED;;EAcI,aAAA;ClB60EH;AkB31ED;EAiBI,aAAA;EACA,iBAAA;EACA,kBAAA;EACA,gBAAA;EACA,iBAAA;ClB60EH;AkBz0ED;ECzRE,aAAA;EACA,mBAAA;EACA,gBAAA;EACA,uBAAA;EACA,mBAAA;CnBqmFD;AmBnmFC;EACE,aAAA;EACA,kBAAA;CnBqmFH;AmBlmFC;;EAEE,aAAA;CnBomFH;AkBr1ED;EAEI,aAAA;EACA,mBAAA;EACA,gBAAA;EACA,uBAAA;EACA,mBAAA;ClBs1EH;AkB51ED;EASI,aAAA;EACA,kBAAA;ClBs1EH;AkBh2ED;;EAcI,aAAA;ClBs1EH;AkBp2ED;EAiBI,aAAA;EACA,iBAAA;EACA,mBAAA;EACA,gBAAA;EACA,uBAAA;ClBs1EH;AkB70ED;EAEE,mBAAA;ClB80ED;AkBh1ED;EAMI,sBAAA;ClB60EH;AkBz0ED;EACE,mBAAA;EACA,OAAA;EACA,SAAA;EACA,WAAA;EACA,eAAA;EACA,YAAA;EACA,aAAA;EACA,kBAAA;EACA,mBAAA;EACA,qBAAA;ClB20ED;AkBz0ED;;;EAGE,YAAA;EACA,aAAA;EACA,kBAAA;ClB20ED;AkBz0ED;;;EAGE,YAAA;EACA,aAAA;EACA,kBAAA;ClB20ED;AkBv0ED;;;;;;;;;;ECpZI,eAAA;CnBuuFH;AkBn1ED;EChZI,sBAAA;Ed+CF,yDAAA;EACQ,iDAAA;CLwrFT;AmBtuFG;EACE,sBAAA;Ed4CJ,0EAAA;EACQ,kEAAA;CL6rFT;AkB71ED;ECtYI,eAAA;EACA,sBAAA;EACA,0BAAA;CnBsuFH;AkBl2ED;EChYI,eAAA;CnBquFH;AkBl2ED;;;;;;;;;;ECvZI,eAAA;CnBqwFH;AkB92ED;ECnZI,sBAAA;Ed+CF,yDAAA;EACQ,iDAAA;CLstFT;AmBpwFG;EACE,sBAAA;Ed4CJ,0EAAA;EACQ,kEAAA;CL2tFT;AkBx3ED;ECzYI,eAAA;EACA,sBAAA;EACA,0BAAA;CnBowFH;AkB73ED;ECnYI,eAAA;CnBmwFH;AkB73ED;;;;;;;;;;EC1ZI,eAAA;CnBmyFH;AkBz4ED;ECtZI,sBAAA;Ed+CF,yDAAA;EACQ,iDAAA;CLovFT;AmBlyFG;EACE,sBAAA;Ed4CJ,0EAAA;EACQ,kEAAA;CLyvFT;AkBn5ED;EC5YI,eAAA;EACA,sBAAA;EACA,0BAAA;CnBkyFH;AkBx5ED;ECtYI,eAAA;CnBiyFH;AkBp5EC;EACG,UAAA;ClBs5EJ;AkBp5EC;EACG,OAAA;ClBs5EJ;AkB54ED;EACE,eAAA;EACA,gBAAA;EACA,oBAAA;EACA,eAAA;ClB84ED;AkB3zED;EAAA;IA9DM,sBAAA;IACA,iBAAA;IACA,uBAAA;GlB63EH;EkBj0EH;IAvDM,sBAAA;IACA,YAAA;IACA,uBAAA;GlB23EH;EkBt0EH;IAhDM,sBAAA;GlBy3EH;EkBz0EH;IA5CM,sBAAA;IACA,uBAAA;GlBw3EH;EkB70EH;;;IAtCQ,YAAA;GlBw3EL;EkBl1EH;IAhCM,YAAA;GlBq3EH;EkBr1EH;IA5BM,iBAAA;IACA,uBAAA;GlBo3EH;EkBz1EH;;IApBM,sBAAA;IACA,cAAA;IACA,iBAAA;IACA,uBAAA;GlBi3EH;EkBh2EH;;IAdQ,gBAAA;GlBk3EL;EkBp2EH;;IATM,mBAAA;IACA,eAAA;GlBi3EH;EkBz2EH;IAHM,OAAA;GlB+2EH;CACF;AkBr2ED;;;;EASI,cAAA;EACA,iBAAA;EACA,iBAAA;ClBk2EH;AkB72ED;;EAiBI,iBAAA;ClBg2EH;AkBj3ED;EJhhBE,mBAAA;EACA,oBAAA;Cdo4FD;AkB90EC;EAAA;IAVI,kBAAA;IACA,iBAAA;IACA,iBAAA;GlB41EH;CACF;AkB53ED;EAwCI,YAAA;ClBu1EH;AkBz0EC;EAAA;IAJM,yBAAA;IACA,gBAAA;GlBi1EL;CACF;AkBv0EC;EAAA;IAJM,iBAAA;IACA,gBAAA;GlB+0EL;CACF;AoBl6FD;EACE,sBAAA;EACA,iBAAA;EACA,oBAAA;EACA,mBAAA;EACA,uBAAA;EACA,+BAAA;MAAA,2BAAA;EACA,gBAAA;EACA,uBAAA;EACA,8BAAA;EACA,oBAAA;EC6CA,kBAAA;EACA,gBAAA;EACA,wBAAA;EACA,mBAAA;EhB4JA,0BAAA;EACG,uBAAA;EACC,sBAAA;EACI,kBAAA;CL6tFT;AoBr6FG;;;;;;EdrBF,qBAAA;EAEA,2CAAA;EACA,qBAAA;CNi8FD;AoBz6FC;;;EAGE,eAAA;EACA,sBAAA;CpB26FH;AoBx6FC;;EAEE,WAAA;EACA,uBAAA;Ef2BF,yDAAA;EACQ,iDAAA;CLg5FT;AoBx6FC;;;EAGE,oBAAA;EE7CF,cAAA;EAGA,0BAAA;EjB8DA,yBAAA;EACQ,iBAAA;CLy5FT;AoBx6FG;;EAEE,qBAAA;CpB06FL;AoBj6FD;EC3DE,eAAA;EACA,0BAAA;EACA,sBAAA;CrB+9FD;AqB79FC;;EAEE,eAAA;EACA,0BAAA;EACI,sBAAA;CrB+9FP;AqB79FC;EACE,eAAA;EACA,0BAAA;EACI,sBAAA;CrB+9FP;AqB79FC;;;EAGE,eAAA;EACA,0BAAA;EACI,sBAAA;CrB+9FP;AqB79FG;;;;;;;;;EAGE,eAAA;EACA,0BAAA;EACI,sBAAA;CrBq+FT;AqBl+FC;;;EAGE,uBAAA;CrBo+FH;AqB/9FG;;;;;;;;;;;;;;;;;;EAME,0BAAA;EACI,sBAAA;CrB6+FT;AoB/9FD;ECTI,eAAA;EACA,0BAAA;CrB2+FH;AoBh+FD;EC9DE,eAAA;EACA,0BAAA;EACA,sBAAA;CrBiiGD;AqB/hGC;;EAEE,eAAA;EACA,0BAAA;EACI,sBAAA;CrBiiGP;AqB/hGC;EACE,eAAA;EACA,0BAAA;EACI,sBAAA;CrBiiGP;AqB/hGC;;;EAGE,eAAA;EACA,0BAAA;EACI,sBAAA;CrBiiGP;AqB/hGG;;;;;;;;;EAGE,eAAA;EACA,0BAAA;EACI,sBAAA;CrBuiGT;AqBpiGC;;;EAGE,uBAAA;CrBsiGH;AqBjiGG;;;;;;;;;;;;;;;;;;EAME,0BAAA;EACI,sBAAA;CrB+iGT;AoB9hGD;ECZI,eAAA;EACA,0BAAA;CrB6iGH;AoB9hGD;EClEE,eAAA;EACA,0BAAA;EACA,sBAAA;CrBmmGD;AqBjmGC;;EAEE,eAAA;EACA,0BAAA;EACI,sBAAA;CrBmmGP;AqBjmGC;EACE,eAAA;EACA,0BAAA;EACI,sBAAA;CrBmmGP;AqBjmGC;;;EAGE,eAAA;EACA,0BAAA;EACI,sBAAA;CrBmmGP;AqBjmGG;;;;;;;;;EAGE,eAAA;EACA,0BAAA;EACI,sBAAA;CrBymGT;AqBtmGC;;;EAGE,uBAAA;CrBwmGH;AqBnmGG;;;;;;;;;;;;;;;;;;EAME,0BAAA;EACI,sBAAA;CrBinGT;AoB5lGD;EChBI,eAAA;EACA,0BAAA;CrB+mGH;AoB5lGD;ECtEE,eAAA;EACA,0BAAA;EACA,sBAAA;CrBqqGD;AqBnqGC;;EAEE,eAAA;EACA,0BAAA;EACI,sBAAA;CrBqqGP;AqBnqGC;EACE,eAAA;EACA,0BAAA;EACI,sBAAA;CrBqqGP;AqBnqGC;;;EAGE,eAAA;EACA,0BAAA;EACI,sBAAA;CrBqqGP;AqBnqGG;;;;;;;;;EAGE,eAAA;EACA,0BAAA;EACI,sBAAA;CrB2qGT;AqBxqGC;;;EAGE,uBAAA;CrB0qGH;AqBrqGG;;;;;;;;;;;;;;;;;;EAME,0BAAA;EACI,sBAAA;CrBmrGT;AoB1pGD;ECpBI,eAAA;EACA,0BAAA;CrBirGH;AoB1pGD;EC1EE,eAAA;EACA,0BAAA;EACA,sBAAA;CrBuuGD;AqBruGC;;EAEE,eAAA;EACA,0BAAA;EACI,sBAAA;CrBuuGP;AqBruGC;EACE,eAAA;EACA,0BAAA;EACI,sBAAA;CrBuuGP;AqBruGC;;;EAGE,eAAA;EACA,0BAAA;EACI,sBAAA;CrBuuGP;AqBruGG;;;;;;;;;EAGE,eAAA;EACA,0BAAA;EACI,sBAAA;CrB6uGT;AqB1uGC;;;EAGE,uBAAA;CrB4uGH;AqBvuGG;;;;;;;;;;;;;;;;;;EAME,0BAAA;EACI,sBAAA;CrBqvGT;AoBxtGD;ECxBI,eAAA;EACA,0BAAA;CrBmvGH;AoBxtGD;EC9EE,eAAA;EACA,0BAAA;EACA,sBAAA;CrByyGD;AqBvyGC;;EAEE,eAAA;EACA,0BAAA;EACI,sBAAA;CrByyGP;AqBvyGC;EACE,eAAA;EACA,0BAAA;EACI,sBAAA;CrByyGP;AqBvyGC;;;EAGE,eAAA;EACA,0BAAA;EACI,sBAAA;CrByyGP;AqBvyGG;;;;;;;;;EAGE,eAAA;EACA,0BAAA;EACI,sBAAA;CrB+yGT;AqB5yGC;;;EAGE,uBAAA;CrB8yGH;AqBzyGG;;;;;;;;;;;;;;;;;;EAME,0BAAA;EACI,sBAAA;CrBuzGT;AoBtxGD;EC5BI,eAAA;EACA,0BAAA;CrBqzGH;AoBjxGD;EACE,eAAA;EACA,oBAAA;EACA,iBAAA;CpBmxGD;AoBjxGC;;;;;EAKE,8BAAA;EfnCF,yBAAA;EACQ,iBAAA;CLuzGT;AoBlxGC;;;;EAIE,0BAAA;CpBoxGH;AoBlxGC;;EAEE,eAAA;EACA,2BAAA;EACA,8BAAA;CpBoxGH;AoBhxGG;;;;EAEE,eAAA;EACA,sBAAA;CpBoxGL;AoB3wGD;;ECrEE,mBAAA;EACA,gBAAA;EACA,uBAAA;EACA,mBAAA;CrBo1GD;AoB9wGD;;ECzEE,kBAAA;EACA,gBAAA;EACA,iBAAA;EACA,mBAAA;CrB21GD;AoBjxGD;;EC7EE,iBAAA;EACA,gBAAA;EACA,iBAAA;EACA,mBAAA;CrBk2GD;AoBhxGD;EACE,eAAA;EACA,YAAA;CpBkxGD;AoB9wGD;EACE,gBAAA;CpBgxGD;AoBzwGC;;;EACE,YAAA;CpB6wGH;AuBv6GD;EACE,WAAA;ElBoLA,yCAAA;EACK,oCAAA;EACG,iCAAA;CLsvGT;AuB16GC;EACE,WAAA;CvB46GH;AuBx6GD;EACE,cAAA;CvB06GD;AuBx6GC;EAAY,eAAA;CvB26Gb;AuB16GC;EAAY,mBAAA;CvB66Gb;AuB56GC;EAAY,yBAAA;CvB+6Gb;AuB56GD;EACE,mBAAA;EACA,UAAA;EACA,iBAAA;ElBuKA,gDAAA;EACQ,2CAAA;KAAA,wCAAA;EAOR,mCAAA;EACQ,8BAAA;KAAA,2BAAA;EAGR,yCAAA;EACQ,oCAAA;KAAA,iCAAA;CLgwGT;AwB18GD;EACE,sBAAA;EACA,SAAA;EACA,UAAA;EACA,iBAAA;EACA,uBAAA;EACA,uBAAA;EACA,yBAAA;EACA,oCAAA;EACA,mCAAA;CxB48GD;AwBx8GD;;EAEE,mBAAA;CxB08GD;AwBt8GD;EACE,WAAA;CxBw8GD;AwBp8GD;EACE,mBAAA;EACA,UAAA;EACA,QAAA;EACA,cAAA;EACA,cAAA;EACA,YAAA;EACA,iBAAA;EACA,eAAA;EACA,gBAAA;EACA,iBAAA;EACA,gBAAA;EACA,iBAAA;EACA,0BAAA;EACA,0BAAA;EACA,sCAAA;EACA,mBAAA;EnBsBA,oDAAA;EACQ,4CAAA;EmBrBR,qCAAA;UAAA,6BAAA;CxBu8GD;AwBl8GC;EACE,SAAA;EACA,WAAA;CxBo8GH;AwB79GD;ECzBE,YAAA;EACA,cAAA;EACA,iBAAA;EACA,0BAAA;CzBy/GD;AwBn+GD;EAmCI,eAAA;EACA,kBAAA;EACA,YAAA;EACA,oBAAA;EACA,wBAAA;EACA,eAAA;EACA,oBAAA;CxBm8GH;AwB77GC;;EAEE,sBAAA;EACA,eAAA;EACA,0BAAA;CxB+7GH;AwBz7GC;;;EAGE,eAAA;EACA,sBAAA;EACA,WAAA;EACA,0BAAA;CxB27GH;AwBl7GC;;;EAGE,eAAA;CxBo7GH;AwBh7GC;;EAEE,sBAAA;EACA,8BAAA;EACA,uBAAA;EE3GF,oEAAA;EF6GE,oBAAA;CxBk7GH;AwB76GD;EAGI,eAAA;CxB66GH;AwBh7GD;EAQI,WAAA;CxB26GH;AwBn6GD;EACE,WAAA;EACA,SAAA;CxBq6GD;AwB75GD;EACE,QAAA;EACA,YAAA;CxB+5GD;AwB35GD;EACE,eAAA;EACA,kBAAA;EACA,gBAAA;EACA,wBAAA;EACA,eAAA;EACA,oBAAA;CxB65GD;AwBz5GD;EACE,gBAAA;EACA,QAAA;EACA,SAAA;EACA,UAAA;EACA,OAAA;EACA,aAAA;CxB25GD;AwBv5GD;EACE,SAAA;EACA,WAAA;CxBy5GD;AwBj5GD;;EAII,cAAA;EACA,0BAAA;EACA,4BAAA;EACA,YAAA;CxBi5GH;AwBx5GD;;EAWI,UAAA;EACA,aAAA;EACA,mBAAA;CxBi5GH;AwB53GD;EAXE;IApEA,WAAA;IACA,SAAA;GxB+8GC;EwB54GD;IA1DA,QAAA;IACA,YAAA;GxBy8GC;CACF;A2BzlHD;;EAEE,mBAAA;EACA,sBAAA;EACA,uBAAA;C3B2lHD;A2B/lHD;;EAMI,mBAAA;EACA,YAAA;C3B6lHH;A2B3lHG;;;;;;;;EAIE,WAAA;C3BimHL;A2B3lHD;;;;EAKI,kBAAA;C3B4lHH;A2BvlHD;EACE,kBAAA;C3BylHD;A2B1lHD;;;EAOI,YAAA;C3BwlHH;A2B/lHD;;;EAYI,iBAAA;C3BwlHH;A2BplHD;EACE,iBAAA;C3BslHD;A2BllHD;EACE,eAAA;C3BolHD;A2BnlHC;EClDA,8BAAA;EACG,2BAAA;C5BwoHJ;A2BllHD;;EC/CE,6BAAA;EACG,0BAAA;C5BqoHJ;A2BjlHD;EACE,YAAA;C3BmlHD;A2BjlHD;EACE,iBAAA;C3BmlHD;A2BjlHD;;ECnEE,8BAAA;EACG,2BAAA;C5BwpHJ;A2BhlHD;ECjEE,6BAAA;EACG,0BAAA;C5BopHJ;A2B/kHD;;EAEE,WAAA;C3BilHD;A2BhkHD;EACE,kBAAA;EACA,mBAAA;C3BkkHD;A2BhkHD;EACE,mBAAA;EACA,oBAAA;C3BkkHD;A2B7jHD;EtB/CE,yDAAA;EACQ,iDAAA;CL+mHT;A2B7jHC;EtBnDA,yBAAA;EACQ,iBAAA;CLmnHT;A2B1jHD;EACE,eAAA;C3B4jHD;A2BzjHD;EACE,wBAAA;EACA,uBAAA;C3B2jHD;A2BxjHD;EACE,wBAAA;C3B0jHD;A2BnjHD;;;EAII,eAAA;EACA,YAAA;EACA,YAAA;EACA,gBAAA;C3BojHH;A2B3jHD;EAcM,YAAA;C3BgjHL;A2B9jHD;;;;EAsBI,iBAAA;EACA,eAAA;C3B8iHH;A2BziHC;EACE,iBAAA;C3B2iHH;A2BziHC;EACE,6BAAA;ECpKF,8BAAA;EACC,6BAAA;C5BgtHF;A2B1iHC;EACE,+BAAA;EChLF,2BAAA;EACC,0BAAA;C5B6tHF;A2B1iHD;EACE,iBAAA;C3B4iHD;A2B1iHD;;EC/KE,8BAAA;EACC,6BAAA;C5B6tHF;A2BziHD;EC7LE,2BAAA;EACC,0BAAA;C5ByuHF;A2BriHD;EACE,eAAA;EACA,YAAA;EACA,oBAAA;EACA,0BAAA;C3BuiHD;A2B3iHD;;EAOI,YAAA;EACA,oBAAA;EACA,UAAA;C3BwiHH;A2BjjHD;EAYI,YAAA;C3BwiHH;A2BpjHD;EAgBI,WAAA;C3BuiHH;A2BthHD;;;;EAKM,mBAAA;EACA,uBAAA;EACA,qBAAA;C3BuhHL;A6BjwHD;EACE,mBAAA;EACA,eAAA;EACA,0BAAA;C7BmwHD;A6BhwHC;EACE,YAAA;EACA,gBAAA;EACA,iBAAA;C7BkwHH;A6B3wHD;EAeI,mBAAA;EACA,WAAA;EAKA,YAAA;EAEA,YAAA;EACA,iBAAA;C7B0vHH;A6BjvHD;;;EV8BE,aAAA;EACA,mBAAA;EACA,gBAAA;EACA,uBAAA;EACA,mBAAA;CnBwtHD;AmBttHC;;;EACE,aAAA;EACA,kBAAA;CnB0tHH;AmBvtHC;;;;;;EAEE,aAAA;CnB6tHH;A6BnwHD;;;EVyBE,aAAA;EACA,kBAAA;EACA,gBAAA;EACA,iBAAA;EACA,mBAAA;CnB+uHD;AmB7uHC;;;EACE,aAAA;EACA,kBAAA;CnBivHH;AmB9uHC;;;;;;EAEE,aAAA;CnBovHH;A6BjxHD;;;EAGE,oBAAA;C7BmxHD;A6BjxHC;;;EACE,iBAAA;C7BqxHH;A6BjxHD;;EAEE,UAAA;EACA,oBAAA;EACA,uBAAA;C7BmxHD;A6B9wHD;EACE,kBAAA;EACA,gBAAA;EACA,oBAAA;EACA,eAAA;EACA,eAAA;EACA,mBAAA;EACA,0BAAA;EACA,0BAAA;EACA,mBAAA;C7BgxHD;A6B7wHC;EACE,kBAAA;EACA,gBAAA;EACA,mBAAA;C7B+wHH;A6B7wHC;EACE,mBAAA;EACA,gBAAA;EACA,mBAAA;C7B+wHH;A6BnyHD;;EA0BI,cAAA;C7B6wHH;A6BxwHD;;;;;;;EDhGE,8BAAA;EACG,2BAAA;C5Bi3HJ;A6BzwHD;EACE,gBAAA;C7B2wHD;A6BzwHD;;;;;;;EDpGE,6BAAA;EACG,0BAAA;C5Bs3HJ;A6B1wHD;EACE,eAAA;C7B4wHD;A6BvwHD;EACE,mBAAA;EAGA,aAAA;EACA,oBAAA;C7BuwHD;A6B5wHD;EAUI,mBAAA;C7BqwHH;A6B/wHD;EAYM,kBAAA;C7BswHL;A6BnwHG;;;EAGE,WAAA;C7BqwHL;A6BhwHC;;EAGI,mBAAA;C7BiwHL;A6B9vHC;;EAGI,WAAA;EACA,kBAAA;C7B+vHL;A8B15HD;EACE,iBAAA;EACA,gBAAA;EACA,iBAAA;C9B45HD;A8B/5HD;EAOI,mBAAA;EACA,eAAA;C9B25HH;A8Bn6HD;EAWM,mBAAA;EACA,eAAA;EACA,mBAAA;C9B25HL;A8B15HK;;EAEE,sBAAA;EACA,0BAAA;C9B45HP;A8Bv5HG;EACE,eAAA;C9By5HL;A8Bv5HK;;EAEE,eAAA;EACA,sBAAA;EACA,8BAAA;EACA,oBAAA;C9By5HP;A8Bl5HG;;;EAGE,0BAAA;EACA,sBAAA;C9Bo5HL;A8B77HD;ELHE,YAAA;EACA,cAAA;EACA,iBAAA;EACA,0BAAA;CzBm8HD;A8Bn8HD;EA0DI,gBAAA;C9B44HH;A8Bn4HD;EACE,iCAAA;C9Bq4HD;A8Bt4HD;EAGI,YAAA;EAEA,oBAAA;C9Bq4HH;A8B14HD;EASM,kBAAA;EACA,wBAAA;EACA,8BAAA;EACA,2BAAA;C9Bo4HL;A8Bn4HK;EACE,sCAAA;C9Bq4HP;A8B/3HK;;;EAGE,eAAA;EACA,0BAAA;EACA,0BAAA;EACA,iCAAA;EACA,gBAAA;C9Bi4HP;A8B53HC;EAqDA,YAAA;EA8BA,iBAAA;C9B6yHD;A8Bh4HC;EAwDE,YAAA;C9B20HH;A8Bn4HC;EA0DI,mBAAA;EACA,mBAAA;C9B40HL;A8Bv4HC;EAgEE,UAAA;EACA,WAAA;C9B00HH;A8B9zHD;EAAA;IAPM,oBAAA;IACA,UAAA;G9By0HH;E8Bn0HH;IAJQ,iBAAA;G9B00HL;CACF;A8Bp5HC;EAuFE,gBAAA;EACA,mBAAA;C9Bg0HH;A8Bx5HC;;;EA8FE,0BAAA;C9B+zHH;A8BjzHD;EAAA;IATM,iCAAA;IACA,2BAAA;G9B8zHH;E8BtzHH;;;IAHM,6BAAA;G9B8zHH;CACF;A8B/5HD;EAEI,YAAA;C9Bg6HH;A8Bl6HD;EAMM,mBAAA;C9B+5HL;A8Br6HD;EASM,iBAAA;C9B+5HL;A8B15HK;;;EAGE,eAAA;EACA,0BAAA;C9B45HP;A8Bp5HD;EAEI,YAAA;C9Bq5HH;A8Bv5HD;EAIM,gBAAA;EACA,eAAA;C9Bs5HL;A8B14HD;EACE,YAAA;C9B44HD;A8B74HD;EAII,YAAA;C9B44HH;A8Bh5HD;EAMM,mBAAA;EACA,mBAAA;C9B64HL;A8Bp5HD;EAYI,UAAA;EACA,WAAA;C9B24HH;A8B/3HD;EAAA;IAPM,oBAAA;IACA,UAAA;G9B04HH;E8Bp4HH;IAJQ,iBAAA;G9B24HL;CACF;A8Bn4HD;EACE,iBAAA;C9Bq4HD;A8Bt4HD;EAKI,gBAAA;EACA,mBAAA;C9Bo4HH;A8B14HD;;;EAYI,0BAAA;C9Bm4HH;A8Br3HD;EAAA;IATM,iCAAA;IACA,2BAAA;G9Bk4HH;E8B13HH;;;IAHM,6BAAA;G9Bk4HH;CACF;A8Bz3HD;EAEI,cAAA;C9B03HH;A8B53HD;EAKI,eAAA;C9B03HH;A8Bj3HD;EAEE,iBAAA;EF3OA,2BAAA;EACC,0BAAA;C5B8lIF;A+BxlID;EACE,mBAAA;EACA,iBAAA;EACA,oBAAA;EACA,8BAAA;C/B0lID;A+BllID;EAAA;IAFI,mBAAA;G/BwlID;CACF;A+BzkID;EAAA;IAFI,YAAA;G/B+kID;CACF;A+BjkID;EACE,oBAAA;EACA,oBAAA;EACA,mBAAA;EACA,kCAAA;EACA,2DAAA;UAAA,mDAAA;EAEA,kCAAA;C/BkkID;A+BhkIC;EACE,iBAAA;C/BkkIH;A+BtiID;EAAA;IAxBI,YAAA;IACA,cAAA;IACA,yBAAA;YAAA,iBAAA;G/BkkID;E+BhkIC;IACE,0BAAA;IACA,wBAAA;IACA,kBAAA;IACA,6BAAA;G/BkkIH;E+B/jIC;IACE,oBAAA;G/BikIH;E+B5jIC;;;IAGE,gBAAA;IACA,iBAAA;G/B8jIH;CACF;A+B1jID;;EAGI,kBAAA;C/B2jIH;A+BtjIC;EAAA;;IAFI,kBAAA;G/B6jIH;CACF;A+BpjID;;;;EAII,oBAAA;EACA,mBAAA;C/BsjIH;A+BhjIC;EAAA;;;;IAHI,gBAAA;IACA,eAAA;G/B0jIH;CACF;A+B9iID;EACE,cAAA;EACA,sBAAA;C/BgjID;A+B3iID;EAAA;IAFI,iBAAA;G/BijID;CACF;A+B7iID;;EAEE,gBAAA;EACA,SAAA;EACA,QAAA;EACA,cAAA;C/B+iID;A+BziID;EAAA;;IAFI,iBAAA;G/BgjID;CACF;A+B9iID;EACE,OAAA;EACA,sBAAA;C/BgjID;A+B9iID;EACE,UAAA;EACA,iBAAA;EACA,sBAAA;C/BgjID;A+B1iID;EACE,YAAA;EACA,mBAAA;EACA,gBAAA;EACA,kBAAA;EACA,aAAA;C/B4iID;A+B1iIC;;EAEE,sBAAA;C/B4iIH;A+BrjID;EAaI,eAAA;C/B2iIH;A+BliID;EALI;;IAEE,mBAAA;G/B0iIH;CACF;A+BhiID;EACE,mBAAA;EACA,aAAA;EACA,mBAAA;EACA,kBAAA;EC9LA,gBAAA;EACA,mBAAA;ED+LA,8BAAA;EACA,uBAAA;EACA,8BAAA;EACA,mBAAA;C/BmiID;A+B/hIC;EACE,WAAA;C/BiiIH;A+B/iID;EAmBI,eAAA;EACA,YAAA;EACA,YAAA;EACA,mBAAA;C/B+hIH;A+BrjID;EAyBI,gBAAA;C/B+hIH;A+BzhID;EAAA;IAFI,cAAA;G/B+hID;CACF;A+BthID;EACE,oBAAA;C/BwhID;A+BzhID;EAII,kBAAA;EACA,qBAAA;EACA,kBAAA;C/BwhIH;A+B5/HC;EAAA;IAtBI,iBAAA;IACA,YAAA;IACA,YAAA;IACA,cAAA;IACA,8BAAA;IACA,UAAA;IACA,yBAAA;YAAA,iBAAA;G/BshIH;E+BtgID;;IAbM,2BAAA;G/BuhIL;E+B1gID;IAVM,kBAAA;G/BuhIL;E+BthIK;;IAEE,uBAAA;G/BwhIP;CACF;A+BtgID;EAAA;IAXI,YAAA;IACA,UAAA;G/BqhID;E+B3gIH;IAPM,YAAA;G/BqhIH;E+B9gIH;IALQ,kBAAA;IACA,qBAAA;G/BshIL;CACF;A+B3gID;EACE,mBAAA;EACA,oBAAA;EACA,mBAAA;EACA,kCAAA;EACA,qCAAA;E1B9NA,6FAAA;EACQ,qFAAA;E2B/DR,gBAAA;EACA,mBAAA;ChC4yID;AkB5xHD;EAAA;IA9DM,sBAAA;IACA,iBAAA;IACA,uBAAA;GlB81HH;EkBlyHH;IAvDM,sBAAA;IACA,YAAA;IACA,uBAAA;GlB41HH;EkBvyHH;IAhDM,sBAAA;GlB01HH;EkB1yHH;IA5CM,sBAAA;IACA,uBAAA;GlBy1HH;EkB9yHH;;;IAtCQ,YAAA;GlBy1HL;EkBnzHH;IAhCM,YAAA;GlBs1HH;EkBtzHH;IA5BM,iBAAA;IACA,uBAAA;GlBq1HH;EkB1zHH;;IApBM,sBAAA;IACA,cAAA;IACA,iBAAA;IACA,uBAAA;GlBk1HH;EkBj0HH;;IAdQ,gBAAA;GlBm1HL;EkBr0HH;;IATM,mBAAA;IACA,eAAA;GlBk1HH;EkB10HH;IAHM,OAAA;GlBg1HH;CACF;A+BpjIC;EAAA;IANI,mBAAA;G/B8jIH;E+B5jIG;IACE,iBAAA;G/B8jIL;CACF;A+B7iID;EAAA;IARI,YAAA;IACA,UAAA;IACA,eAAA;IACA,gBAAA;IACA,eAAA;IACA,kBAAA;I1BzPF,yBAAA;IACQ,iBAAA;GLmzIP;CACF;A+BnjID;EACE,cAAA;EHpUA,2BAAA;EACC,0BAAA;C5B03IF;A+BnjID;EACE,iBAAA;EHzUA,6BAAA;EACC,4BAAA;EAOD,8BAAA;EACC,6BAAA;C5By3IF;A+B/iID;EChVE,gBAAA;EACA,mBAAA;ChCk4ID;A+BhjIC;ECnVA,iBAAA;EACA,oBAAA;ChCs4ID;A+BjjIC;ECtVA,iBAAA;EACA,oBAAA;ChC04ID;A+B3iID;EChWE,iBAAA;EACA,oBAAA;ChC84ID;A+BviID;EAAA;IAJI,YAAA;IACA,kBAAA;IACA,mBAAA;G/B+iID;CACF;A+BlhID;EAhBE;IExWA,uBAAA;GjC84IC;E+BriID;IE5WA,wBAAA;IF8WE,oBAAA;G/BuiID;E+BziID;IAKI,gBAAA;G/BuiIH;CACF;A+B9hID;EACE,0BAAA;EACA,sBAAA;C/BgiID;A+BliID;EAKI,eAAA;C/BgiIH;A+B/hIG;;EAEE,eAAA;EACA,8BAAA;C/BiiIL;A+B1iID;EAcI,eAAA;C/B+hIH;A+B7iID;EAmBM,eAAA;C/B6hIL;A+B3hIK;;EAEE,eAAA;EACA,8BAAA;C/B6hIP;A+BzhIK;;;EAGE,eAAA;EACA,0BAAA;C/B2hIP;A+BvhIK;;;EAGE,eAAA;EACA,8BAAA;C/ByhIP;A+BjkID;EA8CI,sBAAA;C/BshIH;A+BrhIG;;EAEE,0BAAA;C/BuhIL;A+BxkID;EAoDM,0BAAA;C/BuhIL;A+B3kID;;EA0DI,sBAAA;C/BqhIH;A+B9gIK;;;EAGE,0BAAA;EACA,eAAA;C/BghIP;A+B/+HC;EAAA;IAzBQ,eAAA;G/B4gIP;E+B3gIO;;IAEE,eAAA;IACA,8BAAA;G/B6gIT;E+BzgIO;;;IAGE,eAAA;IACA,0BAAA;G/B2gIT;E+BvgIO;;;IAGE,eAAA;IACA,8BAAA;G/BygIT;CACF;A+B3mID;EA8GI,eAAA;C/BggIH;A+B//HG;EACE,eAAA;C/BigIL;A+BjnID;EAqHI,eAAA;C/B+/HH;A+B9/HG;;EAEE,eAAA;C/BggIL;A+B5/HK;;;;EAEE,eAAA;C/BggIP;A+Bx/HD;EACE,0BAAA;EACA,sBAAA;C/B0/HD;A+B5/HD;EAKI,eAAA;C/B0/HH;A+Bz/HG;;EAEE,eAAA;EACA,8BAAA;C/B2/HL;A+BpgID;EAcI,eAAA;C/By/HH;A+BvgID;EAmBM,eAAA;C/Bu/HL;A+Br/HK;;EAEE,eAAA;EACA,8BAAA;C/Bu/HP;A+Bn/HK;;;EAGE,eAAA;EACA,0BAAA;C/Bq/HP;A+Bj/HK;;;EAGE,eAAA;EACA,8BAAA;C/Bm/HP;A+B3hID;EA+CI,sBAAA;C/B++HH;A+B9+HG;;EAEE,0BAAA;C/Bg/HL;A+BliID;EAqDM,0BAAA;C/Bg/HL;A+BriID;;EA2DI,sBAAA;C/B8+HH;A+Bx+HK;;;EAGE,0BAAA;EACA,eAAA;C/B0+HP;A+Bn8HC;EAAA;IA/BQ,sBAAA;G/Bs+HP;E+Bv8HD;IA5BQ,0BAAA;G/Bs+HP;E+B18HD;IAzBQ,eAAA;G/Bs+HP;E+Br+HO;;IAEE,eAAA;IACA,8BAAA;G/Bu+HT;E+Bn+HO;;;IAGE,eAAA;IACA,0BAAA;G/Bq+HT;E+Bj+HO;;;IAGE,eAAA;IACA,8BAAA;G/Bm+HT;CACF;A+B3kID;EA+GI,eAAA;C/B+9HH;A+B99HG;EACE,eAAA;C/Bg+HL;A+BjlID;EAsHI,eAAA;C/B89HH;A+B79HG;;EAEE,eAAA;C/B+9HL;A+B39HK;;;;EAEE,eAAA;C/B+9HP;AkCzmJD;EACE,kBAAA;EACA,oBAAA;EACA,iBAAA;EACA,0BAAA;EACA,mBAAA;ClC2mJD;AkChnJD;EAQI,sBAAA;ClC2mJH;AkCnnJD;EAWM,kBAAA;EACA,eAAA;EACA,eAAA;ClC2mJL;AkCxnJD;EAkBI,eAAA;ClCymJH;AmC7nJD;EACE,sBAAA;EACA,gBAAA;EACA,eAAA;EACA,mBAAA;CnC+nJD;AmCnoJD;EAOI,gBAAA;CnC+nJH;AmCtoJD;;EAUM,mBAAA;EACA,YAAA;EACA,kBAAA;EACA,wBAAA;EACA,sBAAA;EACA,eAAA;EACA,0BAAA;EACA,0BAAA;EACA,kBAAA;CnCgoJL;AmC9nJG;;EAGI,eAAA;EPXN,+BAAA;EACG,4BAAA;C5B2oJJ;AmC7nJG;;EPvBF,gCAAA;EACG,6BAAA;C5BwpJJ;AmCxnJG;;;;EAEE,WAAA;EACA,eAAA;EACA,0BAAA;EACA,sBAAA;CnC4nJL;AmCtnJG;;;;;;EAGE,WAAA;EACA,eAAA;EACA,0BAAA;EACA,sBAAA;EACA,gBAAA;CnC2nJL;AmClrJD;;;;;;EAkEM,eAAA;EACA,0BAAA;EACA,sBAAA;EACA,oBAAA;CnCwnJL;AmC/mJD;;EC3EM,mBAAA;EACA,gBAAA;EACA,uBAAA;CpC8rJL;AoC5rJG;;ERKF,+BAAA;EACG,4BAAA;C5B2rJJ;AoC3rJG;;ERTF,gCAAA;EACG,6BAAA;C5BwsJJ;AmC1nJD;;EChFM,kBAAA;EACA,gBAAA;EACA,iBAAA;CpC8sJL;AoC5sJG;;ERKF,+BAAA;EACG,4BAAA;C5B2sJJ;AoC3sJG;;ERTF,gCAAA;EACG,6BAAA;C5BwtJJ;AqC3tJD;EACE,gBAAA;EACA,eAAA;EACA,iBAAA;EACA,mBAAA;CrC6tJD;AqCjuJD;EAOI,gBAAA;CrC6tJH;AqCpuJD;;EAUM,sBAAA;EACA,kBAAA;EACA,0BAAA;EACA,0BAAA;EACA,oBAAA;CrC8tJL;AqC5uJD;;EAmBM,sBAAA;EACA,0BAAA;CrC6tJL;AqCjvJD;;EA2BM,aAAA;CrC0tJL;AqCrvJD;;EAkCM,YAAA;CrCutJL;AqCzvJD;;;;EA2CM,eAAA;EACA,0BAAA;EACA,oBAAA;CrCotJL;AsClwJD;EACE,gBAAA;EACA,wBAAA;EACA,eAAA;EACA,kBAAA;EACA,eAAA;EACA,eAAA;EACA,mBAAA;EACA,oBAAA;EACA,yBAAA;EACA,qBAAA;CtCowJD;AsChwJG;;EAEE,eAAA;EACA,sBAAA;EACA,gBAAA;CtCkwJL;AsC7vJC;EACE,cAAA;CtC+vJH;AsC3vJC;EACE,mBAAA;EACA,UAAA;CtC6vJH;AsCtvJD;ECtCE,0BAAA;CvC+xJD;AuC5xJG;;EAEE,0BAAA;CvC8xJL;AsCzvJD;EC1CE,0BAAA;CvCsyJD;AuCnyJG;;EAEE,0BAAA;CvCqyJL;AsC5vJD;EC9CE,0BAAA;CvC6yJD;AuC1yJG;;EAEE,0BAAA;CvC4yJL;AsC/vJD;EClDE,0BAAA;CvCozJD;AuCjzJG;;EAEE,0BAAA;CvCmzJL;AsClwJD;ECtDE,0BAAA;CvC2zJD;AuCxzJG;;EAEE,0BAAA;CvC0zJL;AsCrwJD;EC1DE,0BAAA;CvCk0JD;AuC/zJG;;EAEE,0BAAA;CvCi0JL;AwCn0JD;EACE,sBAAA;EACA,gBAAA;EACA,iBAAA;EACA,gBAAA;EACA,kBAAA;EACA,eAAA;EACA,eAAA;EACA,uBAAA;EACA,oBAAA;EACA,mBAAA;EACA,0BAAA;EACA,oBAAA;CxCq0JD;AwCl0JC;EACE,cAAA;CxCo0JH;AwCh0JC;EACE,mBAAA;EACA,UAAA;CxCk0JH;AwC/zJC;;EAEE,OAAA;EACA,iBAAA;CxCi0JH;AwC5zJG;;EAEE,eAAA;EACA,sBAAA;EACA,gBAAA;CxC8zJL;AwCzzJC;;EAEE,eAAA;EACA,0BAAA;CxC2zJH;AwCxzJC;EACE,aAAA;CxC0zJH;AwCvzJC;EACE,kBAAA;CxCyzJH;AwCtzJC;EACE,iBAAA;CxCwzJH;AyCl3JD;EACE,kBAAA;EACA,qBAAA;EACA,oBAAA;EACA,eAAA;EACA,0BAAA;CzCo3JD;AyCz3JD;;EASI,eAAA;CzCo3JH;AyC73JD;EAaI,oBAAA;EACA,gBAAA;EACA,iBAAA;CzCm3JH;AyCl4JD;EAmBI,0BAAA;CzCk3JH;AyC/2JC;;EAEE,mBAAA;CzCi3JH;AyCz4JD;EA4BI,gBAAA;CzCg3JH;AyC91JD;EAAA;IAdI,kBAAA;IACA,qBAAA;GzCg3JD;EyC92JC;;IAEE,mBAAA;IACA,oBAAA;GzCg3JH;EyCx2JH;;IAHM,gBAAA;GzC+2JH;CACF;A0C15JD;EACE,eAAA;EACA,aAAA;EACA,oBAAA;EACA,wBAAA;EACA,0BAAA;EACA,0BAAA;EACA,mBAAA;ErCiLA,4CAAA;EACK,uCAAA;EACG,oCAAA;CL4uJT;A0Ct6JD;;EAaI,kBAAA;EACA,mBAAA;C1C65JH;A0Cz5JC;;;EAGE,sBAAA;C1C25JH;A0Ch7JD;EA0BI,aAAA;EACA,eAAA;C1Cy5JH;A2Cl7JD;EACE,cAAA;EACA,oBAAA;EACA,8BAAA;EACA,mBAAA;C3Co7JD;A2Cx7JD;EAQI,cAAA;EAEA,eAAA;C3Ck7JH;A2C57JD;EAeI,kBAAA;C3Cg7JH;A2C/7JD;;EAqBI,iBAAA;C3C86JH;A2Cn8JD;EAyBI,gBAAA;C3C66JH;A2Cr6JD;;EAEE,oBAAA;C3Cu6JD;A2Cz6JD;;EAMI,mBAAA;EACA,UAAA;EACA,aAAA;EACA,eAAA;C3Cu6JH;A2C/5JD;ECvDE,0BAAA;EACA,sBAAA;EACA,eAAA;C5Cy9JD;A2Cp6JD;EClDI,0BAAA;C5Cy9JH;A2Cv6JD;EC/CI,eAAA;C5Cy9JH;A2Ct6JD;EC3DE,0BAAA;EACA,sBAAA;EACA,eAAA;C5Co+JD;A2C36JD;ECtDI,0BAAA;C5Co+JH;A2C96JD;ECnDI,eAAA;C5Co+JH;A2C76JD;EC/DE,0BAAA;EACA,sBAAA;EACA,eAAA;C5C++JD;A2Cl7JD;EC1DI,0BAAA;C5C++JH;A2Cr7JD;ECvDI,eAAA;C5C++JH;A2Cp7JD;ECnEE,0BAAA;EACA,sBAAA;EACA,eAAA;C5C0/JD;A2Cz7JD;EC9DI,0BAAA;C5C0/JH;A2C57JD;EC3DI,eAAA;C5C0/JH;A6C5/JD;EACE;IAAQ,4BAAA;G7C+/JP;E6C9/JD;IAAQ,yBAAA;G7CigKP;CACF;A6C9/JD;EACE;IAAQ,4BAAA;G7CigKP;E6ChgKD;IAAQ,yBAAA;G7CmgKP;CACF;A6CtgKD;EACE;IAAQ,4BAAA;G7CigKP;E6ChgKD;IAAQ,yBAAA;G7CmgKP;CACF;A6C5/JD;EACE,iBAAA;EACA,aAAA;EACA,oBAAA;EACA,0BAAA;EACA,mBAAA;ExCsCA,uDAAA;EACQ,+CAAA;CLy9JT;A6C3/JD;EACE,YAAA;EACA,UAAA;EACA,aAAA;EACA,gBAAA;EACA,kBAAA;EACA,eAAA;EACA,mBAAA;EACA,0BAAA;ExCyBA,uDAAA;EACQ,+CAAA;EAyHR,oCAAA;EACK,+BAAA;EACG,4BAAA;CL62JT;A6Cx/JD;;ECCI,8MAAA;EACA,yMAAA;EACA,sMAAA;EDAF,mCAAA;UAAA,2BAAA;C7C4/JD;A6Cr/JD;;ExC5CE,2DAAA;EACK,sDAAA;EACG,mDAAA;CLqiKT;A6Cl/JD;EErEE,0BAAA;C/C0jKD;A+CvjKC;EDgDE,8MAAA;EACA,yMAAA;EACA,sMAAA;C9C0gKH;A6Ct/JD;EEzEE,0BAAA;C/CkkKD;A+C/jKC;EDgDE,8MAAA;EACA,yMAAA;EACA,sMAAA;C9CkhKH;A6C1/JD;EE7EE,0BAAA;C/C0kKD;A+CvkKC;EDgDE,8MAAA;EACA,yMAAA;EACA,sMAAA;C9C0hKH;A6C9/JD;EEjFE,0BAAA;C/CklKD;A+C/kKC;EDgDE,8MAAA;EACA,yMAAA;EACA,sMAAA;C9CkiKH;AgD1lKD;EAEE,iBAAA;ChD2lKD;AgDzlKC;EACE,cAAA;ChD2lKH;AgDvlKD;;EAEE,QAAA;EACA,iBAAA;ChDylKD;AgDtlKD;EACE,eAAA;ChDwlKD;AgDrlKD;EACE,eAAA;ChDulKD;AgDplKC;EACE,gBAAA;ChDslKH;AgDllKD;;EAEE,mBAAA;ChDolKD;AgDjlKD;;EAEE,oBAAA;ChDmlKD;AgDhlKD;;;EAGE,oBAAA;EACA,oBAAA;ChDklKD;AgD/kKD;EACE,uBAAA;ChDilKD;AgD9kKD;EACE,uBAAA;ChDglKD;AgD5kKD;EACE,cAAA;EACA,mBAAA;ChD8kKD;AgDxkKD;EACE,gBAAA;EACA,iBAAA;ChD0kKD;AiDjoKD;EAEE,oBAAA;EACA,gBAAA;CjDkoKD;AiD1nKD;EACE,mBAAA;EACA,eAAA;EACA,mBAAA;EAEA,oBAAA;EACA,0BAAA;EACA,0BAAA;CjD2nKD;AiDxnKC;ErB3BA,6BAAA;EACC,4BAAA;C5BspKF;AiDznKC;EACE,iBAAA;ErBvBF,gCAAA;EACC,+BAAA;C5BmpKF;AiDlnKD;;EAEE,eAAA;CjDonKD;AiDtnKD;;EAKI,eAAA;CjDqnKH;AiDjnKC;;;;EAEE,sBAAA;EACA,eAAA;EACA,0BAAA;CjDqnKH;AiDjnKD;EACE,YAAA;EACA,iBAAA;CjDmnKD;AiD9mKC;;;EAGE,0BAAA;EACA,eAAA;EACA,oBAAA;CjDgnKH;AiDrnKC;;;EASI,eAAA;CjDinKL;AiD1nKC;;;EAYI,eAAA;CjDmnKL;AiD9mKC;;;EAGE,WAAA;EACA,eAAA;EACA,0BAAA;EACA,sBAAA;CjDgnKH;AiDtnKC;;;;;;;;;EAYI,eAAA;CjDqnKL;AiDjoKC;;;EAeI,eAAA;CjDunKL;AkDztKC;EACE,eAAA;EACA,0BAAA;ClD2tKH;AkDztKG;;EAEE,eAAA;ClD2tKL;AkD7tKG;;EAKI,eAAA;ClD4tKP;AkDztKK;;;;EAEE,eAAA;EACA,0BAAA;ClD6tKP;AkD3tKK;;;;;;EAGE,YAAA;EACA,0BAAA;EACA,sBAAA;ClDguKP;AkDtvKC;EACE,eAAA;EACA,0BAAA;ClDwvKH;AkDtvKG;;EAEE,eAAA;ClDwvKL;AkD1vKG;;EAKI,eAAA;ClDyvKP;AkDtvKK;;;;EAEE,eAAA;EACA,0BAAA;ClD0vKP;AkDxvKK;;;;;;EAGE,YAAA;EACA,0BAAA;EACA,sBAAA;ClD6vKP;AkDnxKC;EACE,eAAA;EACA,0BAAA;ClDqxKH;AkDnxKG;;EAEE,eAAA;ClDqxKL;AkDvxKG;;EAKI,eAAA;ClDsxKP;AkDnxKK;;;;EAEE,eAAA;EACA,0BAAA;ClDuxKP;AkDrxKK;;;;;;EAGE,YAAA;EACA,0BAAA;EACA,sBAAA;ClD0xKP;AkDhzKC;EACE,eAAA;EACA,0BAAA;ClDkzKH;AkDhzKG;;EAEE,eAAA;ClDkzKL;AkDpzKG;;EAKI,eAAA;ClDmzKP;AkDhzKK;;;;EAEE,eAAA;EACA,0BAAA;ClDozKP;AkDlzKK;;;;;;EAGE,YAAA;EACA,0BAAA;EACA,sBAAA;ClDuzKP;AiDttKD;EACE,cAAA;EACA,mBAAA;CjDwtKD;AiDttKD;EACE,iBAAA;EACA,iBAAA;CjDwtKD;AmDl1KD;EACE,oBAAA;EACA,0BAAA;EACA,8BAAA;EACA,mBAAA;E9C0DA,kDAAA;EACQ,0CAAA;CL2xKT;AmDj1KD;EACE,cAAA;CnDm1KD;AmD90KD;EACE,mBAAA;EACA,qCAAA;EvBpBA,6BAAA;EACC,4BAAA;C5Bq2KF;AmDp1KD;EAMI,eAAA;CnDi1KH;AmD50KD;EACE,cAAA;EACA,iBAAA;EACA,gBAAA;EACA,eAAA;CnD80KD;AmDl1KD;;;;;EAWI,eAAA;CnD80KH;AmDz0KD;EACE,mBAAA;EACA,0BAAA;EACA,8BAAA;EvBxCA,gCAAA;EACC,+BAAA;C5Bo3KF;AmDn0KD;;EAGI,iBAAA;CnDo0KH;AmDv0KD;;EAMM,oBAAA;EACA,iBAAA;CnDq0KL;AmDj0KG;;EAEI,cAAA;EvBvEN,6BAAA;EACC,4BAAA;C5B24KF;AmD/zKG;;EAEI,iBAAA;EvBvEN,gCAAA;EACC,+BAAA;C5By4KF;AmDx1KD;EvB1DE,2BAAA;EACC,0BAAA;C5Bq5KF;AmD3zKD;EAEI,oBAAA;CnD4zKH;AmDzzKD;EACE,oBAAA;CnD2zKD;AmDnzKD;;;EAII,iBAAA;CnDozKH;AmDxzKD;;;EAOM,mBAAA;EACA,oBAAA;CnDszKL;AmD9zKD;;EvBzGE,6BAAA;EACC,4BAAA;C5B26KF;AmDn0KD;;;;EAmBQ,4BAAA;EACA,6BAAA;CnDszKP;AmD10KD;;;;;;;;EAwBU,4BAAA;CnD4zKT;AmDp1KD;;;;;;;;EA4BU,6BAAA;CnDk0KT;AmD91KD;;EvBjGE,gCAAA;EACC,+BAAA;C5Bm8KF;AmDn2KD;;;;EAyCQ,+BAAA;EACA,gCAAA;CnDg0KP;AmD12KD;;;;;;;;EA8CU,+BAAA;CnDs0KT;AmDp3KD;;;;;;;;EAkDU,gCAAA;CnD40KT;AmD93KD;;;;EA2DI,8BAAA;CnDy0KH;AmDp4KD;;EA+DI,cAAA;CnDy0KH;AmDx4KD;;EAmEI,UAAA;CnDy0KH;AmD54KD;;;;;;;;;;;;EA0EU,eAAA;CnDg1KT;AmD15KD;;;;;;;;;;;;EA8EU,gBAAA;CnD01KT;AmDx6KD;;;;;;;;EAuFU,iBAAA;CnD21KT;AmDl7KD;;;;;;;;EAgGU,iBAAA;CnD41KT;AmD57KD;EAsGI,UAAA;EACA,iBAAA;CnDy1KH;AmD/0KD;EACE,oBAAA;CnDi1KD;AmDl1KD;EAKI,iBAAA;EACA,mBAAA;CnDg1KH;AmDt1KD;EASM,gBAAA;CnDg1KL;AmDz1KD;EAcI,iBAAA;CnD80KH;AmD51KD;;EAkBM,8BAAA;CnD80KL;AmDh2KD;EAuBI,cAAA;CnD40KH;AmDn2KD;EAyBM,iCAAA;CnD60KL;AmDt0KD;EC1PE,sBAAA;CpDmkLD;AoDjkLC;EACE,eAAA;EACA,0BAAA;EACA,sBAAA;CpDmkLH;AoDtkLC;EAMI,0BAAA;CpDmkLL;AoDzkLC;EASI,eAAA;EACA,0BAAA;CpDmkLL;AoDhkLC;EAEI,6BAAA;CpDikLL;AmDr1KD;EC7PE,sBAAA;CpDqlLD;AoDnlLC;EACE,eAAA;EACA,0BAAA;EACA,sBAAA;CpDqlLH;AoDxlLC;EAMI,0BAAA;CpDqlLL;AoD3lLC;EASI,eAAA;EACA,0BAAA;CpDqlLL;AoDllLC;EAEI,6BAAA;CpDmlLL;AmDp2KD;EChQE,sBAAA;CpDumLD;AoDrmLC;EACE,eAAA;EACA,0BAAA;EACA,sBAAA;CpDumLH;AoD1mLC;EAMI,0BAAA;CpDumLL;AoD7mLC;EASI,eAAA;EACA,0BAAA;CpDumLL;AoDpmLC;EAEI,6BAAA;CpDqmLL;AmDn3KD;ECnQE,sBAAA;CpDynLD;AoDvnLC;EACE,eAAA;EACA,0BAAA;EACA,sBAAA;CpDynLH;AoD5nLC;EAMI,0BAAA;CpDynLL;AoD/nLC;EASI,eAAA;EACA,0BAAA;CpDynLL;AoDtnLC;EAEI,6BAAA;CpDunLL;AmDl4KD;ECtQE,sBAAA;CpD2oLD;AoDzoLC;EACE,eAAA;EACA,0BAAA;EACA,sBAAA;CpD2oLH;AoD9oLC;EAMI,0BAAA;CpD2oLL;AoDjpLC;EASI,eAAA;EACA,0BAAA;CpD2oLL;AoDxoLC;EAEI,6BAAA;CpDyoLL;AmDj5KD;ECzQE,sBAAA;CpD6pLD;AoD3pLC;EACE,eAAA;EACA,0BAAA;EACA,sBAAA;CpD6pLH;AoDhqLC;EAMI,0BAAA;CpD6pLL;AoDnqLC;EASI,eAAA;EACA,0BAAA;CpD6pLL;AoD1pLC;EAEI,6BAAA;CpD2pLL;AqD3qLD;EACE,mBAAA;EACA,eAAA;EACA,UAAA;EACA,WAAA;EACA,iBAAA;CrD6qLD;AqDlrLD;;;;;EAYI,mBAAA;EACA,OAAA;EACA,QAAA;EACA,UAAA;EACA,aAAA;EACA,YAAA;EACA,UAAA;CrD6qLH;AqDxqLD;EACE,uBAAA;CrD0qLD;AqDtqLD;EACE,oBAAA;CrDwqLD;AsDnsLD;EACE,iBAAA;EACA,cAAA;EACA,oBAAA;EACA,0BAAA;EACA,0BAAA;EACA,mBAAA;EjDwDA,wDAAA;EACQ,gDAAA;CL8oLT;AsD7sLD;EASI,mBAAA;EACA,kCAAA;CtDusLH;AsDlsLD;EACE,cAAA;EACA,mBAAA;CtDosLD;AsDlsLD;EACE,aAAA;EACA,mBAAA;CtDosLD;AuD1tLD;EACE,aAAA;EACA,gBAAA;EACA,kBAAA;EACA,eAAA;EACA,eAAA;EACA,6BAAA;EjCRA,aAAA;EAGA,0BAAA;CtBmuLD;AuD3tLC;;EAEE,eAAA;EACA,sBAAA;EACA,gBAAA;EjCfF,aAAA;EAGA,0BAAA;CtB2uLD;AuDvtLC;EACE,WAAA;EACA,gBAAA;EACA,wBAAA;EACA,UAAA;EACA,yBAAA;CvDytLH;AwD9uLD;EACE,iBAAA;CxDgvLD;AwD5uLD;EACE,cAAA;EACA,iBAAA;EACA,gBAAA;EACA,OAAA;EACA,SAAA;EACA,UAAA;EACA,QAAA;EACA,cAAA;EACA,kCAAA;EAIA,WAAA;CxD2uLD;AwDxuLC;EnD+GA,sCAAA;EACI,kCAAA;EACC,iCAAA;EACG,8BAAA;EAkER,oDAAA;EAEK,0CAAA;EACG,oCAAA;CL2jLT;AwD9uLC;EnD2GA,mCAAA;EACI,+BAAA;EACC,8BAAA;EACG,2BAAA;CLsoLT;AwDlvLD;EACE,mBAAA;EACA,iBAAA;CxDovLD;AwDhvLD;EACE,mBAAA;EACA,YAAA;EACA,aAAA;CxDkvLD;AwD9uLD;EACE,mBAAA;EACA,0BAAA;EACA,0BAAA;EACA,qCAAA;EACA,mBAAA;EnDaA,iDAAA;EACQ,yCAAA;EmDZR,qCAAA;UAAA,6BAAA;EAEA,WAAA;CxDgvLD;AwD5uLD;EACE,gBAAA;EACA,OAAA;EACA,SAAA;EACA,UAAA;EACA,QAAA;EACA,cAAA;EACA,0BAAA;CxD8uLD;AwD5uLC;ElCrEA,WAAA;EAGA,yBAAA;CtBkzLD;AwD/uLC;ElCtEA,aAAA;EAGA,0BAAA;CtBszLD;AwD9uLD;EACE,cAAA;EACA,iCAAA;EACA,0BAAA;CxDgvLD;AwD7uLD;EACE,iBAAA;CxD+uLD;AwD3uLD;EACE,UAAA;EACA,wBAAA;CxD6uLD;AwDxuLD;EACE,mBAAA;EACA,cAAA;CxD0uLD;AwDtuLD;EACE,cAAA;EACA,kBAAA;EACA,8BAAA;CxDwuLD;AwD3uLD;EAQI,iBAAA;EACA,iBAAA;CxDsuLH;AwD/uLD;EAaI,kBAAA;CxDquLH;AwDlvLD;EAiBI,eAAA;CxDouLH;AwD/tLD;EACE,mBAAA;EACA,aAAA;EACA,YAAA;EACA,aAAA;EACA,iBAAA;CxDiuLD;AwD/sLD;EAZE;IACE,aAAA;IACA,kBAAA;GxD8tLD;EwD5tLD;InDvEA,kDAAA;IACQ,0CAAA;GLsyLP;EwD3tLD;IAAY,aAAA;GxD8tLX;CACF;AwDztLD;EAFE;IAAY,aAAA;GxD+tLX;CACF;AyD92LD;EACE,mBAAA;EACA,cAAA;EACA,eAAA;ECRA,4DAAA;EAEA,mBAAA;EACA,oBAAA;EACA,uBAAA;EACA,iBAAA;EACA,wBAAA;EACA,iBAAA;EACA,kBAAA;EACA,sBAAA;EACA,kBAAA;EACA,qBAAA;EACA,oBAAA;EACA,mBAAA;EACA,qBAAA;EACA,kBAAA;EDHA,gBAAA;EnCVA,WAAA;EAGA,yBAAA;CtBq4LD;AyD13LC;EnCdA,aAAA;EAGA,0BAAA;CtBy4LD;AyD73LC;EAAW,iBAAA;EAAmB,eAAA;CzDi4L/B;AyDh4LC;EAAW,iBAAA;EAAmB,eAAA;CzDo4L/B;AyDn4LC;EAAW,gBAAA;EAAmB,eAAA;CzDu4L/B;AyDt4LC;EAAW,kBAAA;EAAmB,eAAA;CzD04L/B;AyDt4LD;EACE,iBAAA;EACA,iBAAA;EACA,eAAA;EACA,mBAAA;EACA,0BAAA;EACA,mBAAA;CzDw4LD;AyDp4LD;EACE,mBAAA;EACA,SAAA;EACA,UAAA;EACA,0BAAA;EACA,oBAAA;CzDs4LD;AyDl4LC;EACE,UAAA;EACA,UAAA;EACA,kBAAA;EACA,wBAAA;EACA,0BAAA;CzDo4LH;AyDl4LC;EACE,UAAA;EACA,WAAA;EACA,oBAAA;EACA,wBAAA;EACA,0BAAA;CzDo4LH;AyDl4LC;EACE,UAAA;EACA,UAAA;EACA,oBAAA;EACA,wBAAA;EACA,0BAAA;CzDo4LH;AyDl4LC;EACE,SAAA;EACA,QAAA;EACA,iBAAA;EACA,4BAAA;EACA,4BAAA;CzDo4LH;AyDl4LC;EACE,SAAA;EACA,SAAA;EACA,iBAAA;EACA,4BAAA;EACA,2BAAA;CzDo4LH;AyDl4LC;EACE,OAAA;EACA,UAAA;EACA,kBAAA;EACA,wBAAA;EACA,6BAAA;CzDo4LH;AyDl4LC;EACE,OAAA;EACA,WAAA;EACA,iBAAA;EACA,wBAAA;EACA,6BAAA;CzDo4LH;AyDl4LC;EACE,OAAA;EACA,UAAA;EACA,iBAAA;EACA,wBAAA;EACA,6BAAA;CzDo4LH;A2Dj+LD;EACE,mBAAA;EACA,OAAA;EACA,QAAA;EACA,cAAA;EACA,cAAA;EACA,iBAAA;EACA,aAAA;EDXA,4DAAA;EAEA,mBAAA;EACA,oBAAA;EACA,uBAAA;EACA,iBAAA;EACA,wBAAA;EACA,iBAAA;EACA,kBAAA;EACA,sBAAA;EACA,kBAAA;EACA,qBAAA;EACA,oBAAA;EACA,mBAAA;EACA,qBAAA;EACA,kBAAA;ECAA,gBAAA;EAEA,0BAAA;EACA,qCAAA;UAAA,6BAAA;EACA,0BAAA;EACA,qCAAA;EACA,mBAAA;EtD8CA,kDAAA;EACQ,0CAAA;CLi8LT;A2D5+LC;EAAY,kBAAA;C3D++Lb;A2D9+LC;EAAY,kBAAA;C3Di/Lb;A2Dh/LC;EAAY,iBAAA;C3Dm/Lb;A2Dl/LC;EAAY,mBAAA;C3Dq/Lb;A2Dl/LD;EACE,UAAA;EACA,kBAAA;EACA,gBAAA;EACA,0BAAA;EACA,iCAAA;EACA,2BAAA;C3Do/LD;A2Dj/LD;EACE,kBAAA;C3Dm/LD;A2D3+LC;;EAEE,mBAAA;EACA,eAAA;EACA,SAAA;EACA,UAAA;EACA,0BAAA;EACA,oBAAA;C3D6+LH;A2D1+LD;EACE,mBAAA;C3D4+LD;A2D1+LD;EACE,mBAAA;EACA,YAAA;C3D4+LD;A2Dx+LC;EACE,UAAA;EACA,mBAAA;EACA,uBAAA;EACA,0BAAA;EACA,sCAAA;EACA,cAAA;C3D0+LH;A2Dz+LG;EACE,aAAA;EACA,YAAA;EACA,mBAAA;EACA,uBAAA;EACA,0BAAA;C3D2+LL;A2Dx+LC;EACE,SAAA;EACA,YAAA;EACA,kBAAA;EACA,qBAAA;EACA,4BAAA;EACA,wCAAA;C3D0+LH;A2Dz+LG;EACE,aAAA;EACA,UAAA;EACA,cAAA;EACA,qBAAA;EACA,4BAAA;C3D2+LL;A2Dx+LC;EACE,UAAA;EACA,mBAAA;EACA,oBAAA;EACA,6BAAA;EACA,yCAAA;EACA,WAAA;C3D0+LH;A2Dz+LG;EACE,aAAA;EACA,SAAA;EACA,mBAAA;EACA,oBAAA;EACA,6BAAA;C3D2+LL;A2Dv+LC;EACE,SAAA;EACA,aAAA;EACA,kBAAA;EACA,sBAAA;EACA,2BAAA;EACA,uCAAA;C3Dy+LH;A2Dx+LG;EACE,aAAA;EACA,WAAA;EACA,sBAAA;EACA,2BAAA;EACA,cAAA;C3D0+LL;A4DnmMD;EACE,mBAAA;C5DqmMD;A4DlmMD;EACE,mBAAA;EACA,iBAAA;EACA,YAAA;C5DomMD;A4DvmMD;EAMI,cAAA;EACA,mBAAA;EvD6KF,0CAAA;EACK,qCAAA;EACG,kCAAA;CLw7LT;A4D9mMD;;EAcM,eAAA;C5DomML;A4D1kMC;EAAA;IvDiKA,uDAAA;IAEK,6CAAA;IACG,uCAAA;IA7JR,oCAAA;IAEQ,4BAAA;IA+GR,4BAAA;IAEQ,oBAAA;GL69LP;E4DxmMG;;IvDmHJ,2CAAA;IACQ,mCAAA;IuDjHF,QAAA;G5D2mML;E4DzmMG;;IvD8GJ,4CAAA;IACQ,oCAAA;IuD5GF,QAAA;G5D4mML;E4D1mMG;;;IvDyGJ,wCAAA;IACQ,gCAAA;IuDtGF,QAAA;G5D6mML;CACF;A4DnpMD;;;EA6CI,eAAA;C5D2mMH;A4DxpMD;EAiDI,QAAA;C5D0mMH;A4D3pMD;;EAsDI,mBAAA;EACA,OAAA;EACA,YAAA;C5DymMH;A4DjqMD;EA4DI,WAAA;C5DwmMH;A4DpqMD;EA+DI,YAAA;C5DwmMH;A4DvqMD;;EAmEI,QAAA;C5DwmMH;A4D3qMD;EAuEI,YAAA;C5DumMH;A4D9qMD;EA0EI,WAAA;C5DumMH;A4D/lMD;EACE,mBAAA;EACA,OAAA;EACA,QAAA;EACA,UAAA;EACA,WAAA;EtC9FA,aAAA;EAGA,0BAAA;EsC6FA,gBAAA;EACA,eAAA;EACA,mBAAA;EACA,0CAAA;C5DkmMD;A4D7lMC;EdlGE,mGAAA;EACA,8FAAA;EACA,qHAAA;EAAA,+FAAA;EACA,4BAAA;EACA,uHAAA;C9CksMH;A4DjmMC;EACE,WAAA;EACA,SAAA;EdvGA,mGAAA;EACA,8FAAA;EACA,qHAAA;EAAA,+FAAA;EACA,4BAAA;EACA,uHAAA;C9C2sMH;A4DnmMC;;EAEE,WAAA;EACA,eAAA;EACA,sBAAA;EtCtHF,aAAA;EAGA,0BAAA;CtB0tMD;A4DpoMD;;;;EAsCI,mBAAA;EACA,SAAA;EACA,kBAAA;EACA,WAAA;EACA,sBAAA;C5DomMH;A4D9oMD;;EA8CI,UAAA;EACA,mBAAA;C5DomMH;A4DnpMD;;EAmDI,WAAA;EACA,oBAAA;C5DomMH;A4DxpMD;;EAwDI,YAAA;EACA,aAAA;EACA,eAAA;EACA,mBAAA;C5DomMH;A4D/lMG;EACE,iBAAA;C5DimML;A4D7lMG;EACE,iBAAA;C5D+lML;A4DrlMD;EACE,mBAAA;EACA,aAAA;EACA,UAAA;EACA,YAAA;EACA,WAAA;EACA,kBAAA;EACA,gBAAA;EACA,iBAAA;EACA,mBAAA;C5DulMD;A4DhmMD;EAYI,sBAAA;EACA,YAAA;EACA,aAAA;EACA,YAAA;EACA,oBAAA;EACA,0BAAA;EACA,oBAAA;EACA,gBAAA;EAWA,0BAAA;EACA,mCAAA;C5D6kMH;A4D5mMD;EAkCI,UAAA;EACA,YAAA;EACA,aAAA;EACA,0BAAA;C5D6kMH;A4DtkMD;EACE,mBAAA;EACA,UAAA;EACA,WAAA;EACA,aAAA;EACA,YAAA;EACA,kBAAA;EACA,qBAAA;EACA,eAAA;EACA,mBAAA;EACA,0CAAA;C5DwkMD;A4DvkMC;EACE,kBAAA;C5DykMH;A4DhiMD;EAhCE;;;;IAKI,YAAA;IACA,aAAA;IACA,kBAAA;IACA,gBAAA;G5DkkMH;E4D1kMD;;IAYI,mBAAA;G5DkkMH;E4D9kMD;;IAgBI,oBAAA;G5DkkMH;E4D7jMD;IACE,UAAA;IACA,WAAA;IACA,qBAAA;G5D+jMD;E4D3jMD;IACE,aAAA;G5D6jMD;CACF;A6D3zMC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAEE,aAAA;EACA,eAAA;C7Dy1MH;A6Dv1MC;;;;;;;;;;;;;;;EACE,YAAA;C7Du2MH;AiC/2MD;E6BRE,eAAA;EACA,kBAAA;EACA,mBAAA;C9D03MD;AiCj3MD;EACE,wBAAA;CjCm3MD;AiCj3MD;EACE,uBAAA;CjCm3MD;AiC32MD;EACE,yBAAA;CjC62MD;AiC32MD;EACE,0BAAA;CjC62MD;AiC32MD;EACE,mBAAA;CjC62MD;AiC32MD;E8BzBE,YAAA;EACA,mBAAA;EACA,kBAAA;EACA,8BAAA;EACA,UAAA;C/Du4MD;AiCz2MD;EACE,yBAAA;CjC22MD;AiCp2MD;EACE,gBAAA;CjCs2MD;AgEv4MD;EACE,oBAAA;ChEy4MD;AgEn4MD;;;;ECdE,yBAAA;CjEu5MD;AgEl4MD;;;;;;;;;;;;EAYE,yBAAA;ChEo4MD;AgE73MD;EAAA;IChDE,0BAAA;GjEi7MC;EiEh7MD;IAAU,0BAAA;GjEm7MT;EiEl7MD;IAAU,8BAAA;GjEq7MT;EiEp7MD;;IACU,+BAAA;GjEu7MT;CACF;AgEv4MD;EAAA;IAFI,0BAAA;GhE64MD;CACF;AgEv4MD;EAAA;IAFI,2BAAA;GhE64MD;CACF;AgEv4MD;EAAA;IAFI,iCAAA;GhE64MD;CACF;AgEt4MD;EAAA;ICrEE,0BAAA;GjE+8MC;EiE98MD;IAAU,0BAAA;GjEi9MT;EiEh9MD;IAAU,8BAAA;GjEm9MT;EiEl9MD;;IACU,+BAAA;GjEq9MT;CACF;AgEh5MD;EAAA;IAFI,0BAAA;GhEs5MD;CACF;AgEh5MD;EAAA;IAFI,2BAAA;GhEs5MD;CACF;AgEh5MD;EAAA;IAFI,iCAAA;GhEs5MD;CACF;AgE/4MD;EAAA;IC1FE,0BAAA;GjE6+MC;EiE5+MD;IAAU,0BAAA;GjE++MT;EiE9+MD;IAAU,8BAAA;GjEi/MT;EiEh/MD;;IACU,+BAAA;GjEm/MT;CACF;AgEz5MD;EAAA;IAFI,0BAAA;GhE+5MD;CACF;AgEz5MD;EAAA;IAFI,2BAAA;GhE+5MD;CACF;AgEz5MD;EAAA;IAFI,iCAAA;GhE+5MD;CACF;AgEx5MD;EAAA;IC/GE,0BAAA;GjE2gNC;EiE1gND;IAAU,0BAAA;GjE6gNT;EiE5gND;IAAU,8BAAA;GjE+gNT;EiE9gND;;IACU,+BAAA;GjEihNT;CACF;AgEl6MD;EAAA;IAFI,0BAAA;GhEw6MD;CACF;AgEl6MD;EAAA;IAFI,2BAAA;GhEw6MD;CACF;AgEl6MD;EAAA;IAFI,iCAAA;GhEw6MD;CACF;AgEj6MD;EAAA;IC5HE,yBAAA;GjEiiNC;CACF;AgEj6MD;EAAA;ICjIE,yBAAA;GjEsiNC;CACF;AgEj6MD;EAAA;ICtIE,yBAAA;GjE2iNC;CACF;AgEj6MD;EAAA;IC3IE,yBAAA;GjEgjNC;CACF;AgE95MD;ECnJE,yBAAA;CjEojND;AgE35MD;EAAA;ICjKE,0BAAA;GjEgkNC;EiE/jND;IAAU,0BAAA;GjEkkNT;EiEjkND;IAAU,8BAAA;GjEokNT;EiEnkND;;IACU,+BAAA;GjEskNT;CACF;AgEz6MD;EACE,yBAAA;ChE26MD;AgEt6MD;EAAA;IAFI,0BAAA;GhE46MD;CACF;AgE16MD;EACE,yBAAA;ChE46MD;AgEv6MD;EAAA;IAFI,2BAAA;GhE66MD;CACF;AgE36MD;EACE,yBAAA;ChE66MD;AgEx6MD;EAAA;IAFI,iCAAA;GhE86MD;CACF;AgEv6MD;EAAA;ICpLE,yBAAA;GjE+lNC;CACF","file":"bootstrap.css","sourcesContent":["/*!\n * Bootstrap v3.3.5 (http://getbootstrap.com)\n * Copyright 2011-2015 Twitter, Inc.\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)\n */\n/*! normalize.css v3.0.3 | MIT License | github.com/necolas/normalize.css */\nhtml {\n font-family: sans-serif;\n -ms-text-size-adjust: 100%;\n -webkit-text-size-adjust: 100%;\n}\nbody {\n margin: 0;\n}\narticle,\naside,\ndetails,\nfigcaption,\nfigure,\nfooter,\nheader,\nhgroup,\nmain,\nmenu,\nnav,\nsection,\nsummary {\n display: block;\n}\naudio,\ncanvas,\nprogress,\nvideo {\n display: inline-block;\n vertical-align: baseline;\n}\naudio:not([controls]) {\n display: none;\n height: 0;\n}\n[hidden],\ntemplate {\n display: none;\n}\na {\n background-color: transparent;\n}\na:active,\na:hover {\n outline: 0;\n}\nabbr[title] {\n border-bottom: 1px dotted;\n}\nb,\nstrong {\n font-weight: bold;\n}\ndfn {\n font-style: italic;\n}\nh1 {\n font-size: 2em;\n margin: 0.67em 0;\n}\nmark {\n background: #ff0;\n color: #000;\n}\nsmall {\n font-size: 80%;\n}\nsub,\nsup {\n font-size: 75%;\n line-height: 0;\n position: relative;\n vertical-align: baseline;\n}\nsup {\n top: -0.5em;\n}\nsub {\n bottom: -0.25em;\n}\nimg {\n border: 0;\n}\nsvg:not(:root) {\n overflow: hidden;\n}\nfigure {\n margin: 1em 40px;\n}\nhr {\n box-sizing: content-box;\n height: 0;\n}\npre {\n overflow: auto;\n}\ncode,\nkbd,\npre,\nsamp {\n font-family: monospace, monospace;\n font-size: 1em;\n}\nbutton,\ninput,\noptgroup,\nselect,\ntextarea {\n color: inherit;\n font: inherit;\n margin: 0;\n}\nbutton {\n overflow: visible;\n}\nbutton,\nselect {\n text-transform: none;\n}\nbutton,\nhtml input[type=\"button\"],\ninput[type=\"reset\"],\ninput[type=\"submit\"] {\n -webkit-appearance: button;\n cursor: pointer;\n}\nbutton[disabled],\nhtml input[disabled] {\n cursor: default;\n}\nbutton::-moz-focus-inner,\ninput::-moz-focus-inner {\n border: 0;\n padding: 0;\n}\ninput {\n line-height: normal;\n}\ninput[type=\"checkbox\"],\ninput[type=\"radio\"] {\n box-sizing: border-box;\n padding: 0;\n}\ninput[type=\"number\"]::-webkit-inner-spin-button,\ninput[type=\"number\"]::-webkit-outer-spin-button {\n height: auto;\n}\ninput[type=\"search\"] {\n -webkit-appearance: textfield;\n box-sizing: content-box;\n}\ninput[type=\"search\"]::-webkit-search-cancel-button,\ninput[type=\"search\"]::-webkit-search-decoration {\n -webkit-appearance: none;\n}\nfieldset {\n border: 1px solid #c0c0c0;\n margin: 0 2px;\n padding: 0.35em 0.625em 0.75em;\n}\nlegend {\n border: 0;\n padding: 0;\n}\ntextarea {\n overflow: auto;\n}\noptgroup {\n font-weight: bold;\n}\ntable {\n border-collapse: collapse;\n border-spacing: 0;\n}\ntd,\nth {\n padding: 0;\n}\n/*! Source: https://github.com/h5bp/html5-boilerplate/blob/master/src/css/main.css */\n@media print {\n *,\n *:before,\n *:after {\n background: transparent !important;\n color: #000 !important;\n box-shadow: none !important;\n text-shadow: none !important;\n }\n a,\n a:visited {\n text-decoration: underline;\n }\n a[href]:after {\n content: \" (\" attr(href) \")\";\n }\n abbr[title]:after {\n content: \" (\" attr(title) \")\";\n }\n a[href^=\"#\"]:after,\n a[href^=\"javascript:\"]:after {\n content: \"\";\n }\n pre,\n blockquote {\n border: 1px solid #999;\n page-break-inside: avoid;\n }\n thead {\n display: table-header-group;\n }\n tr,\n img {\n page-break-inside: avoid;\n }\n img {\n max-width: 100% !important;\n }\n p,\n h2,\n h3 {\n orphans: 3;\n widows: 3;\n }\n h2,\n h3 {\n page-break-after: avoid;\n }\n .navbar {\n display: none;\n }\n .btn > .caret,\n .dropup > .btn > .caret {\n border-top-color: #000 !important;\n }\n .label {\n border: 1px solid #000;\n }\n .table {\n border-collapse: collapse !important;\n }\n .table td,\n .table th {\n background-color: #fff !important;\n }\n .table-bordered th,\n .table-bordered td {\n border: 1px solid #ddd !important;\n }\n}\n@font-face {\n font-family: 'Glyphicons Halflings';\n src: url('../fonts/glyphicons-halflings-regular.eot');\n src: url('../fonts/glyphicons-halflings-regular.eot?#iefix') format('embedded-opentype'), url('../fonts/glyphicons-halflings-regular.woff2') format('woff2'), url('../fonts/glyphicons-halflings-regular.woff') format('woff'), url('../fonts/glyphicons-halflings-regular.ttf') format('truetype'), url('../fonts/glyphicons-halflings-regular.svg#glyphicons_halflingsregular') format('svg');\n}\n.glyphicon {\n position: relative;\n top: 1px;\n display: inline-block;\n font-family: 'Glyphicons Halflings';\n font-style: normal;\n font-weight: normal;\n line-height: 1;\n -webkit-font-smoothing: antialiased;\n -moz-osx-font-smoothing: grayscale;\n}\n.glyphicon-asterisk:before {\n content: \"\\2a\";\n}\n.glyphicon-plus:before {\n content: \"\\2b\";\n}\n.glyphicon-euro:before,\n.glyphicon-eur:before {\n content: \"\\20ac\";\n}\n.glyphicon-minus:before {\n content: \"\\2212\";\n}\n.glyphicon-cloud:before {\n content: \"\\2601\";\n}\n.glyphicon-envelope:before {\n content: \"\\2709\";\n}\n.glyphicon-pencil:before {\n content: \"\\270f\";\n}\n.glyphicon-glass:before {\n content: \"\\e001\";\n}\n.glyphicon-music:before {\n content: \"\\e002\";\n}\n.glyphicon-search:before {\n content: \"\\e003\";\n}\n.glyphicon-heart:before {\n content: \"\\e005\";\n}\n.glyphicon-star:before {\n content: \"\\e006\";\n}\n.glyphicon-star-empty:before {\n content: \"\\e007\";\n}\n.glyphicon-user:before {\n content: \"\\e008\";\n}\n.glyphicon-film:before {\n content: \"\\e009\";\n}\n.glyphicon-th-large:before {\n content: \"\\e010\";\n}\n.glyphicon-th:before {\n content: \"\\e011\";\n}\n.glyphicon-th-list:before {\n content: \"\\e012\";\n}\n.glyphicon-ok:before {\n content: \"\\e013\";\n}\n.glyphicon-remove:before {\n content: \"\\e014\";\n}\n.glyphicon-zoom-in:before {\n content: \"\\e015\";\n}\n.glyphicon-zoom-out:before {\n content: \"\\e016\";\n}\n.glyphicon-off:before {\n content: \"\\e017\";\n}\n.glyphicon-signal:before {\n content: \"\\e018\";\n}\n.glyphicon-cog:before {\n content: \"\\e019\";\n}\n.glyphicon-trash:before {\n content: \"\\e020\";\n}\n.glyphicon-home:before {\n content: \"\\e021\";\n}\n.glyphicon-file:before {\n content: \"\\e022\";\n}\n.glyphicon-time:before {\n content: \"\\e023\";\n}\n.glyphicon-road:before {\n content: \"\\e024\";\n}\n.glyphicon-download-alt:before {\n content: \"\\e025\";\n}\n.glyphicon-download:before {\n content: \"\\e026\";\n}\n.glyphicon-upload:before {\n content: \"\\e027\";\n}\n.glyphicon-inbox:before {\n content: \"\\e028\";\n}\n.glyphicon-play-circle:before {\n content: \"\\e029\";\n}\n.glyphicon-repeat:before {\n content: \"\\e030\";\n}\n.glyphicon-refresh:before {\n content: \"\\e031\";\n}\n.glyphicon-list-alt:before {\n content: \"\\e032\";\n}\n.glyphicon-lock:before {\n content: \"\\e033\";\n}\n.glyphicon-flag:before {\n content: \"\\e034\";\n}\n.glyphicon-headphones:before {\n content: \"\\e035\";\n}\n.glyphicon-volume-off:before {\n content: \"\\e036\";\n}\n.glyphicon-volume-down:before {\n content: \"\\e037\";\n}\n.glyphicon-volume-up:before {\n content: \"\\e038\";\n}\n.glyphicon-qrcode:before {\n content: \"\\e039\";\n}\n.glyphicon-barcode:before {\n content: \"\\e040\";\n}\n.glyphicon-tag:before {\n content: \"\\e041\";\n}\n.glyphicon-tags:before {\n content: \"\\e042\";\n}\n.glyphicon-book:before {\n content: \"\\e043\";\n}\n.glyphicon-bookmark:before {\n content: \"\\e044\";\n}\n.glyphicon-print:before {\n content: \"\\e045\";\n}\n.glyphicon-camera:before {\n content: \"\\e046\";\n}\n.glyphicon-font:before {\n content: \"\\e047\";\n}\n.glyphicon-bold:before {\n content: \"\\e048\";\n}\n.glyphicon-italic:before {\n content: \"\\e049\";\n}\n.glyphicon-text-height:before {\n content: \"\\e050\";\n}\n.glyphicon-text-width:before {\n content: \"\\e051\";\n}\n.glyphicon-align-left:before {\n content: \"\\e052\";\n}\n.glyphicon-align-center:before {\n content: \"\\e053\";\n}\n.glyphicon-align-right:before {\n content: \"\\e054\";\n}\n.glyphicon-align-justify:before {\n content: \"\\e055\";\n}\n.glyphicon-list:before {\n content: \"\\e056\";\n}\n.glyphicon-indent-left:before {\n content: \"\\e057\";\n}\n.glyphicon-indent-right:before {\n content: \"\\e058\";\n}\n.glyphicon-facetime-video:before {\n content: \"\\e059\";\n}\n.glyphicon-picture:before {\n content: \"\\e060\";\n}\n.glyphicon-map-marker:before {\n content: \"\\e062\";\n}\n.glyphicon-adjust:before {\n content: \"\\e063\";\n}\n.glyphicon-tint:before {\n content: \"\\e064\";\n}\n.glyphicon-edit:before {\n content: \"\\e065\";\n}\n.glyphicon-share:before {\n content: \"\\e066\";\n}\n.glyphicon-check:before {\n content: \"\\e067\";\n}\n.glyphicon-move:before {\n content: \"\\e068\";\n}\n.glyphicon-step-backward:before {\n content: \"\\e069\";\n}\n.glyphicon-fast-backward:before {\n content: \"\\e070\";\n}\n.glyphicon-backward:before {\n content: \"\\e071\";\n}\n.glyphicon-play:before {\n content: \"\\e072\";\n}\n.glyphicon-pause:before {\n content: \"\\e073\";\n}\n.glyphicon-stop:before {\n content: \"\\e074\";\n}\n.glyphicon-forward:before {\n content: \"\\e075\";\n}\n.glyphicon-fast-forward:before {\n content: \"\\e076\";\n}\n.glyphicon-step-forward:before {\n content: \"\\e077\";\n}\n.glyphicon-eject:before {\n content: \"\\e078\";\n}\n.glyphicon-chevron-left:before {\n content: \"\\e079\";\n}\n.glyphicon-chevron-right:before {\n content: \"\\e080\";\n}\n.glyphicon-plus-sign:before {\n content: \"\\e081\";\n}\n.glyphicon-minus-sign:before {\n content: \"\\e082\";\n}\n.glyphicon-remove-sign:before {\n content: \"\\e083\";\n}\n.glyphicon-ok-sign:before {\n content: \"\\e084\";\n}\n.glyphicon-question-sign:before {\n content: \"\\e085\";\n}\n.glyphicon-info-sign:before {\n content: \"\\e086\";\n}\n.glyphicon-screenshot:before {\n content: \"\\e087\";\n}\n.glyphicon-remove-circle:before {\n content: \"\\e088\";\n}\n.glyphicon-ok-circle:before {\n content: \"\\e089\";\n}\n.glyphicon-ban-circle:before {\n content: \"\\e090\";\n}\n.glyphicon-arrow-left:before {\n content: \"\\e091\";\n}\n.glyphicon-arrow-right:before {\n content: \"\\e092\";\n}\n.glyphicon-arrow-up:before {\n content: \"\\e093\";\n}\n.glyphicon-arrow-down:before {\n content: \"\\e094\";\n}\n.glyphicon-share-alt:before {\n content: \"\\e095\";\n}\n.glyphicon-resize-full:before {\n content: \"\\e096\";\n}\n.glyphicon-resize-small:before {\n content: \"\\e097\";\n}\n.glyphicon-exclamation-sign:before {\n content: \"\\e101\";\n}\n.glyphicon-gift:before {\n content: \"\\e102\";\n}\n.glyphicon-leaf:before {\n content: \"\\e103\";\n}\n.glyphicon-fire:before {\n content: \"\\e104\";\n}\n.glyphicon-eye-open:before {\n content: \"\\e105\";\n}\n.glyphicon-eye-close:before {\n content: \"\\e106\";\n}\n.glyphicon-warning-sign:before {\n content: \"\\e107\";\n}\n.glyphicon-plane:before {\n content: \"\\e108\";\n}\n.glyphicon-calendar:before {\n content: \"\\e109\";\n}\n.glyphicon-random:before {\n content: \"\\e110\";\n}\n.glyphicon-comment:before {\n content: \"\\e111\";\n}\n.glyphicon-magnet:before {\n content: \"\\e112\";\n}\n.glyphicon-chevron-up:before {\n content: \"\\e113\";\n}\n.glyphicon-chevron-down:before {\n content: \"\\e114\";\n}\n.glyphicon-retweet:before {\n content: \"\\e115\";\n}\n.glyphicon-shopping-cart:before {\n content: \"\\e116\";\n}\n.glyphicon-folder-close:before {\n content: \"\\e117\";\n}\n.glyphicon-folder-open:before {\n content: \"\\e118\";\n}\n.glyphicon-resize-vertical:before {\n content: \"\\e119\";\n}\n.glyphicon-resize-horizontal:before {\n content: \"\\e120\";\n}\n.glyphicon-hdd:before {\n content: \"\\e121\";\n}\n.glyphicon-bullhorn:before {\n content: \"\\e122\";\n}\n.glyphicon-bell:before {\n content: \"\\e123\";\n}\n.glyphicon-certificate:before {\n content: \"\\e124\";\n}\n.glyphicon-thumbs-up:before {\n content: \"\\e125\";\n}\n.glyphicon-thumbs-down:before {\n content: \"\\e126\";\n}\n.glyphicon-hand-right:before {\n content: \"\\e127\";\n}\n.glyphicon-hand-left:before {\n content: \"\\e128\";\n}\n.glyphicon-hand-up:before {\n content: \"\\e129\";\n}\n.glyphicon-hand-down:before {\n content: \"\\e130\";\n}\n.glyphicon-circle-arrow-right:before {\n content: \"\\e131\";\n}\n.glyphicon-circle-arrow-left:before {\n content: \"\\e132\";\n}\n.glyphicon-circle-arrow-up:before {\n content: \"\\e133\";\n}\n.glyphicon-circle-arrow-down:before {\n content: \"\\e134\";\n}\n.glyphicon-globe:before {\n content: \"\\e135\";\n}\n.glyphicon-wrench:before {\n content: \"\\e136\";\n}\n.glyphicon-tasks:before {\n content: \"\\e137\";\n}\n.glyphicon-filter:before {\n content: \"\\e138\";\n}\n.glyphicon-briefcase:before {\n content: \"\\e139\";\n}\n.glyphicon-fullscreen:before {\n content: \"\\e140\";\n}\n.glyphicon-dashboard:before {\n content: \"\\e141\";\n}\n.glyphicon-paperclip:before {\n content: \"\\e142\";\n}\n.glyphicon-heart-empty:before {\n content: \"\\e143\";\n}\n.glyphicon-link:before {\n content: \"\\e144\";\n}\n.glyphicon-phone:before {\n content: \"\\e145\";\n}\n.glyphicon-pushpin:before {\n content: \"\\e146\";\n}\n.glyphicon-usd:before {\n content: \"\\e148\";\n}\n.glyphicon-gbp:before {\n content: \"\\e149\";\n}\n.glyphicon-sort:before {\n content: \"\\e150\";\n}\n.glyphicon-sort-by-alphabet:before {\n content: \"\\e151\";\n}\n.glyphicon-sort-by-alphabet-alt:before {\n content: \"\\e152\";\n}\n.glyphicon-sort-by-order:before {\n content: \"\\e153\";\n}\n.glyphicon-sort-by-order-alt:before {\n content: \"\\e154\";\n}\n.glyphicon-sort-by-attributes:before {\n content: \"\\e155\";\n}\n.glyphicon-sort-by-attributes-alt:before {\n content: \"\\e156\";\n}\n.glyphicon-unchecked:before {\n content: \"\\e157\";\n}\n.glyphicon-expand:before {\n content: \"\\e158\";\n}\n.glyphicon-collapse-down:before {\n content: \"\\e159\";\n}\n.glyphicon-collapse-up:before {\n content: \"\\e160\";\n}\n.glyphicon-log-in:before {\n content: \"\\e161\";\n}\n.glyphicon-flash:before {\n content: \"\\e162\";\n}\n.glyphicon-log-out:before {\n content: \"\\e163\";\n}\n.glyphicon-new-window:before {\n content: \"\\e164\";\n}\n.glyphicon-record:before {\n content: \"\\e165\";\n}\n.glyphicon-save:before {\n content: \"\\e166\";\n}\n.glyphicon-open:before {\n content: \"\\e167\";\n}\n.glyphicon-saved:before {\n content: \"\\e168\";\n}\n.glyphicon-import:before {\n content: \"\\e169\";\n}\n.glyphicon-export:before {\n content: \"\\e170\";\n}\n.glyphicon-send:before {\n content: \"\\e171\";\n}\n.glyphicon-floppy-disk:before {\n content: \"\\e172\";\n}\n.glyphicon-floppy-saved:before {\n content: \"\\e173\";\n}\n.glyphicon-floppy-remove:before {\n content: \"\\e174\";\n}\n.glyphicon-floppy-save:before {\n content: \"\\e175\";\n}\n.glyphicon-floppy-open:before {\n content: \"\\e176\";\n}\n.glyphicon-credit-card:before {\n content: \"\\e177\";\n}\n.glyphicon-transfer:before {\n content: \"\\e178\";\n}\n.glyphicon-cutlery:before {\n content: \"\\e179\";\n}\n.glyphicon-header:before {\n content: \"\\e180\";\n}\n.glyphicon-compressed:before {\n content: \"\\e181\";\n}\n.glyphicon-earphone:before {\n content: \"\\e182\";\n}\n.glyphicon-phone-alt:before {\n content: \"\\e183\";\n}\n.glyphicon-tower:before {\n content: \"\\e184\";\n}\n.glyphicon-stats:before {\n content: \"\\e185\";\n}\n.glyphicon-sd-video:before {\n content: \"\\e186\";\n}\n.glyphicon-hd-video:before {\n content: \"\\e187\";\n}\n.glyphicon-subtitles:before {\n content: \"\\e188\";\n}\n.glyphicon-sound-stereo:before {\n content: \"\\e189\";\n}\n.glyphicon-sound-dolby:before {\n content: \"\\e190\";\n}\n.glyphicon-sound-5-1:before {\n content: \"\\e191\";\n}\n.glyphicon-sound-6-1:before {\n content: \"\\e192\";\n}\n.glyphicon-sound-7-1:before {\n content: \"\\e193\";\n}\n.glyphicon-copyright-mark:before {\n content: \"\\e194\";\n}\n.glyphicon-registration-mark:before {\n content: \"\\e195\";\n}\n.glyphicon-cloud-download:before {\n content: \"\\e197\";\n}\n.glyphicon-cloud-upload:before {\n content: \"\\e198\";\n}\n.glyphicon-tree-conifer:before {\n content: \"\\e199\";\n}\n.glyphicon-tree-deciduous:before {\n content: \"\\e200\";\n}\n.glyphicon-cd:before {\n content: \"\\e201\";\n}\n.glyphicon-save-file:before {\n content: \"\\e202\";\n}\n.glyphicon-open-file:before {\n content: \"\\e203\";\n}\n.glyphicon-level-up:before {\n content: \"\\e204\";\n}\n.glyphicon-copy:before {\n content: \"\\e205\";\n}\n.glyphicon-paste:before {\n content: \"\\e206\";\n}\n.glyphicon-alert:before {\n content: \"\\e209\";\n}\n.glyphicon-equalizer:before {\n content: \"\\e210\";\n}\n.glyphicon-king:before {\n content: \"\\e211\";\n}\n.glyphicon-queen:before {\n content: \"\\e212\";\n}\n.glyphicon-pawn:before {\n content: \"\\e213\";\n}\n.glyphicon-bishop:before {\n content: \"\\e214\";\n}\n.glyphicon-knight:before {\n content: \"\\e215\";\n}\n.glyphicon-baby-formula:before {\n content: \"\\e216\";\n}\n.glyphicon-tent:before {\n content: \"\\26fa\";\n}\n.glyphicon-blackboard:before {\n content: \"\\e218\";\n}\n.glyphicon-bed:before {\n content: \"\\e219\";\n}\n.glyphicon-apple:before {\n content: \"\\f8ff\";\n}\n.glyphicon-erase:before {\n content: \"\\e221\";\n}\n.glyphicon-hourglass:before {\n content: \"\\231b\";\n}\n.glyphicon-lamp:before {\n content: \"\\e223\";\n}\n.glyphicon-duplicate:before {\n content: \"\\e224\";\n}\n.glyphicon-piggy-bank:before {\n content: \"\\e225\";\n}\n.glyphicon-scissors:before {\n content: \"\\e226\";\n}\n.glyphicon-bitcoin:before {\n content: \"\\e227\";\n}\n.glyphicon-btc:before {\n content: \"\\e227\";\n}\n.glyphicon-xbt:before {\n content: \"\\e227\";\n}\n.glyphicon-yen:before {\n content: \"\\00a5\";\n}\n.glyphicon-jpy:before {\n content: \"\\00a5\";\n}\n.glyphicon-ruble:before {\n content: \"\\20bd\";\n}\n.glyphicon-rub:before {\n content: \"\\20bd\";\n}\n.glyphicon-scale:before {\n content: \"\\e230\";\n}\n.glyphicon-ice-lolly:before {\n content: \"\\e231\";\n}\n.glyphicon-ice-lolly-tasted:before {\n content: \"\\e232\";\n}\n.glyphicon-education:before {\n content: \"\\e233\";\n}\n.glyphicon-option-horizontal:before {\n content: \"\\e234\";\n}\n.glyphicon-option-vertical:before {\n content: \"\\e235\";\n}\n.glyphicon-menu-hamburger:before {\n content: \"\\e236\";\n}\n.glyphicon-modal-window:before {\n content: \"\\e237\";\n}\n.glyphicon-oil:before {\n content: \"\\e238\";\n}\n.glyphicon-grain:before {\n content: \"\\e239\";\n}\n.glyphicon-sunglasses:before {\n content: \"\\e240\";\n}\n.glyphicon-text-size:before {\n content: \"\\e241\";\n}\n.glyphicon-text-color:before {\n content: \"\\e242\";\n}\n.glyphicon-text-background:before {\n content: \"\\e243\";\n}\n.glyphicon-object-align-top:before {\n content: \"\\e244\";\n}\n.glyphicon-object-align-bottom:before {\n content: \"\\e245\";\n}\n.glyphicon-object-align-horizontal:before {\n content: \"\\e246\";\n}\n.glyphicon-object-align-left:before {\n content: \"\\e247\";\n}\n.glyphicon-object-align-vertical:before {\n content: \"\\e248\";\n}\n.glyphicon-object-align-right:before {\n content: \"\\e249\";\n}\n.glyphicon-triangle-right:before {\n content: \"\\e250\";\n}\n.glyphicon-triangle-left:before {\n content: \"\\e251\";\n}\n.glyphicon-triangle-bottom:before {\n content: \"\\e252\";\n}\n.glyphicon-triangle-top:before {\n content: \"\\e253\";\n}\n.glyphicon-console:before {\n content: \"\\e254\";\n}\n.glyphicon-superscript:before {\n content: \"\\e255\";\n}\n.glyphicon-subscript:before {\n content: \"\\e256\";\n}\n.glyphicon-menu-left:before {\n content: \"\\e257\";\n}\n.glyphicon-menu-right:before {\n content: \"\\e258\";\n}\n.glyphicon-menu-down:before {\n content: \"\\e259\";\n}\n.glyphicon-menu-up:before {\n content: \"\\e260\";\n}\n* {\n -webkit-box-sizing: border-box;\n -moz-box-sizing: border-box;\n box-sizing: border-box;\n}\n*:before,\n*:after {\n -webkit-box-sizing: border-box;\n -moz-box-sizing: border-box;\n box-sizing: border-box;\n}\nhtml {\n font-size: 10px;\n -webkit-tap-highlight-color: rgba(0, 0, 0, 0);\n}\nbody {\n font-family: \"Helvetica Neue\", Helvetica, Arial, sans-serif;\n font-size: 14px;\n line-height: 1.42857143;\n color: #333333;\n background-color: #ffffff;\n}\ninput,\nbutton,\nselect,\ntextarea {\n font-family: inherit;\n font-size: inherit;\n line-height: inherit;\n}\na {\n color: #337ab7;\n text-decoration: none;\n}\na:hover,\na:focus {\n color: #23527c;\n text-decoration: underline;\n}\na:focus {\n outline: thin dotted;\n outline: 5px auto -webkit-focus-ring-color;\n outline-offset: -2px;\n}\nfigure {\n margin: 0;\n}\nimg {\n vertical-align: middle;\n}\n.img-responsive,\n.thumbnail > img,\n.thumbnail a > img,\n.carousel-inner > .item > img,\n.carousel-inner > .item > a > img {\n display: block;\n max-width: 100%;\n height: auto;\n}\n.img-rounded {\n border-radius: 6px;\n}\n.img-thumbnail {\n padding: 4px;\n line-height: 1.42857143;\n background-color: #ffffff;\n border: 1px solid #dddddd;\n border-radius: 4px;\n -webkit-transition: all 0.2s ease-in-out;\n -o-transition: all 0.2s ease-in-out;\n transition: all 0.2s ease-in-out;\n display: inline-block;\n max-width: 100%;\n height: auto;\n}\n.img-circle {\n border-radius: 50%;\n}\nhr {\n margin-top: 20px;\n margin-bottom: 20px;\n border: 0;\n border-top: 1px solid #eeeeee;\n}\n.sr-only {\n position: absolute;\n width: 1px;\n height: 1px;\n margin: -1px;\n padding: 0;\n overflow: hidden;\n clip: rect(0, 0, 0, 0);\n border: 0;\n}\n.sr-only-focusable:active,\n.sr-only-focusable:focus {\n position: static;\n width: auto;\n height: auto;\n margin: 0;\n overflow: visible;\n clip: auto;\n}\n[role=\"button\"] {\n cursor: pointer;\n}\nh1,\nh2,\nh3,\nh4,\nh5,\nh6,\n.h1,\n.h2,\n.h3,\n.h4,\n.h5,\n.h6 {\n font-family: inherit;\n font-weight: 500;\n line-height: 1.1;\n color: inherit;\n}\nh1 small,\nh2 small,\nh3 small,\nh4 small,\nh5 small,\nh6 small,\n.h1 small,\n.h2 small,\n.h3 small,\n.h4 small,\n.h5 small,\n.h6 small,\nh1 .small,\nh2 .small,\nh3 .small,\nh4 .small,\nh5 .small,\nh6 .small,\n.h1 .small,\n.h2 .small,\n.h3 .small,\n.h4 .small,\n.h5 .small,\n.h6 .small {\n font-weight: normal;\n line-height: 1;\n color: #777777;\n}\nh1,\n.h1,\nh2,\n.h2,\nh3,\n.h3 {\n margin-top: 20px;\n margin-bottom: 10px;\n}\nh1 small,\n.h1 small,\nh2 small,\n.h2 small,\nh3 small,\n.h3 small,\nh1 .small,\n.h1 .small,\nh2 .small,\n.h2 .small,\nh3 .small,\n.h3 .small {\n font-size: 65%;\n}\nh4,\n.h4,\nh5,\n.h5,\nh6,\n.h6 {\n margin-top: 10px;\n margin-bottom: 10px;\n}\nh4 small,\n.h4 small,\nh5 small,\n.h5 small,\nh6 small,\n.h6 small,\nh4 .small,\n.h4 .small,\nh5 .small,\n.h5 .small,\nh6 .small,\n.h6 .small {\n font-size: 75%;\n}\nh1,\n.h1 {\n font-size: 36px;\n}\nh2,\n.h2 {\n font-size: 30px;\n}\nh3,\n.h3 {\n font-size: 24px;\n}\nh4,\n.h4 {\n font-size: 18px;\n}\nh5,\n.h5 {\n font-size: 14px;\n}\nh6,\n.h6 {\n font-size: 12px;\n}\np {\n margin: 0 0 10px;\n}\n.lead {\n margin-bottom: 20px;\n font-size: 16px;\n font-weight: 300;\n line-height: 1.4;\n}\n@media (min-width: 768px) {\n .lead {\n font-size: 21px;\n }\n}\nsmall,\n.small {\n font-size: 85%;\n}\nmark,\n.mark {\n background-color: #fcf8e3;\n padding: .2em;\n}\n.text-left {\n text-align: left;\n}\n.text-right {\n text-align: right;\n}\n.text-center {\n text-align: center;\n}\n.text-justify {\n text-align: justify;\n}\n.text-nowrap {\n white-space: nowrap;\n}\n.text-lowercase {\n text-transform: lowercase;\n}\n.text-uppercase {\n text-transform: uppercase;\n}\n.text-capitalize {\n text-transform: capitalize;\n}\n.text-muted {\n color: #777777;\n}\n.text-primary {\n color: #337ab7;\n}\na.text-primary:hover,\na.text-primary:focus {\n color: #286090;\n}\n.text-success {\n color: #3c763d;\n}\na.text-success:hover,\na.text-success:focus {\n color: #2b542c;\n}\n.text-info {\n color: #31708f;\n}\na.text-info:hover,\na.text-info:focus {\n color: #245269;\n}\n.text-warning {\n color: #8a6d3b;\n}\na.text-warning:hover,\na.text-warning:focus {\n color: #66512c;\n}\n.text-danger {\n color: #a94442;\n}\na.text-danger:hover,\na.text-danger:focus {\n color: #843534;\n}\n.bg-primary {\n color: #fff;\n background-color: #337ab7;\n}\na.bg-primary:hover,\na.bg-primary:focus {\n background-color: #286090;\n}\n.bg-success {\n background-color: #dff0d8;\n}\na.bg-success:hover,\na.bg-success:focus {\n background-color: #c1e2b3;\n}\n.bg-info {\n background-color: #d9edf7;\n}\na.bg-info:hover,\na.bg-info:focus {\n background-color: #afd9ee;\n}\n.bg-warning {\n background-color: #fcf8e3;\n}\na.bg-warning:hover,\na.bg-warning:focus {\n background-color: #f7ecb5;\n}\n.bg-danger {\n background-color: #f2dede;\n}\na.bg-danger:hover,\na.bg-danger:focus {\n background-color: #e4b9b9;\n}\n.page-header {\n padding-bottom: 9px;\n margin: 40px 0 20px;\n border-bottom: 1px solid #eeeeee;\n}\nul,\nol {\n margin-top: 0;\n margin-bottom: 10px;\n}\nul ul,\nol ul,\nul ol,\nol ol {\n margin-bottom: 0;\n}\n.list-unstyled {\n padding-left: 0;\n list-style: none;\n}\n.list-inline {\n padding-left: 0;\n list-style: none;\n margin-left: -5px;\n}\n.list-inline > li {\n display: inline-block;\n padding-left: 5px;\n padding-right: 5px;\n}\ndl {\n margin-top: 0;\n margin-bottom: 20px;\n}\ndt,\ndd {\n line-height: 1.42857143;\n}\ndt {\n font-weight: bold;\n}\ndd {\n margin-left: 0;\n}\n@media (min-width: 768px) {\n .dl-horizontal dt {\n float: left;\n width: 160px;\n clear: left;\n text-align: right;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n .dl-horizontal dd {\n margin-left: 180px;\n }\n}\nabbr[title],\nabbr[data-original-title] {\n cursor: help;\n border-bottom: 1px dotted #777777;\n}\n.initialism {\n font-size: 90%;\n text-transform: uppercase;\n}\nblockquote {\n padding: 10px 20px;\n margin: 0 0 20px;\n font-size: 17.5px;\n border-left: 5px solid #eeeeee;\n}\nblockquote p:last-child,\nblockquote ul:last-child,\nblockquote ol:last-child {\n margin-bottom: 0;\n}\nblockquote footer,\nblockquote small,\nblockquote .small {\n display: block;\n font-size: 80%;\n line-height: 1.42857143;\n color: #777777;\n}\nblockquote footer:before,\nblockquote small:before,\nblockquote .small:before {\n content: '\\2014 \\00A0';\n}\n.blockquote-reverse,\nblockquote.pull-right {\n padding-right: 15px;\n padding-left: 0;\n border-right: 5px solid #eeeeee;\n border-left: 0;\n text-align: right;\n}\n.blockquote-reverse footer:before,\nblockquote.pull-right footer:before,\n.blockquote-reverse small:before,\nblockquote.pull-right small:before,\n.blockquote-reverse .small:before,\nblockquote.pull-right .small:before {\n content: '';\n}\n.blockquote-reverse footer:after,\nblockquote.pull-right footer:after,\n.blockquote-reverse small:after,\nblockquote.pull-right small:after,\n.blockquote-reverse .small:after,\nblockquote.pull-right .small:after {\n content: '\\00A0 \\2014';\n}\naddress {\n margin-bottom: 20px;\n font-style: normal;\n line-height: 1.42857143;\n}\ncode,\nkbd,\npre,\nsamp {\n font-family: Menlo, Monaco, Consolas, \"Courier New\", monospace;\n}\ncode {\n padding: 2px 4px;\n font-size: 90%;\n color: #c7254e;\n background-color: #f9f2f4;\n border-radius: 4px;\n}\nkbd {\n padding: 2px 4px;\n font-size: 90%;\n color: #ffffff;\n background-color: #333333;\n border-radius: 3px;\n box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.25);\n}\nkbd kbd {\n padding: 0;\n font-size: 100%;\n font-weight: bold;\n box-shadow: none;\n}\npre {\n display: block;\n padding: 9.5px;\n margin: 0 0 10px;\n font-size: 13px;\n line-height: 1.42857143;\n word-break: break-all;\n word-wrap: break-word;\n color: #333333;\n background-color: #f5f5f5;\n border: 1px solid #cccccc;\n border-radius: 4px;\n}\npre code {\n padding: 0;\n font-size: inherit;\n color: inherit;\n white-space: pre-wrap;\n background-color: transparent;\n border-radius: 0;\n}\n.pre-scrollable {\n max-height: 340px;\n overflow-y: scroll;\n}\n.container {\n margin-right: auto;\n margin-left: auto;\n padding-left: 15px;\n padding-right: 15px;\n}\n@media (min-width: 768px) {\n .container {\n width: 750px;\n }\n}\n@media (min-width: 992px) {\n .container {\n width: 970px;\n }\n}\n@media (min-width: 1200px) {\n .container {\n width: 1170px;\n }\n}\n.container-fluid {\n margin-right: auto;\n margin-left: auto;\n padding-left: 15px;\n padding-right: 15px;\n}\n.row {\n margin-left: -15px;\n margin-right: -15px;\n}\n.col-xs-1, .col-sm-1, .col-md-1, .col-lg-1, .col-xs-2, .col-sm-2, .col-md-2, .col-lg-2, .col-xs-3, .col-sm-3, .col-md-3, .col-lg-3, .col-xs-4, .col-sm-4, .col-md-4, .col-lg-4, .col-xs-5, .col-sm-5, .col-md-5, .col-lg-5, .col-xs-6, .col-sm-6, .col-md-6, .col-lg-6, .col-xs-7, .col-sm-7, .col-md-7, .col-lg-7, .col-xs-8, .col-sm-8, .col-md-8, .col-lg-8, .col-xs-9, .col-sm-9, .col-md-9, .col-lg-9, .col-xs-10, .col-sm-10, .col-md-10, .col-lg-10, .col-xs-11, .col-sm-11, .col-md-11, .col-lg-11, .col-xs-12, .col-sm-12, .col-md-12, .col-lg-12 {\n position: relative;\n min-height: 1px;\n padding-left: 15px;\n padding-right: 15px;\n}\n.col-xs-1, .col-xs-2, .col-xs-3, .col-xs-4, .col-xs-5, .col-xs-6, .col-xs-7, .col-xs-8, .col-xs-9, .col-xs-10, .col-xs-11, .col-xs-12 {\n float: left;\n}\n.col-xs-12 {\n width: 100%;\n}\n.col-xs-11 {\n width: 91.66666667%;\n}\n.col-xs-10 {\n width: 83.33333333%;\n}\n.col-xs-9 {\n width: 75%;\n}\n.col-xs-8 {\n width: 66.66666667%;\n}\n.col-xs-7 {\n width: 58.33333333%;\n}\n.col-xs-6 {\n width: 50%;\n}\n.col-xs-5 {\n width: 41.66666667%;\n}\n.col-xs-4 {\n width: 33.33333333%;\n}\n.col-xs-3 {\n width: 25%;\n}\n.col-xs-2 {\n width: 16.66666667%;\n}\n.col-xs-1 {\n width: 8.33333333%;\n}\n.col-xs-pull-12 {\n right: 100%;\n}\n.col-xs-pull-11 {\n right: 91.66666667%;\n}\n.col-xs-pull-10 {\n right: 83.33333333%;\n}\n.col-xs-pull-9 {\n right: 75%;\n}\n.col-xs-pull-8 {\n right: 66.66666667%;\n}\n.col-xs-pull-7 {\n right: 58.33333333%;\n}\n.col-xs-pull-6 {\n right: 50%;\n}\n.col-xs-pull-5 {\n right: 41.66666667%;\n}\n.col-xs-pull-4 {\n right: 33.33333333%;\n}\n.col-xs-pull-3 {\n right: 25%;\n}\n.col-xs-pull-2 {\n right: 16.66666667%;\n}\n.col-xs-pull-1 {\n right: 8.33333333%;\n}\n.col-xs-pull-0 {\n right: auto;\n}\n.col-xs-push-12 {\n left: 100%;\n}\n.col-xs-push-11 {\n left: 91.66666667%;\n}\n.col-xs-push-10 {\n left: 83.33333333%;\n}\n.col-xs-push-9 {\n left: 75%;\n}\n.col-xs-push-8 {\n left: 66.66666667%;\n}\n.col-xs-push-7 {\n left: 58.33333333%;\n}\n.col-xs-push-6 {\n left: 50%;\n}\n.col-xs-push-5 {\n left: 41.66666667%;\n}\n.col-xs-push-4 {\n left: 33.33333333%;\n}\n.col-xs-push-3 {\n left: 25%;\n}\n.col-xs-push-2 {\n left: 16.66666667%;\n}\n.col-xs-push-1 {\n left: 8.33333333%;\n}\n.col-xs-push-0 {\n left: auto;\n}\n.col-xs-offset-12 {\n margin-left: 100%;\n}\n.col-xs-offset-11 {\n margin-left: 91.66666667%;\n}\n.col-xs-offset-10 {\n margin-left: 83.33333333%;\n}\n.col-xs-offset-9 {\n margin-left: 75%;\n}\n.col-xs-offset-8 {\n margin-left: 66.66666667%;\n}\n.col-xs-offset-7 {\n margin-left: 58.33333333%;\n}\n.col-xs-offset-6 {\n margin-left: 50%;\n}\n.col-xs-offset-5 {\n margin-left: 41.66666667%;\n}\n.col-xs-offset-4 {\n margin-left: 33.33333333%;\n}\n.col-xs-offset-3 {\n margin-left: 25%;\n}\n.col-xs-offset-2 {\n margin-left: 16.66666667%;\n}\n.col-xs-offset-1 {\n margin-left: 8.33333333%;\n}\n.col-xs-offset-0 {\n margin-left: 0%;\n}\n@media (min-width: 768px) {\n .col-sm-1, .col-sm-2, .col-sm-3, .col-sm-4, .col-sm-5, .col-sm-6, .col-sm-7, .col-sm-8, .col-sm-9, .col-sm-10, .col-sm-11, .col-sm-12 {\n float: left;\n }\n .col-sm-12 {\n width: 100%;\n }\n .col-sm-11 {\n width: 91.66666667%;\n }\n .col-sm-10 {\n width: 83.33333333%;\n }\n .col-sm-9 {\n width: 75%;\n }\n .col-sm-8 {\n width: 66.66666667%;\n }\n .col-sm-7 {\n width: 58.33333333%;\n }\n .col-sm-6 {\n width: 50%;\n }\n .col-sm-5 {\n width: 41.66666667%;\n }\n .col-sm-4 {\n width: 33.33333333%;\n }\n .col-sm-3 {\n width: 25%;\n }\n .col-sm-2 {\n width: 16.66666667%;\n }\n .col-sm-1 {\n width: 8.33333333%;\n }\n .col-sm-pull-12 {\n right: 100%;\n }\n .col-sm-pull-11 {\n right: 91.66666667%;\n }\n .col-sm-pull-10 {\n right: 83.33333333%;\n }\n .col-sm-pull-9 {\n right: 75%;\n }\n .col-sm-pull-8 {\n right: 66.66666667%;\n }\n .col-sm-pull-7 {\n right: 58.33333333%;\n }\n .col-sm-pull-6 {\n right: 50%;\n }\n .col-sm-pull-5 {\n right: 41.66666667%;\n }\n .col-sm-pull-4 {\n right: 33.33333333%;\n }\n .col-sm-pull-3 {\n right: 25%;\n }\n .col-sm-pull-2 {\n right: 16.66666667%;\n }\n .col-sm-pull-1 {\n right: 8.33333333%;\n }\n .col-sm-pull-0 {\n right: auto;\n }\n .col-sm-push-12 {\n left: 100%;\n }\n .col-sm-push-11 {\n left: 91.66666667%;\n }\n .col-sm-push-10 {\n left: 83.33333333%;\n }\n .col-sm-push-9 {\n left: 75%;\n }\n .col-sm-push-8 {\n left: 66.66666667%;\n }\n .col-sm-push-7 {\n left: 58.33333333%;\n }\n .col-sm-push-6 {\n left: 50%;\n }\n .col-sm-push-5 {\n left: 41.66666667%;\n }\n .col-sm-push-4 {\n left: 33.33333333%;\n }\n .col-sm-push-3 {\n left: 25%;\n }\n .col-sm-push-2 {\n left: 16.66666667%;\n }\n .col-sm-push-1 {\n left: 8.33333333%;\n }\n .col-sm-push-0 {\n left: auto;\n }\n .col-sm-offset-12 {\n margin-left: 100%;\n }\n .col-sm-offset-11 {\n margin-left: 91.66666667%;\n }\n .col-sm-offset-10 {\n margin-left: 83.33333333%;\n }\n .col-sm-offset-9 {\n margin-left: 75%;\n }\n .col-sm-offset-8 {\n margin-left: 66.66666667%;\n }\n .col-sm-offset-7 {\n margin-left: 58.33333333%;\n }\n .col-sm-offset-6 {\n margin-left: 50%;\n }\n .col-sm-offset-5 {\n margin-left: 41.66666667%;\n }\n .col-sm-offset-4 {\n margin-left: 33.33333333%;\n }\n .col-sm-offset-3 {\n margin-left: 25%;\n }\n .col-sm-offset-2 {\n margin-left: 16.66666667%;\n }\n .col-sm-offset-1 {\n margin-left: 8.33333333%;\n }\n .col-sm-offset-0 {\n margin-left: 0%;\n }\n}\n@media (min-width: 992px) {\n .col-md-1, .col-md-2, .col-md-3, .col-md-4, .col-md-5, .col-md-6, .col-md-7, .col-md-8, .col-md-9, .col-md-10, .col-md-11, .col-md-12 {\n float: left;\n }\n .col-md-12 {\n width: 100%;\n }\n .col-md-11 {\n width: 91.66666667%;\n }\n .col-md-10 {\n width: 83.33333333%;\n }\n .col-md-9 {\n width: 75%;\n }\n .col-md-8 {\n width: 66.66666667%;\n }\n .col-md-7 {\n width: 58.33333333%;\n }\n .col-md-6 {\n width: 50%;\n }\n .col-md-5 {\n width: 41.66666667%;\n }\n .col-md-4 {\n width: 33.33333333%;\n }\n .col-md-3 {\n width: 25%;\n }\n .col-md-2 {\n width: 16.66666667%;\n }\n .col-md-1 {\n width: 8.33333333%;\n }\n .col-md-pull-12 {\n right: 100%;\n }\n .col-md-pull-11 {\n right: 91.66666667%;\n }\n .col-md-pull-10 {\n right: 83.33333333%;\n }\n .col-md-pull-9 {\n right: 75%;\n }\n .col-md-pull-8 {\n right: 66.66666667%;\n }\n .col-md-pull-7 {\n right: 58.33333333%;\n }\n .col-md-pull-6 {\n right: 50%;\n }\n .col-md-pull-5 {\n right: 41.66666667%;\n }\n .col-md-pull-4 {\n right: 33.33333333%;\n }\n .col-md-pull-3 {\n right: 25%;\n }\n .col-md-pull-2 {\n right: 16.66666667%;\n }\n .col-md-pull-1 {\n right: 8.33333333%;\n }\n .col-md-pull-0 {\n right: auto;\n }\n .col-md-push-12 {\n left: 100%;\n }\n .col-md-push-11 {\n left: 91.66666667%;\n }\n .col-md-push-10 {\n left: 83.33333333%;\n }\n .col-md-push-9 {\n left: 75%;\n }\n .col-md-push-8 {\n left: 66.66666667%;\n }\n .col-md-push-7 {\n left: 58.33333333%;\n }\n .col-md-push-6 {\n left: 50%;\n }\n .col-md-push-5 {\n left: 41.66666667%;\n }\n .col-md-push-4 {\n left: 33.33333333%;\n }\n .col-md-push-3 {\n left: 25%;\n }\n .col-md-push-2 {\n left: 16.66666667%;\n }\n .col-md-push-1 {\n left: 8.33333333%;\n }\n .col-md-push-0 {\n left: auto;\n }\n .col-md-offset-12 {\n margin-left: 100%;\n }\n .col-md-offset-11 {\n margin-left: 91.66666667%;\n }\n .col-md-offset-10 {\n margin-left: 83.33333333%;\n }\n .col-md-offset-9 {\n margin-left: 75%;\n }\n .col-md-offset-8 {\n margin-left: 66.66666667%;\n }\n .col-md-offset-7 {\n margin-left: 58.33333333%;\n }\n .col-md-offset-6 {\n margin-left: 50%;\n }\n .col-md-offset-5 {\n margin-left: 41.66666667%;\n }\n .col-md-offset-4 {\n margin-left: 33.33333333%;\n }\n .col-md-offset-3 {\n margin-left: 25%;\n }\n .col-md-offset-2 {\n margin-left: 16.66666667%;\n }\n .col-md-offset-1 {\n margin-left: 8.33333333%;\n }\n .col-md-offset-0 {\n margin-left: 0%;\n }\n}\n@media (min-width: 1200px) {\n .col-lg-1, .col-lg-2, .col-lg-3, .col-lg-4, .col-lg-5, .col-lg-6, .col-lg-7, .col-lg-8, .col-lg-9, .col-lg-10, .col-lg-11, .col-lg-12 {\n float: left;\n }\n .col-lg-12 {\n width: 100%;\n }\n .col-lg-11 {\n width: 91.66666667%;\n }\n .col-lg-10 {\n width: 83.33333333%;\n }\n .col-lg-9 {\n width: 75%;\n }\n .col-lg-8 {\n width: 66.66666667%;\n }\n .col-lg-7 {\n width: 58.33333333%;\n }\n .col-lg-6 {\n width: 50%;\n }\n .col-lg-5 {\n width: 41.66666667%;\n }\n .col-lg-4 {\n width: 33.33333333%;\n }\n .col-lg-3 {\n width: 25%;\n }\n .col-lg-2 {\n width: 16.66666667%;\n }\n .col-lg-1 {\n width: 8.33333333%;\n }\n .col-lg-pull-12 {\n right: 100%;\n }\n .col-lg-pull-11 {\n right: 91.66666667%;\n }\n .col-lg-pull-10 {\n right: 83.33333333%;\n }\n .col-lg-pull-9 {\n right: 75%;\n }\n .col-lg-pull-8 {\n right: 66.66666667%;\n }\n .col-lg-pull-7 {\n right: 58.33333333%;\n }\n .col-lg-pull-6 {\n right: 50%;\n }\n .col-lg-pull-5 {\n right: 41.66666667%;\n }\n .col-lg-pull-4 {\n right: 33.33333333%;\n }\n .col-lg-pull-3 {\n right: 25%;\n }\n .col-lg-pull-2 {\n right: 16.66666667%;\n }\n .col-lg-pull-1 {\n right: 8.33333333%;\n }\n .col-lg-pull-0 {\n right: auto;\n }\n .col-lg-push-12 {\n left: 100%;\n }\n .col-lg-push-11 {\n left: 91.66666667%;\n }\n .col-lg-push-10 {\n left: 83.33333333%;\n }\n .col-lg-push-9 {\n left: 75%;\n }\n .col-lg-push-8 {\n left: 66.66666667%;\n }\n .col-lg-push-7 {\n left: 58.33333333%;\n }\n .col-lg-push-6 {\n left: 50%;\n }\n .col-lg-push-5 {\n left: 41.66666667%;\n }\n .col-lg-push-4 {\n left: 33.33333333%;\n }\n .col-lg-push-3 {\n left: 25%;\n }\n .col-lg-push-2 {\n left: 16.66666667%;\n }\n .col-lg-push-1 {\n left: 8.33333333%;\n }\n .col-lg-push-0 {\n left: auto;\n }\n .col-lg-offset-12 {\n margin-left: 100%;\n }\n .col-lg-offset-11 {\n margin-left: 91.66666667%;\n }\n .col-lg-offset-10 {\n margin-left: 83.33333333%;\n }\n .col-lg-offset-9 {\n margin-left: 75%;\n }\n .col-lg-offset-8 {\n margin-left: 66.66666667%;\n }\n .col-lg-offset-7 {\n margin-left: 58.33333333%;\n }\n .col-lg-offset-6 {\n margin-left: 50%;\n }\n .col-lg-offset-5 {\n margin-left: 41.66666667%;\n }\n .col-lg-offset-4 {\n margin-left: 33.33333333%;\n }\n .col-lg-offset-3 {\n margin-left: 25%;\n }\n .col-lg-offset-2 {\n margin-left: 16.66666667%;\n }\n .col-lg-offset-1 {\n margin-left: 8.33333333%;\n }\n .col-lg-offset-0 {\n margin-left: 0%;\n }\n}\ntable {\n background-color: transparent;\n}\ncaption {\n padding-top: 8px;\n padding-bottom: 8px;\n color: #777777;\n text-align: left;\n}\nth {\n text-align: left;\n}\n.table {\n width: 100%;\n max-width: 100%;\n margin-bottom: 20px;\n}\n.table > thead > tr > th,\n.table > tbody > tr > th,\n.table > tfoot > tr > th,\n.table > thead > tr > td,\n.table > tbody > tr > td,\n.table > tfoot > tr > td {\n padding: 8px;\n line-height: 1.42857143;\n vertical-align: top;\n border-top: 1px solid #dddddd;\n}\n.table > thead > tr > th {\n vertical-align: bottom;\n border-bottom: 2px solid #dddddd;\n}\n.table > caption + thead > tr:first-child > th,\n.table > colgroup + thead > tr:first-child > th,\n.table > thead:first-child > tr:first-child > th,\n.table > caption + thead > tr:first-child > td,\n.table > colgroup + thead > tr:first-child > td,\n.table > thead:first-child > tr:first-child > td {\n border-top: 0;\n}\n.table > tbody + tbody {\n border-top: 2px solid #dddddd;\n}\n.table .table {\n background-color: #ffffff;\n}\n.table-condensed > thead > tr > th,\n.table-condensed > tbody > tr > th,\n.table-condensed > tfoot > tr > th,\n.table-condensed > thead > tr > td,\n.table-condensed > tbody > tr > td,\n.table-condensed > tfoot > tr > td {\n padding: 5px;\n}\n.table-bordered {\n border: 1px solid #dddddd;\n}\n.table-bordered > thead > tr > th,\n.table-bordered > tbody > tr > th,\n.table-bordered > tfoot > tr > th,\n.table-bordered > thead > tr > td,\n.table-bordered > tbody > tr > td,\n.table-bordered > tfoot > tr > td {\n border: 1px solid #dddddd;\n}\n.table-bordered > thead > tr > th,\n.table-bordered > thead > tr > td {\n border-bottom-width: 2px;\n}\n.table-striped > tbody > tr:nth-of-type(odd) {\n background-color: #f9f9f9;\n}\n.table-hover > tbody > tr:hover {\n background-color: #f5f5f5;\n}\ntable col[class*=\"col-\"] {\n position: static;\n float: none;\n display: table-column;\n}\ntable td[class*=\"col-\"],\ntable th[class*=\"col-\"] {\n position: static;\n float: none;\n display: table-cell;\n}\n.table > thead > tr > td.active,\n.table > tbody > tr > td.active,\n.table > tfoot > tr > td.active,\n.table > thead > tr > th.active,\n.table > tbody > tr > th.active,\n.table > tfoot > tr > th.active,\n.table > thead > tr.active > td,\n.table > tbody > tr.active > td,\n.table > tfoot > tr.active > td,\n.table > thead > tr.active > th,\n.table > tbody > tr.active > th,\n.table > tfoot > tr.active > th {\n background-color: #f5f5f5;\n}\n.table-hover > tbody > tr > td.active:hover,\n.table-hover > tbody > tr > th.active:hover,\n.table-hover > tbody > tr.active:hover > td,\n.table-hover > tbody > tr:hover > .active,\n.table-hover > tbody > tr.active:hover > th {\n background-color: #e8e8e8;\n}\n.table > thead > tr > td.success,\n.table > tbody > tr > td.success,\n.table > tfoot > tr > td.success,\n.table > thead > tr > th.success,\n.table > tbody > tr > th.success,\n.table > tfoot > tr > th.success,\n.table > thead > tr.success > td,\n.table > tbody > tr.success > td,\n.table > tfoot > tr.success > td,\n.table > thead > tr.success > th,\n.table > tbody > tr.success > th,\n.table > tfoot > tr.success > th {\n background-color: #dff0d8;\n}\n.table-hover > tbody > tr > td.success:hover,\n.table-hover > tbody > tr > th.success:hover,\n.table-hover > tbody > tr.success:hover > td,\n.table-hover > tbody > tr:hover > .success,\n.table-hover > tbody > tr.success:hover > th {\n background-color: #d0e9c6;\n}\n.table > thead > tr > td.info,\n.table > tbody > tr > td.info,\n.table > tfoot > tr > td.info,\n.table > thead > tr > th.info,\n.table > tbody > tr > th.info,\n.table > tfoot > tr > th.info,\n.table > thead > tr.info > td,\n.table > tbody > tr.info > td,\n.table > tfoot > tr.info > td,\n.table > thead > tr.info > th,\n.table > tbody > tr.info > th,\n.table > tfoot > tr.info > th {\n background-color: #d9edf7;\n}\n.table-hover > tbody > tr > td.info:hover,\n.table-hover > tbody > tr > th.info:hover,\n.table-hover > tbody > tr.info:hover > td,\n.table-hover > tbody > tr:hover > .info,\n.table-hover > tbody > tr.info:hover > th {\n background-color: #c4e3f3;\n}\n.table > thead > tr > td.warning,\n.table > tbody > tr > td.warning,\n.table > tfoot > tr > td.warning,\n.table > thead > tr > th.warning,\n.table > tbody > tr > th.warning,\n.table > tfoot > tr > th.warning,\n.table > thead > tr.warning > td,\n.table > tbody > tr.warning > td,\n.table > tfoot > tr.warning > td,\n.table > thead > tr.warning > th,\n.table > tbody > tr.warning > th,\n.table > tfoot > tr.warning > th {\n background-color: #fcf8e3;\n}\n.table-hover > tbody > tr > td.warning:hover,\n.table-hover > tbody > tr > th.warning:hover,\n.table-hover > tbody > tr.warning:hover > td,\n.table-hover > tbody > tr:hover > .warning,\n.table-hover > tbody > tr.warning:hover > th {\n background-color: #faf2cc;\n}\n.table > thead > tr > td.danger,\n.table > tbody > tr > td.danger,\n.table > tfoot > tr > td.danger,\n.table > thead > tr > th.danger,\n.table > tbody > tr > th.danger,\n.table > tfoot > tr > th.danger,\n.table > thead > tr.danger > td,\n.table > tbody > tr.danger > td,\n.table > tfoot > tr.danger > td,\n.table > thead > tr.danger > th,\n.table > tbody > tr.danger > th,\n.table > tfoot > tr.danger > th {\n background-color: #f2dede;\n}\n.table-hover > tbody > tr > td.danger:hover,\n.table-hover > tbody > tr > th.danger:hover,\n.table-hover > tbody > tr.danger:hover > td,\n.table-hover > tbody > tr:hover > .danger,\n.table-hover > tbody > tr.danger:hover > th {\n background-color: #ebcccc;\n}\n.table-responsive {\n overflow-x: auto;\n min-height: 0.01%;\n}\n@media screen and (max-width: 767px) {\n .table-responsive {\n width: 100%;\n margin-bottom: 15px;\n overflow-y: hidden;\n -ms-overflow-style: -ms-autohiding-scrollbar;\n border: 1px solid #dddddd;\n }\n .table-responsive > .table {\n margin-bottom: 0;\n }\n .table-responsive > .table > thead > tr > th,\n .table-responsive > .table > tbody > tr > th,\n .table-responsive > .table > tfoot > tr > th,\n .table-responsive > .table > thead > tr > td,\n .table-responsive > .table > tbody > tr > td,\n .table-responsive > .table > tfoot > tr > td {\n white-space: nowrap;\n }\n .table-responsive > .table-bordered {\n border: 0;\n }\n .table-responsive > .table-bordered > thead > tr > th:first-child,\n .table-responsive > .table-bordered > tbody > tr > th:first-child,\n .table-responsive > .table-bordered > tfoot > tr > th:first-child,\n .table-responsive > .table-bordered > thead > tr > td:first-child,\n .table-responsive > .table-bordered > tbody > tr > td:first-child,\n .table-responsive > .table-bordered > tfoot > tr > td:first-child {\n border-left: 0;\n }\n .table-responsive > .table-bordered > thead > tr > th:last-child,\n .table-responsive > .table-bordered > tbody > tr > th:last-child,\n .table-responsive > .table-bordered > tfoot > tr > th:last-child,\n .table-responsive > .table-bordered > thead > tr > td:last-child,\n .table-responsive > .table-bordered > tbody > tr > td:last-child,\n .table-responsive > .table-bordered > tfoot > tr > td:last-child {\n border-right: 0;\n }\n .table-responsive > .table-bordered > tbody > tr:last-child > th,\n .table-responsive > .table-bordered > tfoot > tr:last-child > th,\n .table-responsive > .table-bordered > tbody > tr:last-child > td,\n .table-responsive > .table-bordered > tfoot > tr:last-child > td {\n border-bottom: 0;\n }\n}\nfieldset {\n padding: 0;\n margin: 0;\n border: 0;\n min-width: 0;\n}\nlegend {\n display: block;\n width: 100%;\n padding: 0;\n margin-bottom: 20px;\n font-size: 21px;\n line-height: inherit;\n color: #333333;\n border: 0;\n border-bottom: 1px solid #e5e5e5;\n}\nlabel {\n display: inline-block;\n max-width: 100%;\n margin-bottom: 5px;\n font-weight: bold;\n}\ninput[type=\"search\"] {\n -webkit-box-sizing: border-box;\n -moz-box-sizing: border-box;\n box-sizing: border-box;\n}\ninput[type=\"radio\"],\ninput[type=\"checkbox\"] {\n margin: 4px 0 0;\n margin-top: 1px \\9;\n line-height: normal;\n}\ninput[type=\"file\"] {\n display: block;\n}\ninput[type=\"range\"] {\n display: block;\n width: 100%;\n}\nselect[multiple],\nselect[size] {\n height: auto;\n}\ninput[type=\"file\"]:focus,\ninput[type=\"radio\"]:focus,\ninput[type=\"checkbox\"]:focus {\n outline: thin dotted;\n outline: 5px auto -webkit-focus-ring-color;\n outline-offset: -2px;\n}\noutput {\n display: block;\n padding-top: 7px;\n font-size: 14px;\n line-height: 1.42857143;\n color: #555555;\n}\n.form-control {\n display: block;\n width: 100%;\n height: 34px;\n padding: 6px 12px;\n font-size: 14px;\n line-height: 1.42857143;\n color: #555555;\n background-color: #ffffff;\n background-image: none;\n border: 1px solid #cccccc;\n border-radius: 4px;\n -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);\n box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);\n -webkit-transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s;\n -o-transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s;\n transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s;\n}\n.form-control:focus {\n border-color: #66afe9;\n outline: 0;\n -webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(102, 175, 233, 0.6);\n box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(102, 175, 233, 0.6);\n}\n.form-control::-moz-placeholder {\n color: #999999;\n opacity: 1;\n}\n.form-control:-ms-input-placeholder {\n color: #999999;\n}\n.form-control::-webkit-input-placeholder {\n color: #999999;\n}\n.form-control[disabled],\n.form-control[readonly],\nfieldset[disabled] .form-control {\n background-color: #eeeeee;\n opacity: 1;\n}\n.form-control[disabled],\nfieldset[disabled] .form-control {\n cursor: not-allowed;\n}\ntextarea.form-control {\n height: auto;\n}\ninput[type=\"search\"] {\n -webkit-appearance: none;\n}\n@media screen and (-webkit-min-device-pixel-ratio: 0) {\n input[type=\"date\"].form-control,\n input[type=\"time\"].form-control,\n input[type=\"datetime-local\"].form-control,\n input[type=\"month\"].form-control {\n line-height: 34px;\n }\n input[type=\"date\"].input-sm,\n input[type=\"time\"].input-sm,\n input[type=\"datetime-local\"].input-sm,\n input[type=\"month\"].input-sm,\n .input-group-sm input[type=\"date\"],\n .input-group-sm input[type=\"time\"],\n .input-group-sm input[type=\"datetime-local\"],\n .input-group-sm input[type=\"month\"] {\n line-height: 30px;\n }\n input[type=\"date\"].input-lg,\n input[type=\"time\"].input-lg,\n input[type=\"datetime-local\"].input-lg,\n input[type=\"month\"].input-lg,\n .input-group-lg input[type=\"date\"],\n .input-group-lg input[type=\"time\"],\n .input-group-lg input[type=\"datetime-local\"],\n .input-group-lg input[type=\"month\"] {\n line-height: 46px;\n }\n}\n.form-group {\n margin-bottom: 15px;\n}\n.radio,\n.checkbox {\n position: relative;\n display: block;\n margin-top: 10px;\n margin-bottom: 10px;\n}\n.radio label,\n.checkbox label {\n min-height: 20px;\n padding-left: 20px;\n margin-bottom: 0;\n font-weight: normal;\n cursor: pointer;\n}\n.radio input[type=\"radio\"],\n.radio-inline input[type=\"radio\"],\n.checkbox input[type=\"checkbox\"],\n.checkbox-inline input[type=\"checkbox\"] {\n position: absolute;\n margin-left: -20px;\n margin-top: 4px \\9;\n}\n.radio + .radio,\n.checkbox + .checkbox {\n margin-top: -5px;\n}\n.radio-inline,\n.checkbox-inline {\n position: relative;\n display: inline-block;\n padding-left: 20px;\n margin-bottom: 0;\n vertical-align: middle;\n font-weight: normal;\n cursor: pointer;\n}\n.radio-inline + .radio-inline,\n.checkbox-inline + .checkbox-inline {\n margin-top: 0;\n margin-left: 10px;\n}\ninput[type=\"radio\"][disabled],\ninput[type=\"checkbox\"][disabled],\ninput[type=\"radio\"].disabled,\ninput[type=\"checkbox\"].disabled,\nfieldset[disabled] input[type=\"radio\"],\nfieldset[disabled] input[type=\"checkbox\"] {\n cursor: not-allowed;\n}\n.radio-inline.disabled,\n.checkbox-inline.disabled,\nfieldset[disabled] .radio-inline,\nfieldset[disabled] .checkbox-inline {\n cursor: not-allowed;\n}\n.radio.disabled label,\n.checkbox.disabled label,\nfieldset[disabled] .radio label,\nfieldset[disabled] .checkbox label {\n cursor: not-allowed;\n}\n.form-control-static {\n padding-top: 7px;\n padding-bottom: 7px;\n margin-bottom: 0;\n min-height: 34px;\n}\n.form-control-static.input-lg,\n.form-control-static.input-sm {\n padding-left: 0;\n padding-right: 0;\n}\n.input-sm {\n height: 30px;\n padding: 5px 10px;\n font-size: 12px;\n line-height: 1.5;\n border-radius: 3px;\n}\nselect.input-sm {\n height: 30px;\n line-height: 30px;\n}\ntextarea.input-sm,\nselect[multiple].input-sm {\n height: auto;\n}\n.form-group-sm .form-control {\n height: 30px;\n padding: 5px 10px;\n font-size: 12px;\n line-height: 1.5;\n border-radius: 3px;\n}\n.form-group-sm select.form-control {\n height: 30px;\n line-height: 30px;\n}\n.form-group-sm textarea.form-control,\n.form-group-sm select[multiple].form-control {\n height: auto;\n}\n.form-group-sm .form-control-static {\n height: 30px;\n min-height: 32px;\n padding: 6px 10px;\n font-size: 12px;\n line-height: 1.5;\n}\n.input-lg {\n height: 46px;\n padding: 10px 16px;\n font-size: 18px;\n line-height: 1.3333333;\n border-radius: 6px;\n}\nselect.input-lg {\n height: 46px;\n line-height: 46px;\n}\ntextarea.input-lg,\nselect[multiple].input-lg {\n height: auto;\n}\n.form-group-lg .form-control {\n height: 46px;\n padding: 10px 16px;\n font-size: 18px;\n line-height: 1.3333333;\n border-radius: 6px;\n}\n.form-group-lg select.form-control {\n height: 46px;\n line-height: 46px;\n}\n.form-group-lg textarea.form-control,\n.form-group-lg select[multiple].form-control {\n height: auto;\n}\n.form-group-lg .form-control-static {\n height: 46px;\n min-height: 38px;\n padding: 11px 16px;\n font-size: 18px;\n line-height: 1.3333333;\n}\n.has-feedback {\n position: relative;\n}\n.has-feedback .form-control {\n padding-right: 42.5px;\n}\n.form-control-feedback {\n position: absolute;\n top: 0;\n right: 0;\n z-index: 2;\n display: block;\n width: 34px;\n height: 34px;\n line-height: 34px;\n text-align: center;\n pointer-events: none;\n}\n.input-lg + .form-control-feedback,\n.input-group-lg + .form-control-feedback,\n.form-group-lg .form-control + .form-control-feedback {\n width: 46px;\n height: 46px;\n line-height: 46px;\n}\n.input-sm + .form-control-feedback,\n.input-group-sm + .form-control-feedback,\n.form-group-sm .form-control + .form-control-feedback {\n width: 30px;\n height: 30px;\n line-height: 30px;\n}\n.has-success .help-block,\n.has-success .control-label,\n.has-success .radio,\n.has-success .checkbox,\n.has-success .radio-inline,\n.has-success .checkbox-inline,\n.has-success.radio label,\n.has-success.checkbox label,\n.has-success.radio-inline label,\n.has-success.checkbox-inline label {\n color: #3c763d;\n}\n.has-success .form-control {\n border-color: #3c763d;\n -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);\n box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);\n}\n.has-success .form-control:focus {\n border-color: #2b542c;\n -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #67b168;\n box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #67b168;\n}\n.has-success .input-group-addon {\n color: #3c763d;\n border-color: #3c763d;\n background-color: #dff0d8;\n}\n.has-success .form-control-feedback {\n color: #3c763d;\n}\n.has-warning .help-block,\n.has-warning .control-label,\n.has-warning .radio,\n.has-warning .checkbox,\n.has-warning .radio-inline,\n.has-warning .checkbox-inline,\n.has-warning.radio label,\n.has-warning.checkbox label,\n.has-warning.radio-inline label,\n.has-warning.checkbox-inline label {\n color: #8a6d3b;\n}\n.has-warning .form-control {\n border-color: #8a6d3b;\n -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);\n box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);\n}\n.has-warning .form-control:focus {\n border-color: #66512c;\n -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #c0a16b;\n box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #c0a16b;\n}\n.has-warning .input-group-addon {\n color: #8a6d3b;\n border-color: #8a6d3b;\n background-color: #fcf8e3;\n}\n.has-warning .form-control-feedback {\n color: #8a6d3b;\n}\n.has-error .help-block,\n.has-error .control-label,\n.has-error .radio,\n.has-error .checkbox,\n.has-error .radio-inline,\n.has-error .checkbox-inline,\n.has-error.radio label,\n.has-error.checkbox label,\n.has-error.radio-inline label,\n.has-error.checkbox-inline label {\n color: #a94442;\n}\n.has-error .form-control {\n border-color: #a94442;\n -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);\n box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);\n}\n.has-error .form-control:focus {\n border-color: #843534;\n -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #ce8483;\n box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #ce8483;\n}\n.has-error .input-group-addon {\n color: #a94442;\n border-color: #a94442;\n background-color: #f2dede;\n}\n.has-error .form-control-feedback {\n color: #a94442;\n}\n.has-feedback label ~ .form-control-feedback {\n top: 25px;\n}\n.has-feedback label.sr-only ~ .form-control-feedback {\n top: 0;\n}\n.help-block {\n display: block;\n margin-top: 5px;\n margin-bottom: 10px;\n color: #737373;\n}\n@media (min-width: 768px) {\n .form-inline .form-group {\n display: inline-block;\n margin-bottom: 0;\n vertical-align: middle;\n }\n .form-inline .form-control {\n display: inline-block;\n width: auto;\n vertical-align: middle;\n }\n .form-inline .form-control-static {\n display: inline-block;\n }\n .form-inline .input-group {\n display: inline-table;\n vertical-align: middle;\n }\n .form-inline .input-group .input-group-addon,\n .form-inline .input-group .input-group-btn,\n .form-inline .input-group .form-control {\n width: auto;\n }\n .form-inline .input-group > .form-control {\n width: 100%;\n }\n .form-inline .control-label {\n margin-bottom: 0;\n vertical-align: middle;\n }\n .form-inline .radio,\n .form-inline .checkbox {\n display: inline-block;\n margin-top: 0;\n margin-bottom: 0;\n vertical-align: middle;\n }\n .form-inline .radio label,\n .form-inline .checkbox label {\n padding-left: 0;\n }\n .form-inline .radio input[type=\"radio\"],\n .form-inline .checkbox input[type=\"checkbox\"] {\n position: relative;\n margin-left: 0;\n }\n .form-inline .has-feedback .form-control-feedback {\n top: 0;\n }\n}\n.form-horizontal .radio,\n.form-horizontal .checkbox,\n.form-horizontal .radio-inline,\n.form-horizontal .checkbox-inline {\n margin-top: 0;\n margin-bottom: 0;\n padding-top: 7px;\n}\n.form-horizontal .radio,\n.form-horizontal .checkbox {\n min-height: 27px;\n}\n.form-horizontal .form-group {\n margin-left: -15px;\n margin-right: -15px;\n}\n@media (min-width: 768px) {\n .form-horizontal .control-label {\n text-align: right;\n margin-bottom: 0;\n padding-top: 7px;\n }\n}\n.form-horizontal .has-feedback .form-control-feedback {\n right: 15px;\n}\n@media (min-width: 768px) {\n .form-horizontal .form-group-lg .control-label {\n padding-top: 14.333333px;\n font-size: 18px;\n }\n}\n@media (min-width: 768px) {\n .form-horizontal .form-group-sm .control-label {\n padding-top: 6px;\n font-size: 12px;\n }\n}\n.btn {\n display: inline-block;\n margin-bottom: 0;\n font-weight: normal;\n text-align: center;\n vertical-align: middle;\n touch-action: manipulation;\n cursor: pointer;\n background-image: none;\n border: 1px solid transparent;\n white-space: nowrap;\n padding: 6px 12px;\n font-size: 14px;\n line-height: 1.42857143;\n border-radius: 4px;\n -webkit-user-select: none;\n -moz-user-select: none;\n -ms-user-select: none;\n user-select: none;\n}\n.btn:focus,\n.btn:active:focus,\n.btn.active:focus,\n.btn.focus,\n.btn:active.focus,\n.btn.active.focus {\n outline: thin dotted;\n outline: 5px auto -webkit-focus-ring-color;\n outline-offset: -2px;\n}\n.btn:hover,\n.btn:focus,\n.btn.focus {\n color: #333333;\n text-decoration: none;\n}\n.btn:active,\n.btn.active {\n outline: 0;\n background-image: none;\n -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);\n box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);\n}\n.btn.disabled,\n.btn[disabled],\nfieldset[disabled] .btn {\n cursor: not-allowed;\n opacity: 0.65;\n filter: alpha(opacity=65);\n -webkit-box-shadow: none;\n box-shadow: none;\n}\na.btn.disabled,\nfieldset[disabled] a.btn {\n pointer-events: none;\n}\n.btn-default {\n color: #333333;\n background-color: #ffffff;\n border-color: #cccccc;\n}\n.btn-default:focus,\n.btn-default.focus {\n color: #333333;\n background-color: #e6e6e6;\n border-color: #8c8c8c;\n}\n.btn-default:hover {\n color: #333333;\n background-color: #e6e6e6;\n border-color: #adadad;\n}\n.btn-default:active,\n.btn-default.active,\n.open > .dropdown-toggle.btn-default {\n color: #333333;\n background-color: #e6e6e6;\n border-color: #adadad;\n}\n.btn-default:active:hover,\n.btn-default.active:hover,\n.open > .dropdown-toggle.btn-default:hover,\n.btn-default:active:focus,\n.btn-default.active:focus,\n.open > .dropdown-toggle.btn-default:focus,\n.btn-default:active.focus,\n.btn-default.active.focus,\n.open > .dropdown-toggle.btn-default.focus {\n color: #333333;\n background-color: #d4d4d4;\n border-color: #8c8c8c;\n}\n.btn-default:active,\n.btn-default.active,\n.open > .dropdown-toggle.btn-default {\n background-image: none;\n}\n.btn-default.disabled,\n.btn-default[disabled],\nfieldset[disabled] .btn-default,\n.btn-default.disabled:hover,\n.btn-default[disabled]:hover,\nfieldset[disabled] .btn-default:hover,\n.btn-default.disabled:focus,\n.btn-default[disabled]:focus,\nfieldset[disabled] .btn-default:focus,\n.btn-default.disabled.focus,\n.btn-default[disabled].focus,\nfieldset[disabled] .btn-default.focus,\n.btn-default.disabled:active,\n.btn-default[disabled]:active,\nfieldset[disabled] .btn-default:active,\n.btn-default.disabled.active,\n.btn-default[disabled].active,\nfieldset[disabled] .btn-default.active {\n background-color: #ffffff;\n border-color: #cccccc;\n}\n.btn-default .badge {\n color: #ffffff;\n background-color: #333333;\n}\n.btn-primary {\n color: #ffffff;\n background-color: #337ab7;\n border-color: #2e6da4;\n}\n.btn-primary:focus,\n.btn-primary.focus {\n color: #ffffff;\n background-color: #286090;\n border-color: #122b40;\n}\n.btn-primary:hover {\n color: #ffffff;\n background-color: #286090;\n border-color: #204d74;\n}\n.btn-primary:active,\n.btn-primary.active,\n.open > .dropdown-toggle.btn-primary {\n color: #ffffff;\n background-color: #286090;\n border-color: #204d74;\n}\n.btn-primary:active:hover,\n.btn-primary.active:hover,\n.open > .dropdown-toggle.btn-primary:hover,\n.btn-primary:active:focus,\n.btn-primary.active:focus,\n.open > .dropdown-toggle.btn-primary:focus,\n.btn-primary:active.focus,\n.btn-primary.active.focus,\n.open > .dropdown-toggle.btn-primary.focus {\n color: #ffffff;\n background-color: #204d74;\n border-color: #122b40;\n}\n.btn-primary:active,\n.btn-primary.active,\n.open > .dropdown-toggle.btn-primary {\n background-image: none;\n}\n.btn-primary.disabled,\n.btn-primary[disabled],\nfieldset[disabled] .btn-primary,\n.btn-primary.disabled:hover,\n.btn-primary[disabled]:hover,\nfieldset[disabled] .btn-primary:hover,\n.btn-primary.disabled:focus,\n.btn-primary[disabled]:focus,\nfieldset[disabled] .btn-primary:focus,\n.btn-primary.disabled.focus,\n.btn-primary[disabled].focus,\nfieldset[disabled] .btn-primary.focus,\n.btn-primary.disabled:active,\n.btn-primary[disabled]:active,\nfieldset[disabled] .btn-primary:active,\n.btn-primary.disabled.active,\n.btn-primary[disabled].active,\nfieldset[disabled] .btn-primary.active {\n background-color: #337ab7;\n border-color: #2e6da4;\n}\n.btn-primary .badge {\n color: #337ab7;\n background-color: #ffffff;\n}\n.btn-success {\n color: #ffffff;\n background-color: #5cb85c;\n border-color: #4cae4c;\n}\n.btn-success:focus,\n.btn-success.focus {\n color: #ffffff;\n background-color: #449d44;\n border-color: #255625;\n}\n.btn-success:hover {\n color: #ffffff;\n background-color: #449d44;\n border-color: #398439;\n}\n.btn-success:active,\n.btn-success.active,\n.open > .dropdown-toggle.btn-success {\n color: #ffffff;\n background-color: #449d44;\n border-color: #398439;\n}\n.btn-success:active:hover,\n.btn-success.active:hover,\n.open > .dropdown-toggle.btn-success:hover,\n.btn-success:active:focus,\n.btn-success.active:focus,\n.open > .dropdown-toggle.btn-success:focus,\n.btn-success:active.focus,\n.btn-success.active.focus,\n.open > .dropdown-toggle.btn-success.focus {\n color: #ffffff;\n background-color: #398439;\n border-color: #255625;\n}\n.btn-success:active,\n.btn-success.active,\n.open > .dropdown-toggle.btn-success {\n background-image: none;\n}\n.btn-success.disabled,\n.btn-success[disabled],\nfieldset[disabled] .btn-success,\n.btn-success.disabled:hover,\n.btn-success[disabled]:hover,\nfieldset[disabled] .btn-success:hover,\n.btn-success.disabled:focus,\n.btn-success[disabled]:focus,\nfieldset[disabled] .btn-success:focus,\n.btn-success.disabled.focus,\n.btn-success[disabled].focus,\nfieldset[disabled] .btn-success.focus,\n.btn-success.disabled:active,\n.btn-success[disabled]:active,\nfieldset[disabled] .btn-success:active,\n.btn-success.disabled.active,\n.btn-success[disabled].active,\nfieldset[disabled] .btn-success.active {\n background-color: #5cb85c;\n border-color: #4cae4c;\n}\n.btn-success .badge {\n color: #5cb85c;\n background-color: #ffffff;\n}\n.btn-info {\n color: #ffffff;\n background-color: #5bc0de;\n border-color: #46b8da;\n}\n.btn-info:focus,\n.btn-info.focus {\n color: #ffffff;\n background-color: #31b0d5;\n border-color: #1b6d85;\n}\n.btn-info:hover {\n color: #ffffff;\n background-color: #31b0d5;\n border-color: #269abc;\n}\n.btn-info:active,\n.btn-info.active,\n.open > .dropdown-toggle.btn-info {\n color: #ffffff;\n background-color: #31b0d5;\n border-color: #269abc;\n}\n.btn-info:active:hover,\n.btn-info.active:hover,\n.open > .dropdown-toggle.btn-info:hover,\n.btn-info:active:focus,\n.btn-info.active:focus,\n.open > .dropdown-toggle.btn-info:focus,\n.btn-info:active.focus,\n.btn-info.active.focus,\n.open > .dropdown-toggle.btn-info.focus {\n color: #ffffff;\n background-color: #269abc;\n border-color: #1b6d85;\n}\n.btn-info:active,\n.btn-info.active,\n.open > .dropdown-toggle.btn-info {\n background-image: none;\n}\n.btn-info.disabled,\n.btn-info[disabled],\nfieldset[disabled] .btn-info,\n.btn-info.disabled:hover,\n.btn-info[disabled]:hover,\nfieldset[disabled] .btn-info:hover,\n.btn-info.disabled:focus,\n.btn-info[disabled]:focus,\nfieldset[disabled] .btn-info:focus,\n.btn-info.disabled.focus,\n.btn-info[disabled].focus,\nfieldset[disabled] .btn-info.focus,\n.btn-info.disabled:active,\n.btn-info[disabled]:active,\nfieldset[disabled] .btn-info:active,\n.btn-info.disabled.active,\n.btn-info[disabled].active,\nfieldset[disabled] .btn-info.active {\n background-color: #5bc0de;\n border-color: #46b8da;\n}\n.btn-info .badge {\n color: #5bc0de;\n background-color: #ffffff;\n}\n.btn-warning {\n color: #ffffff;\n background-color: #f0ad4e;\n border-color: #eea236;\n}\n.btn-warning:focus,\n.btn-warning.focus {\n color: #ffffff;\n background-color: #ec971f;\n border-color: #985f0d;\n}\n.btn-warning:hover {\n color: #ffffff;\n background-color: #ec971f;\n border-color: #d58512;\n}\n.btn-warning:active,\n.btn-warning.active,\n.open > .dropdown-toggle.btn-warning {\n color: #ffffff;\n background-color: #ec971f;\n border-color: #d58512;\n}\n.btn-warning:active:hover,\n.btn-warning.active:hover,\n.open > .dropdown-toggle.btn-warning:hover,\n.btn-warning:active:focus,\n.btn-warning.active:focus,\n.open > .dropdown-toggle.btn-warning:focus,\n.btn-warning:active.focus,\n.btn-warning.active.focus,\n.open > .dropdown-toggle.btn-warning.focus {\n color: #ffffff;\n background-color: #d58512;\n border-color: #985f0d;\n}\n.btn-warning:active,\n.btn-warning.active,\n.open > .dropdown-toggle.btn-warning {\n background-image: none;\n}\n.btn-warning.disabled,\n.btn-warning[disabled],\nfieldset[disabled] .btn-warning,\n.btn-warning.disabled:hover,\n.btn-warning[disabled]:hover,\nfieldset[disabled] .btn-warning:hover,\n.btn-warning.disabled:focus,\n.btn-warning[disabled]:focus,\nfieldset[disabled] .btn-warning:focus,\n.btn-warning.disabled.focus,\n.btn-warning[disabled].focus,\nfieldset[disabled] .btn-warning.focus,\n.btn-warning.disabled:active,\n.btn-warning[disabled]:active,\nfieldset[disabled] .btn-warning:active,\n.btn-warning.disabled.active,\n.btn-warning[disabled].active,\nfieldset[disabled] .btn-warning.active {\n background-color: #f0ad4e;\n border-color: #eea236;\n}\n.btn-warning .badge {\n color: #f0ad4e;\n background-color: #ffffff;\n}\n.btn-danger {\n color: #ffffff;\n background-color: #d9534f;\n border-color: #d43f3a;\n}\n.btn-danger:focus,\n.btn-danger.focus {\n color: #ffffff;\n background-color: #c9302c;\n border-color: #761c19;\n}\n.btn-danger:hover {\n color: #ffffff;\n background-color: #c9302c;\n border-color: #ac2925;\n}\n.btn-danger:active,\n.btn-danger.active,\n.open > .dropdown-toggle.btn-danger {\n color: #ffffff;\n background-color: #c9302c;\n border-color: #ac2925;\n}\n.btn-danger:active:hover,\n.btn-danger.active:hover,\n.open > .dropdown-toggle.btn-danger:hover,\n.btn-danger:active:focus,\n.btn-danger.active:focus,\n.open > .dropdown-toggle.btn-danger:focus,\n.btn-danger:active.focus,\n.btn-danger.active.focus,\n.open > .dropdown-toggle.btn-danger.focus {\n color: #ffffff;\n background-color: #ac2925;\n border-color: #761c19;\n}\n.btn-danger:active,\n.btn-danger.active,\n.open > .dropdown-toggle.btn-danger {\n background-image: none;\n}\n.btn-danger.disabled,\n.btn-danger[disabled],\nfieldset[disabled] .btn-danger,\n.btn-danger.disabled:hover,\n.btn-danger[disabled]:hover,\nfieldset[disabled] .btn-danger:hover,\n.btn-danger.disabled:focus,\n.btn-danger[disabled]:focus,\nfieldset[disabled] .btn-danger:focus,\n.btn-danger.disabled.focus,\n.btn-danger[disabled].focus,\nfieldset[disabled] .btn-danger.focus,\n.btn-danger.disabled:active,\n.btn-danger[disabled]:active,\nfieldset[disabled] .btn-danger:active,\n.btn-danger.disabled.active,\n.btn-danger[disabled].active,\nfieldset[disabled] .btn-danger.active {\n background-color: #d9534f;\n border-color: #d43f3a;\n}\n.btn-danger .badge {\n color: #d9534f;\n background-color: #ffffff;\n}\n.btn-link {\n color: #337ab7;\n font-weight: normal;\n border-radius: 0;\n}\n.btn-link,\n.btn-link:active,\n.btn-link.active,\n.btn-link[disabled],\nfieldset[disabled] .btn-link {\n background-color: transparent;\n -webkit-box-shadow: none;\n box-shadow: none;\n}\n.btn-link,\n.btn-link:hover,\n.btn-link:focus,\n.btn-link:active {\n border-color: transparent;\n}\n.btn-link:hover,\n.btn-link:focus {\n color: #23527c;\n text-decoration: underline;\n background-color: transparent;\n}\n.btn-link[disabled]:hover,\nfieldset[disabled] .btn-link:hover,\n.btn-link[disabled]:focus,\nfieldset[disabled] .btn-link:focus {\n color: #777777;\n text-decoration: none;\n}\n.btn-lg,\n.btn-group-lg > .btn {\n padding: 10px 16px;\n font-size: 18px;\n line-height: 1.3333333;\n border-radius: 6px;\n}\n.btn-sm,\n.btn-group-sm > .btn {\n padding: 5px 10px;\n font-size: 12px;\n line-height: 1.5;\n border-radius: 3px;\n}\n.btn-xs,\n.btn-group-xs > .btn {\n padding: 1px 5px;\n font-size: 12px;\n line-height: 1.5;\n border-radius: 3px;\n}\n.btn-block {\n display: block;\n width: 100%;\n}\n.btn-block + .btn-block {\n margin-top: 5px;\n}\ninput[type=\"submit\"].btn-block,\ninput[type=\"reset\"].btn-block,\ninput[type=\"button\"].btn-block {\n width: 100%;\n}\n.fade {\n opacity: 0;\n -webkit-transition: opacity 0.15s linear;\n -o-transition: opacity 0.15s linear;\n transition: opacity 0.15s linear;\n}\n.fade.in {\n opacity: 1;\n}\n.collapse {\n display: none;\n}\n.collapse.in {\n display: block;\n}\ntr.collapse.in {\n display: table-row;\n}\ntbody.collapse.in {\n display: table-row-group;\n}\n.collapsing {\n position: relative;\n height: 0;\n overflow: hidden;\n -webkit-transition-property: height, visibility;\n transition-property: height, visibility;\n -webkit-transition-duration: 0.35s;\n transition-duration: 0.35s;\n -webkit-transition-timing-function: ease;\n transition-timing-function: ease;\n}\n.caret {\n display: inline-block;\n width: 0;\n height: 0;\n margin-left: 2px;\n vertical-align: middle;\n border-top: 4px dashed;\n border-top: 4px solid \\9;\n border-right: 4px solid transparent;\n border-left: 4px solid transparent;\n}\n.dropup,\n.dropdown {\n position: relative;\n}\n.dropdown-toggle:focus {\n outline: 0;\n}\n.dropdown-menu {\n position: absolute;\n top: 100%;\n left: 0;\n z-index: 1000;\n display: none;\n float: left;\n min-width: 160px;\n padding: 5px 0;\n margin: 2px 0 0;\n list-style: none;\n font-size: 14px;\n text-align: left;\n background-color: #ffffff;\n border: 1px solid #cccccc;\n border: 1px solid rgba(0, 0, 0, 0.15);\n border-radius: 4px;\n -webkit-box-shadow: 0 6px 12px rgba(0, 0, 0, 0.175);\n box-shadow: 0 6px 12px rgba(0, 0, 0, 0.175);\n background-clip: padding-box;\n}\n.dropdown-menu.pull-right {\n right: 0;\n left: auto;\n}\n.dropdown-menu .divider {\n height: 1px;\n margin: 9px 0;\n overflow: hidden;\n background-color: #e5e5e5;\n}\n.dropdown-menu > li > a {\n display: block;\n padding: 3px 20px;\n clear: both;\n font-weight: normal;\n line-height: 1.42857143;\n color: #333333;\n white-space: nowrap;\n}\n.dropdown-menu > li > a:hover,\n.dropdown-menu > li > a:focus {\n text-decoration: none;\n color: #262626;\n background-color: #f5f5f5;\n}\n.dropdown-menu > .active > a,\n.dropdown-menu > .active > a:hover,\n.dropdown-menu > .active > a:focus {\n color: #ffffff;\n text-decoration: none;\n outline: 0;\n background-color: #337ab7;\n}\n.dropdown-menu > .disabled > a,\n.dropdown-menu > .disabled > a:hover,\n.dropdown-menu > .disabled > a:focus {\n color: #777777;\n}\n.dropdown-menu > .disabled > a:hover,\n.dropdown-menu > .disabled > a:focus {\n text-decoration: none;\n background-color: transparent;\n background-image: none;\n filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);\n cursor: not-allowed;\n}\n.open > .dropdown-menu {\n display: block;\n}\n.open > a {\n outline: 0;\n}\n.dropdown-menu-right {\n left: auto;\n right: 0;\n}\n.dropdown-menu-left {\n left: 0;\n right: auto;\n}\n.dropdown-header {\n display: block;\n padding: 3px 20px;\n font-size: 12px;\n line-height: 1.42857143;\n color: #777777;\n white-space: nowrap;\n}\n.dropdown-backdrop {\n position: fixed;\n left: 0;\n right: 0;\n bottom: 0;\n top: 0;\n z-index: 990;\n}\n.pull-right > .dropdown-menu {\n right: 0;\n left: auto;\n}\n.dropup .caret,\n.navbar-fixed-bottom .dropdown .caret {\n border-top: 0;\n border-bottom: 4px dashed;\n border-bottom: 4px solid \\9;\n content: \"\";\n}\n.dropup .dropdown-menu,\n.navbar-fixed-bottom .dropdown .dropdown-menu {\n top: auto;\n bottom: 100%;\n margin-bottom: 2px;\n}\n@media (min-width: 768px) {\n .navbar-right .dropdown-menu {\n left: auto;\n right: 0;\n }\n .navbar-right .dropdown-menu-left {\n left: 0;\n right: auto;\n }\n}\n.btn-group,\n.btn-group-vertical {\n position: relative;\n display: inline-block;\n vertical-align: middle;\n}\n.btn-group > .btn,\n.btn-group-vertical > .btn {\n position: relative;\n float: left;\n}\n.btn-group > .btn:hover,\n.btn-group-vertical > .btn:hover,\n.btn-group > .btn:focus,\n.btn-group-vertical > .btn:focus,\n.btn-group > .btn:active,\n.btn-group-vertical > .btn:active,\n.btn-group > .btn.active,\n.btn-group-vertical > .btn.active {\n z-index: 2;\n}\n.btn-group .btn + .btn,\n.btn-group .btn + .btn-group,\n.btn-group .btn-group + .btn,\n.btn-group .btn-group + .btn-group {\n margin-left: -1px;\n}\n.btn-toolbar {\n margin-left: -5px;\n}\n.btn-toolbar .btn,\n.btn-toolbar .btn-group,\n.btn-toolbar .input-group {\n float: left;\n}\n.btn-toolbar > .btn,\n.btn-toolbar > .btn-group,\n.btn-toolbar > .input-group {\n margin-left: 5px;\n}\n.btn-group > .btn:not(:first-child):not(:last-child):not(.dropdown-toggle) {\n border-radius: 0;\n}\n.btn-group > .btn:first-child {\n margin-left: 0;\n}\n.btn-group > .btn:first-child:not(:last-child):not(.dropdown-toggle) {\n border-bottom-right-radius: 0;\n border-top-right-radius: 0;\n}\n.btn-group > .btn:last-child:not(:first-child),\n.btn-group > .dropdown-toggle:not(:first-child) {\n border-bottom-left-radius: 0;\n border-top-left-radius: 0;\n}\n.btn-group > .btn-group {\n float: left;\n}\n.btn-group > .btn-group:not(:first-child):not(:last-child) > .btn {\n border-radius: 0;\n}\n.btn-group > .btn-group:first-child:not(:last-child) > .btn:last-child,\n.btn-group > .btn-group:first-child:not(:last-child) > .dropdown-toggle {\n border-bottom-right-radius: 0;\n border-top-right-radius: 0;\n}\n.btn-group > .btn-group:last-child:not(:first-child) > .btn:first-child {\n border-bottom-left-radius: 0;\n border-top-left-radius: 0;\n}\n.btn-group .dropdown-toggle:active,\n.btn-group.open .dropdown-toggle {\n outline: 0;\n}\n.btn-group > .btn + .dropdown-toggle {\n padding-left: 8px;\n padding-right: 8px;\n}\n.btn-group > .btn-lg + .dropdown-toggle {\n padding-left: 12px;\n padding-right: 12px;\n}\n.btn-group.open .dropdown-toggle {\n -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);\n box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);\n}\n.btn-group.open .dropdown-toggle.btn-link {\n -webkit-box-shadow: none;\n box-shadow: none;\n}\n.btn .caret {\n margin-left: 0;\n}\n.btn-lg .caret {\n border-width: 5px 5px 0;\n border-bottom-width: 0;\n}\n.dropup .btn-lg .caret {\n border-width: 0 5px 5px;\n}\n.btn-group-vertical > .btn,\n.btn-group-vertical > .btn-group,\n.btn-group-vertical > .btn-group > .btn {\n display: block;\n float: none;\n width: 100%;\n max-width: 100%;\n}\n.btn-group-vertical > .btn-group > .btn {\n float: none;\n}\n.btn-group-vertical > .btn + .btn,\n.btn-group-vertical > .btn + .btn-group,\n.btn-group-vertical > .btn-group + .btn,\n.btn-group-vertical > .btn-group + .btn-group {\n margin-top: -1px;\n margin-left: 0;\n}\n.btn-group-vertical > .btn:not(:first-child):not(:last-child) {\n border-radius: 0;\n}\n.btn-group-vertical > .btn:first-child:not(:last-child) {\n border-top-right-radius: 4px;\n border-bottom-right-radius: 0;\n border-bottom-left-radius: 0;\n}\n.btn-group-vertical > .btn:last-child:not(:first-child) {\n border-bottom-left-radius: 4px;\n border-top-right-radius: 0;\n border-top-left-radius: 0;\n}\n.btn-group-vertical > .btn-group:not(:first-child):not(:last-child) > .btn {\n border-radius: 0;\n}\n.btn-group-vertical > .btn-group:first-child:not(:last-child) > .btn:last-child,\n.btn-group-vertical > .btn-group:first-child:not(:last-child) > .dropdown-toggle {\n border-bottom-right-radius: 0;\n border-bottom-left-radius: 0;\n}\n.btn-group-vertical > .btn-group:last-child:not(:first-child) > .btn:first-child {\n border-top-right-radius: 0;\n border-top-left-radius: 0;\n}\n.btn-group-justified {\n display: table;\n width: 100%;\n table-layout: fixed;\n border-collapse: separate;\n}\n.btn-group-justified > .btn,\n.btn-group-justified > .btn-group {\n float: none;\n display: table-cell;\n width: 1%;\n}\n.btn-group-justified > .btn-group .btn {\n width: 100%;\n}\n.btn-group-justified > .btn-group .dropdown-menu {\n left: auto;\n}\n[data-toggle=\"buttons\"] > .btn input[type=\"radio\"],\n[data-toggle=\"buttons\"] > .btn-group > .btn input[type=\"radio\"],\n[data-toggle=\"buttons\"] > .btn input[type=\"checkbox\"],\n[data-toggle=\"buttons\"] > .btn-group > .btn input[type=\"checkbox\"] {\n position: absolute;\n clip: rect(0, 0, 0, 0);\n pointer-events: none;\n}\n.input-group {\n position: relative;\n display: table;\n border-collapse: separate;\n}\n.input-group[class*=\"col-\"] {\n float: none;\n padding-left: 0;\n padding-right: 0;\n}\n.input-group .form-control {\n position: relative;\n z-index: 2;\n float: left;\n width: 100%;\n margin-bottom: 0;\n}\n.input-group-lg > .form-control,\n.input-group-lg > .input-group-addon,\n.input-group-lg > .input-group-btn > .btn {\n height: 46px;\n padding: 10px 16px;\n font-size: 18px;\n line-height: 1.3333333;\n border-radius: 6px;\n}\nselect.input-group-lg > .form-control,\nselect.input-group-lg > .input-group-addon,\nselect.input-group-lg > .input-group-btn > .btn {\n height: 46px;\n line-height: 46px;\n}\ntextarea.input-group-lg > .form-control,\ntextarea.input-group-lg > .input-group-addon,\ntextarea.input-group-lg > .input-group-btn > .btn,\nselect[multiple].input-group-lg > .form-control,\nselect[multiple].input-group-lg > .input-group-addon,\nselect[multiple].input-group-lg > .input-group-btn > .btn {\n height: auto;\n}\n.input-group-sm > .form-control,\n.input-group-sm > .input-group-addon,\n.input-group-sm > .input-group-btn > .btn {\n height: 30px;\n padding: 5px 10px;\n font-size: 12px;\n line-height: 1.5;\n border-radius: 3px;\n}\nselect.input-group-sm > .form-control,\nselect.input-group-sm > .input-group-addon,\nselect.input-group-sm > .input-group-btn > .btn {\n height: 30px;\n line-height: 30px;\n}\ntextarea.input-group-sm > .form-control,\ntextarea.input-group-sm > .input-group-addon,\ntextarea.input-group-sm > .input-group-btn > .btn,\nselect[multiple].input-group-sm > .form-control,\nselect[multiple].input-group-sm > .input-group-addon,\nselect[multiple].input-group-sm > .input-group-btn > .btn {\n height: auto;\n}\n.input-group-addon,\n.input-group-btn,\n.input-group .form-control {\n display: table-cell;\n}\n.input-group-addon:not(:first-child):not(:last-child),\n.input-group-btn:not(:first-child):not(:last-child),\n.input-group .form-control:not(:first-child):not(:last-child) {\n border-radius: 0;\n}\n.input-group-addon,\n.input-group-btn {\n width: 1%;\n white-space: nowrap;\n vertical-align: middle;\n}\n.input-group-addon {\n padding: 6px 12px;\n font-size: 14px;\n font-weight: normal;\n line-height: 1;\n color: #555555;\n text-align: center;\n background-color: #eeeeee;\n border: 1px solid #cccccc;\n border-radius: 4px;\n}\n.input-group-addon.input-sm {\n padding: 5px 10px;\n font-size: 12px;\n border-radius: 3px;\n}\n.input-group-addon.input-lg {\n padding: 10px 16px;\n font-size: 18px;\n border-radius: 6px;\n}\n.input-group-addon input[type=\"radio\"],\n.input-group-addon input[type=\"checkbox\"] {\n margin-top: 0;\n}\n.input-group .form-control:first-child,\n.input-group-addon:first-child,\n.input-group-btn:first-child > .btn,\n.input-group-btn:first-child > .btn-group > .btn,\n.input-group-btn:first-child > .dropdown-toggle,\n.input-group-btn:last-child > .btn:not(:last-child):not(.dropdown-toggle),\n.input-group-btn:last-child > .btn-group:not(:last-child) > .btn {\n border-bottom-right-radius: 0;\n border-top-right-radius: 0;\n}\n.input-group-addon:first-child {\n border-right: 0;\n}\n.input-group .form-control:last-child,\n.input-group-addon:last-child,\n.input-group-btn:last-child > .btn,\n.input-group-btn:last-child > .btn-group > .btn,\n.input-group-btn:last-child > .dropdown-toggle,\n.input-group-btn:first-child > .btn:not(:first-child),\n.input-group-btn:first-child > .btn-group:not(:first-child) > .btn {\n border-bottom-left-radius: 0;\n border-top-left-radius: 0;\n}\n.input-group-addon:last-child {\n border-left: 0;\n}\n.input-group-btn {\n position: relative;\n font-size: 0;\n white-space: nowrap;\n}\n.input-group-btn > .btn {\n position: relative;\n}\n.input-group-btn > .btn + .btn {\n margin-left: -1px;\n}\n.input-group-btn > .btn:hover,\n.input-group-btn > .btn:focus,\n.input-group-btn > .btn:active {\n z-index: 2;\n}\n.input-group-btn:first-child > .btn,\n.input-group-btn:first-child > .btn-group {\n margin-right: -1px;\n}\n.input-group-btn:last-child > .btn,\n.input-group-btn:last-child > .btn-group {\n z-index: 2;\n margin-left: -1px;\n}\n.nav {\n margin-bottom: 0;\n padding-left: 0;\n list-style: none;\n}\n.nav > li {\n position: relative;\n display: block;\n}\n.nav > li > a {\n position: relative;\n display: block;\n padding: 10px 15px;\n}\n.nav > li > a:hover,\n.nav > li > a:focus {\n text-decoration: none;\n background-color: #eeeeee;\n}\n.nav > li.disabled > a {\n color: #777777;\n}\n.nav > li.disabled > a:hover,\n.nav > li.disabled > a:focus {\n color: #777777;\n text-decoration: none;\n background-color: transparent;\n cursor: not-allowed;\n}\n.nav .open > a,\n.nav .open > a:hover,\n.nav .open > a:focus {\n background-color: #eeeeee;\n border-color: #337ab7;\n}\n.nav .nav-divider {\n height: 1px;\n margin: 9px 0;\n overflow: hidden;\n background-color: #e5e5e5;\n}\n.nav > li > a > img {\n max-width: none;\n}\n.nav-tabs {\n border-bottom: 1px solid #dddddd;\n}\n.nav-tabs > li {\n float: left;\n margin-bottom: -1px;\n}\n.nav-tabs > li > a {\n margin-right: 2px;\n line-height: 1.42857143;\n border: 1px solid transparent;\n border-radius: 4px 4px 0 0;\n}\n.nav-tabs > li > a:hover {\n border-color: #eeeeee #eeeeee #dddddd;\n}\n.nav-tabs > li.active > a,\n.nav-tabs > li.active > a:hover,\n.nav-tabs > li.active > a:focus {\n color: #555555;\n background-color: #ffffff;\n border: 1px solid #dddddd;\n border-bottom-color: transparent;\n cursor: default;\n}\n.nav-tabs.nav-justified {\n width: 100%;\n border-bottom: 0;\n}\n.nav-tabs.nav-justified > li {\n float: none;\n}\n.nav-tabs.nav-justified > li > a {\n text-align: center;\n margin-bottom: 5px;\n}\n.nav-tabs.nav-justified > .dropdown .dropdown-menu {\n top: auto;\n left: auto;\n}\n@media (min-width: 768px) {\n .nav-tabs.nav-justified > li {\n display: table-cell;\n width: 1%;\n }\n .nav-tabs.nav-justified > li > a {\n margin-bottom: 0;\n }\n}\n.nav-tabs.nav-justified > li > a {\n margin-right: 0;\n border-radius: 4px;\n}\n.nav-tabs.nav-justified > .active > a,\n.nav-tabs.nav-justified > .active > a:hover,\n.nav-tabs.nav-justified > .active > a:focus {\n border: 1px solid #dddddd;\n}\n@media (min-width: 768px) {\n .nav-tabs.nav-justified > li > a {\n border-bottom: 1px solid #dddddd;\n border-radius: 4px 4px 0 0;\n }\n .nav-tabs.nav-justified > .active > a,\n .nav-tabs.nav-justified > .active > a:hover,\n .nav-tabs.nav-justified > .active > a:focus {\n border-bottom-color: #ffffff;\n }\n}\n.nav-pills > li {\n float: left;\n}\n.nav-pills > li > a {\n border-radius: 4px;\n}\n.nav-pills > li + li {\n margin-left: 2px;\n}\n.nav-pills > li.active > a,\n.nav-pills > li.active > a:hover,\n.nav-pills > li.active > a:focus {\n color: #ffffff;\n background-color: #337ab7;\n}\n.nav-stacked > li {\n float: none;\n}\n.nav-stacked > li + li {\n margin-top: 2px;\n margin-left: 0;\n}\n.nav-justified {\n width: 100%;\n}\n.nav-justified > li {\n float: none;\n}\n.nav-justified > li > a {\n text-align: center;\n margin-bottom: 5px;\n}\n.nav-justified > .dropdown .dropdown-menu {\n top: auto;\n left: auto;\n}\n@media (min-width: 768px) {\n .nav-justified > li {\n display: table-cell;\n width: 1%;\n }\n .nav-justified > li > a {\n margin-bottom: 0;\n }\n}\n.nav-tabs-justified {\n border-bottom: 0;\n}\n.nav-tabs-justified > li > a {\n margin-right: 0;\n border-radius: 4px;\n}\n.nav-tabs-justified > .active > a,\n.nav-tabs-justified > .active > a:hover,\n.nav-tabs-justified > .active > a:focus {\n border: 1px solid #dddddd;\n}\n@media (min-width: 768px) {\n .nav-tabs-justified > li > a {\n border-bottom: 1px solid #dddddd;\n border-radius: 4px 4px 0 0;\n }\n .nav-tabs-justified > .active > a,\n .nav-tabs-justified > .active > a:hover,\n .nav-tabs-justified > .active > a:focus {\n border-bottom-color: #ffffff;\n }\n}\n.tab-content > .tab-pane {\n display: none;\n}\n.tab-content > .active {\n display: block;\n}\n.nav-tabs .dropdown-menu {\n margin-top: -1px;\n border-top-right-radius: 0;\n border-top-left-radius: 0;\n}\n.navbar {\n position: relative;\n min-height: 50px;\n margin-bottom: 20px;\n border: 1px solid transparent;\n}\n@media (min-width: 768px) {\n .navbar {\n border-radius: 4px;\n }\n}\n@media (min-width: 768px) {\n .navbar-header {\n float: left;\n }\n}\n.navbar-collapse {\n overflow-x: visible;\n padding-right: 15px;\n padding-left: 15px;\n border-top: 1px solid transparent;\n box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1);\n -webkit-overflow-scrolling: touch;\n}\n.navbar-collapse.in {\n overflow-y: auto;\n}\n@media (min-width: 768px) {\n .navbar-collapse {\n width: auto;\n border-top: 0;\n box-shadow: none;\n }\n .navbar-collapse.collapse {\n display: block !important;\n height: auto !important;\n padding-bottom: 0;\n overflow: visible !important;\n }\n .navbar-collapse.in {\n overflow-y: visible;\n }\n .navbar-fixed-top .navbar-collapse,\n .navbar-static-top .navbar-collapse,\n .navbar-fixed-bottom .navbar-collapse {\n padding-left: 0;\n padding-right: 0;\n }\n}\n.navbar-fixed-top .navbar-collapse,\n.navbar-fixed-bottom .navbar-collapse {\n max-height: 340px;\n}\n@media (max-device-width: 480px) and (orientation: landscape) {\n .navbar-fixed-top .navbar-collapse,\n .navbar-fixed-bottom .navbar-collapse {\n max-height: 200px;\n }\n}\n.container > .navbar-header,\n.container-fluid > .navbar-header,\n.container > .navbar-collapse,\n.container-fluid > .navbar-collapse {\n margin-right: -15px;\n margin-left: -15px;\n}\n@media (min-width: 768px) {\n .container > .navbar-header,\n .container-fluid > .navbar-header,\n .container > .navbar-collapse,\n .container-fluid > .navbar-collapse {\n margin-right: 0;\n margin-left: 0;\n }\n}\n.navbar-static-top {\n z-index: 1000;\n border-width: 0 0 1px;\n}\n@media (min-width: 768px) {\n .navbar-static-top {\n border-radius: 0;\n }\n}\n.navbar-fixed-top,\n.navbar-fixed-bottom {\n position: fixed;\n right: 0;\n left: 0;\n z-index: 1030;\n}\n@media (min-width: 768px) {\n .navbar-fixed-top,\n .navbar-fixed-bottom {\n border-radius: 0;\n }\n}\n.navbar-fixed-top {\n top: 0;\n border-width: 0 0 1px;\n}\n.navbar-fixed-bottom {\n bottom: 0;\n margin-bottom: 0;\n border-width: 1px 0 0;\n}\n.navbar-brand {\n float: left;\n padding: 15px 15px;\n font-size: 18px;\n line-height: 20px;\n height: 50px;\n}\n.navbar-brand:hover,\n.navbar-brand:focus {\n text-decoration: none;\n}\n.navbar-brand > img {\n display: block;\n}\n@media (min-width: 768px) {\n .navbar > .container .navbar-brand,\n .navbar > .container-fluid .navbar-brand {\n margin-left: -15px;\n }\n}\n.navbar-toggle {\n position: relative;\n float: right;\n margin-right: 15px;\n padding: 9px 10px;\n margin-top: 8px;\n margin-bottom: 8px;\n background-color: transparent;\n background-image: none;\n border: 1px solid transparent;\n border-radius: 4px;\n}\n.navbar-toggle:focus {\n outline: 0;\n}\n.navbar-toggle .icon-bar {\n display: block;\n width: 22px;\n height: 2px;\n border-radius: 1px;\n}\n.navbar-toggle .icon-bar + .icon-bar {\n margin-top: 4px;\n}\n@media (min-width: 768px) {\n .navbar-toggle {\n display: none;\n }\n}\n.navbar-nav {\n margin: 7.5px -15px;\n}\n.navbar-nav > li > a {\n padding-top: 10px;\n padding-bottom: 10px;\n line-height: 20px;\n}\n@media (max-width: 767px) {\n .navbar-nav .open .dropdown-menu {\n position: static;\n float: none;\n width: auto;\n margin-top: 0;\n background-color: transparent;\n border: 0;\n box-shadow: none;\n }\n .navbar-nav .open .dropdown-menu > li > a,\n .navbar-nav .open .dropdown-menu .dropdown-header {\n padding: 5px 15px 5px 25px;\n }\n .navbar-nav .open .dropdown-menu > li > a {\n line-height: 20px;\n }\n .navbar-nav .open .dropdown-menu > li > a:hover,\n .navbar-nav .open .dropdown-menu > li > a:focus {\n background-image: none;\n }\n}\n@media (min-width: 768px) {\n .navbar-nav {\n float: left;\n margin: 0;\n }\n .navbar-nav > li {\n float: left;\n }\n .navbar-nav > li > a {\n padding-top: 15px;\n padding-bottom: 15px;\n }\n}\n.navbar-form {\n margin-left: -15px;\n margin-right: -15px;\n padding: 10px 15px;\n border-top: 1px solid transparent;\n border-bottom: 1px solid transparent;\n -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.1);\n box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.1);\n margin-top: 8px;\n margin-bottom: 8px;\n}\n@media (min-width: 768px) {\n .navbar-form .form-group {\n display: inline-block;\n margin-bottom: 0;\n vertical-align: middle;\n }\n .navbar-form .form-control {\n display: inline-block;\n width: auto;\n vertical-align: middle;\n }\n .navbar-form .form-control-static {\n display: inline-block;\n }\n .navbar-form .input-group {\n display: inline-table;\n vertical-align: middle;\n }\n .navbar-form .input-group .input-group-addon,\n .navbar-form .input-group .input-group-btn,\n .navbar-form .input-group .form-control {\n width: auto;\n }\n .navbar-form .input-group > .form-control {\n width: 100%;\n }\n .navbar-form .control-label {\n margin-bottom: 0;\n vertical-align: middle;\n }\n .navbar-form .radio,\n .navbar-form .checkbox {\n display: inline-block;\n margin-top: 0;\n margin-bottom: 0;\n vertical-align: middle;\n }\n .navbar-form .radio label,\n .navbar-form .checkbox label {\n padding-left: 0;\n }\n .navbar-form .radio input[type=\"radio\"],\n .navbar-form .checkbox input[type=\"checkbox\"] {\n position: relative;\n margin-left: 0;\n }\n .navbar-form .has-feedback .form-control-feedback {\n top: 0;\n }\n}\n@media (max-width: 767px) {\n .navbar-form .form-group {\n margin-bottom: 5px;\n }\n .navbar-form .form-group:last-child {\n margin-bottom: 0;\n }\n}\n@media (min-width: 768px) {\n .navbar-form {\n width: auto;\n border: 0;\n margin-left: 0;\n margin-right: 0;\n padding-top: 0;\n padding-bottom: 0;\n -webkit-box-shadow: none;\n box-shadow: none;\n }\n}\n.navbar-nav > li > .dropdown-menu {\n margin-top: 0;\n border-top-right-radius: 0;\n border-top-left-radius: 0;\n}\n.navbar-fixed-bottom .navbar-nav > li > .dropdown-menu {\n margin-bottom: 0;\n border-top-right-radius: 4px;\n border-top-left-radius: 4px;\n border-bottom-right-radius: 0;\n border-bottom-left-radius: 0;\n}\n.navbar-btn {\n margin-top: 8px;\n margin-bottom: 8px;\n}\n.navbar-btn.btn-sm {\n margin-top: 10px;\n margin-bottom: 10px;\n}\n.navbar-btn.btn-xs {\n margin-top: 14px;\n margin-bottom: 14px;\n}\n.navbar-text {\n margin-top: 15px;\n margin-bottom: 15px;\n}\n@media (min-width: 768px) {\n .navbar-text {\n float: left;\n margin-left: 15px;\n margin-right: 15px;\n }\n}\n@media (min-width: 768px) {\n .navbar-left {\n float: left !important;\n }\n .navbar-right {\n float: right !important;\n margin-right: -15px;\n }\n .navbar-right ~ .navbar-right {\n margin-right: 0;\n }\n}\n.navbar-default {\n background-color: #f8f8f8;\n border-color: #e7e7e7;\n}\n.navbar-default .navbar-brand {\n color: #777777;\n}\n.navbar-default .navbar-brand:hover,\n.navbar-default .navbar-brand:focus {\n color: #5e5e5e;\n background-color: transparent;\n}\n.navbar-default .navbar-text {\n color: #777777;\n}\n.navbar-default .navbar-nav > li > a {\n color: #777777;\n}\n.navbar-default .navbar-nav > li > a:hover,\n.navbar-default .navbar-nav > li > a:focus {\n color: #333333;\n background-color: transparent;\n}\n.navbar-default .navbar-nav > .active > a,\n.navbar-default .navbar-nav > .active > a:hover,\n.navbar-default .navbar-nav > .active > a:focus {\n color: #555555;\n background-color: #e7e7e7;\n}\n.navbar-default .navbar-nav > .disabled > a,\n.navbar-default .navbar-nav > .disabled > a:hover,\n.navbar-default .navbar-nav > .disabled > a:focus {\n color: #cccccc;\n background-color: transparent;\n}\n.navbar-default .navbar-toggle {\n border-color: #dddddd;\n}\n.navbar-default .navbar-toggle:hover,\n.navbar-default .navbar-toggle:focus {\n background-color: #dddddd;\n}\n.navbar-default .navbar-toggle .icon-bar {\n background-color: #888888;\n}\n.navbar-default .navbar-collapse,\n.navbar-default .navbar-form {\n border-color: #e7e7e7;\n}\n.navbar-default .navbar-nav > .open > a,\n.navbar-default .navbar-nav > .open > a:hover,\n.navbar-default .navbar-nav > .open > a:focus {\n background-color: #e7e7e7;\n color: #555555;\n}\n@media (max-width: 767px) {\n .navbar-default .navbar-nav .open .dropdown-menu > li > a {\n color: #777777;\n }\n .navbar-default .navbar-nav .open .dropdown-menu > li > a:hover,\n .navbar-default .navbar-nav .open .dropdown-menu > li > a:focus {\n color: #333333;\n background-color: transparent;\n }\n .navbar-default .navbar-nav .open .dropdown-menu > .active > a,\n .navbar-default .navbar-nav .open .dropdown-menu > .active > a:hover,\n .navbar-default .navbar-nav .open .dropdown-menu > .active > a:focus {\n color: #555555;\n background-color: #e7e7e7;\n }\n .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a,\n .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a:hover,\n .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a:focus {\n color: #cccccc;\n background-color: transparent;\n }\n}\n.navbar-default .navbar-link {\n color: #777777;\n}\n.navbar-default .navbar-link:hover {\n color: #333333;\n}\n.navbar-default .btn-link {\n color: #777777;\n}\n.navbar-default .btn-link:hover,\n.navbar-default .btn-link:focus {\n color: #333333;\n}\n.navbar-default .btn-link[disabled]:hover,\nfieldset[disabled] .navbar-default .btn-link:hover,\n.navbar-default .btn-link[disabled]:focus,\nfieldset[disabled] .navbar-default .btn-link:focus {\n color: #cccccc;\n}\n.navbar-inverse {\n background-color: #222222;\n border-color: #080808;\n}\n.navbar-inverse .navbar-brand {\n color: #9d9d9d;\n}\n.navbar-inverse .navbar-brand:hover,\n.navbar-inverse .navbar-brand:focus {\n color: #ffffff;\n background-color: transparent;\n}\n.navbar-inverse .navbar-text {\n color: #9d9d9d;\n}\n.navbar-inverse .navbar-nav > li > a {\n color: #9d9d9d;\n}\n.navbar-inverse .navbar-nav > li > a:hover,\n.navbar-inverse .navbar-nav > li > a:focus {\n color: #ffffff;\n background-color: transparent;\n}\n.navbar-inverse .navbar-nav > .active > a,\n.navbar-inverse .navbar-nav > .active > a:hover,\n.navbar-inverse .navbar-nav > .active > a:focus {\n color: #ffffff;\n background-color: #080808;\n}\n.navbar-inverse .navbar-nav > .disabled > a,\n.navbar-inverse .navbar-nav > .disabled > a:hover,\n.navbar-inverse .navbar-nav > .disabled > a:focus {\n color: #444444;\n background-color: transparent;\n}\n.navbar-inverse .navbar-toggle {\n border-color: #333333;\n}\n.navbar-inverse .navbar-toggle:hover,\n.navbar-inverse .navbar-toggle:focus {\n background-color: #333333;\n}\n.navbar-inverse .navbar-toggle .icon-bar {\n background-color: #ffffff;\n}\n.navbar-inverse .navbar-collapse,\n.navbar-inverse .navbar-form {\n border-color: #101010;\n}\n.navbar-inverse .navbar-nav > .open > a,\n.navbar-inverse .navbar-nav > .open > a:hover,\n.navbar-inverse .navbar-nav > .open > a:focus {\n background-color: #080808;\n color: #ffffff;\n}\n@media (max-width: 767px) {\n .navbar-inverse .navbar-nav .open .dropdown-menu > .dropdown-header {\n border-color: #080808;\n }\n .navbar-inverse .navbar-nav .open .dropdown-menu .divider {\n background-color: #080808;\n }\n .navbar-inverse .navbar-nav .open .dropdown-menu > li > a {\n color: #9d9d9d;\n }\n .navbar-inverse .navbar-nav .open .dropdown-menu > li > a:hover,\n .navbar-inverse .navbar-nav .open .dropdown-menu > li > a:focus {\n color: #ffffff;\n background-color: transparent;\n }\n .navbar-inverse .navbar-nav .open .dropdown-menu > .active > a,\n .navbar-inverse .navbar-nav .open .dropdown-menu > .active > a:hover,\n .navbar-inverse .navbar-nav .open .dropdown-menu > .active > a:focus {\n color: #ffffff;\n background-color: #080808;\n }\n .navbar-inverse .navbar-nav .open .dropdown-menu > .disabled > a,\n .navbar-inverse .navbar-nav .open .dropdown-menu > .disabled > a:hover,\n .navbar-inverse .navbar-nav .open .dropdown-menu > .disabled > a:focus {\n color: #444444;\n background-color: transparent;\n }\n}\n.navbar-inverse .navbar-link {\n color: #9d9d9d;\n}\n.navbar-inverse .navbar-link:hover {\n color: #ffffff;\n}\n.navbar-inverse .btn-link {\n color: #9d9d9d;\n}\n.navbar-inverse .btn-link:hover,\n.navbar-inverse .btn-link:focus {\n color: #ffffff;\n}\n.navbar-inverse .btn-link[disabled]:hover,\nfieldset[disabled] .navbar-inverse .btn-link:hover,\n.navbar-inverse .btn-link[disabled]:focus,\nfieldset[disabled] .navbar-inverse .btn-link:focus {\n color: #444444;\n}\n.breadcrumb {\n padding: 8px 15px;\n margin-bottom: 20px;\n list-style: none;\n background-color: #f5f5f5;\n border-radius: 4px;\n}\n.breadcrumb > li {\n display: inline-block;\n}\n.breadcrumb > li + li:before {\n content: \"/\\00a0\";\n padding: 0 5px;\n color: #cccccc;\n}\n.breadcrumb > .active {\n color: #777777;\n}\n.pagination {\n display: inline-block;\n padding-left: 0;\n margin: 20px 0;\n border-radius: 4px;\n}\n.pagination > li {\n display: inline;\n}\n.pagination > li > a,\n.pagination > li > span {\n position: relative;\n float: left;\n padding: 6px 12px;\n line-height: 1.42857143;\n text-decoration: none;\n color: #337ab7;\n background-color: #ffffff;\n border: 1px solid #dddddd;\n margin-left: -1px;\n}\n.pagination > li:first-child > a,\n.pagination > li:first-child > span {\n margin-left: 0;\n border-bottom-left-radius: 4px;\n border-top-left-radius: 4px;\n}\n.pagination > li:last-child > a,\n.pagination > li:last-child > span {\n border-bottom-right-radius: 4px;\n border-top-right-radius: 4px;\n}\n.pagination > li > a:hover,\n.pagination > li > span:hover,\n.pagination > li > a:focus,\n.pagination > li > span:focus {\n z-index: 3;\n color: #23527c;\n background-color: #eeeeee;\n border-color: #dddddd;\n}\n.pagination > .active > a,\n.pagination > .active > span,\n.pagination > .active > a:hover,\n.pagination > .active > span:hover,\n.pagination > .active > a:focus,\n.pagination > .active > span:focus {\n z-index: 2;\n color: #ffffff;\n background-color: #337ab7;\n border-color: #337ab7;\n cursor: default;\n}\n.pagination > .disabled > span,\n.pagination > .disabled > span:hover,\n.pagination > .disabled > span:focus,\n.pagination > .disabled > a,\n.pagination > .disabled > a:hover,\n.pagination > .disabled > a:focus {\n color: #777777;\n background-color: #ffffff;\n border-color: #dddddd;\n cursor: not-allowed;\n}\n.pagination-lg > li > a,\n.pagination-lg > li > span {\n padding: 10px 16px;\n font-size: 18px;\n line-height: 1.3333333;\n}\n.pagination-lg > li:first-child > a,\n.pagination-lg > li:first-child > span {\n border-bottom-left-radius: 6px;\n border-top-left-radius: 6px;\n}\n.pagination-lg > li:last-child > a,\n.pagination-lg > li:last-child > span {\n border-bottom-right-radius: 6px;\n border-top-right-radius: 6px;\n}\n.pagination-sm > li > a,\n.pagination-sm > li > span {\n padding: 5px 10px;\n font-size: 12px;\n line-height: 1.5;\n}\n.pagination-sm > li:first-child > a,\n.pagination-sm > li:first-child > span {\n border-bottom-left-radius: 3px;\n border-top-left-radius: 3px;\n}\n.pagination-sm > li:last-child > a,\n.pagination-sm > li:last-child > span {\n border-bottom-right-radius: 3px;\n border-top-right-radius: 3px;\n}\n.pager {\n padding-left: 0;\n margin: 20px 0;\n list-style: none;\n text-align: center;\n}\n.pager li {\n display: inline;\n}\n.pager li > a,\n.pager li > span {\n display: inline-block;\n padding: 5px 14px;\n background-color: #ffffff;\n border: 1px solid #dddddd;\n border-radius: 15px;\n}\n.pager li > a:hover,\n.pager li > a:focus {\n text-decoration: none;\n background-color: #eeeeee;\n}\n.pager .next > a,\n.pager .next > span {\n float: right;\n}\n.pager .previous > a,\n.pager .previous > span {\n float: left;\n}\n.pager .disabled > a,\n.pager .disabled > a:hover,\n.pager .disabled > a:focus,\n.pager .disabled > span {\n color: #777777;\n background-color: #ffffff;\n cursor: not-allowed;\n}\n.label {\n display: inline;\n padding: .2em .6em .3em;\n font-size: 75%;\n font-weight: bold;\n line-height: 1;\n color: #ffffff;\n text-align: center;\n white-space: nowrap;\n vertical-align: baseline;\n border-radius: .25em;\n}\na.label:hover,\na.label:focus {\n color: #ffffff;\n text-decoration: none;\n cursor: pointer;\n}\n.label:empty {\n display: none;\n}\n.btn .label {\n position: relative;\n top: -1px;\n}\n.label-default {\n background-color: #777777;\n}\n.label-default[href]:hover,\n.label-default[href]:focus {\n background-color: #5e5e5e;\n}\n.label-primary {\n background-color: #337ab7;\n}\n.label-primary[href]:hover,\n.label-primary[href]:focus {\n background-color: #286090;\n}\n.label-success {\n background-color: #5cb85c;\n}\n.label-success[href]:hover,\n.label-success[href]:focus {\n background-color: #449d44;\n}\n.label-info {\n background-color: #5bc0de;\n}\n.label-info[href]:hover,\n.label-info[href]:focus {\n background-color: #31b0d5;\n}\n.label-warning {\n background-color: #f0ad4e;\n}\n.label-warning[href]:hover,\n.label-warning[href]:focus {\n background-color: #ec971f;\n}\n.label-danger {\n background-color: #d9534f;\n}\n.label-danger[href]:hover,\n.label-danger[href]:focus {\n background-color: #c9302c;\n}\n.badge {\n display: inline-block;\n min-width: 10px;\n padding: 3px 7px;\n font-size: 12px;\n font-weight: bold;\n color: #ffffff;\n line-height: 1;\n vertical-align: middle;\n white-space: nowrap;\n text-align: center;\n background-color: #777777;\n border-radius: 10px;\n}\n.badge:empty {\n display: none;\n}\n.btn .badge {\n position: relative;\n top: -1px;\n}\n.btn-xs .badge,\n.btn-group-xs > .btn .badge {\n top: 0;\n padding: 1px 5px;\n}\na.badge:hover,\na.badge:focus {\n color: #ffffff;\n text-decoration: none;\n cursor: pointer;\n}\n.list-group-item.active > .badge,\n.nav-pills > .active > a > .badge {\n color: #337ab7;\n background-color: #ffffff;\n}\n.list-group-item > .badge {\n float: right;\n}\n.list-group-item > .badge + .badge {\n margin-right: 5px;\n}\n.nav-pills > li > a > .badge {\n margin-left: 3px;\n}\n.jumbotron {\n padding-top: 30px;\n padding-bottom: 30px;\n margin-bottom: 30px;\n color: inherit;\n background-color: #eeeeee;\n}\n.jumbotron h1,\n.jumbotron .h1 {\n color: inherit;\n}\n.jumbotron p {\n margin-bottom: 15px;\n font-size: 21px;\n font-weight: 200;\n}\n.jumbotron > hr {\n border-top-color: #d5d5d5;\n}\n.container .jumbotron,\n.container-fluid .jumbotron {\n border-radius: 6px;\n}\n.jumbotron .container {\n max-width: 100%;\n}\n@media screen and (min-width: 768px) {\n .jumbotron {\n padding-top: 48px;\n padding-bottom: 48px;\n }\n .container .jumbotron,\n .container-fluid .jumbotron {\n padding-left: 60px;\n padding-right: 60px;\n }\n .jumbotron h1,\n .jumbotron .h1 {\n font-size: 63px;\n }\n}\n.thumbnail {\n display: block;\n padding: 4px;\n margin-bottom: 20px;\n line-height: 1.42857143;\n background-color: #ffffff;\n border: 1px solid #dddddd;\n border-radius: 4px;\n -webkit-transition: border 0.2s ease-in-out;\n -o-transition: border 0.2s ease-in-out;\n transition: border 0.2s ease-in-out;\n}\n.thumbnail > img,\n.thumbnail a > img {\n margin-left: auto;\n margin-right: auto;\n}\na.thumbnail:hover,\na.thumbnail:focus,\na.thumbnail.active {\n border-color: #337ab7;\n}\n.thumbnail .caption {\n padding: 9px;\n color: #333333;\n}\n.alert {\n padding: 15px;\n margin-bottom: 20px;\n border: 1px solid transparent;\n border-radius: 4px;\n}\n.alert h4 {\n margin-top: 0;\n color: inherit;\n}\n.alert .alert-link {\n font-weight: bold;\n}\n.alert > p,\n.alert > ul {\n margin-bottom: 0;\n}\n.alert > p + p {\n margin-top: 5px;\n}\n.alert-dismissable,\n.alert-dismissible {\n padding-right: 35px;\n}\n.alert-dismissable .close,\n.alert-dismissible .close {\n position: relative;\n top: -2px;\n right: -21px;\n color: inherit;\n}\n.alert-success {\n background-color: #dff0d8;\n border-color: #d6e9c6;\n color: #3c763d;\n}\n.alert-success hr {\n border-top-color: #c9e2b3;\n}\n.alert-success .alert-link {\n color: #2b542c;\n}\n.alert-info {\n background-color: #d9edf7;\n border-color: #bce8f1;\n color: #31708f;\n}\n.alert-info hr {\n border-top-color: #a6e1ec;\n}\n.alert-info .alert-link {\n color: #245269;\n}\n.alert-warning {\n background-color: #fcf8e3;\n border-color: #faebcc;\n color: #8a6d3b;\n}\n.alert-warning hr {\n border-top-color: #f7e1b5;\n}\n.alert-warning .alert-link {\n color: #66512c;\n}\n.alert-danger {\n background-color: #f2dede;\n border-color: #ebccd1;\n color: #a94442;\n}\n.alert-danger hr {\n border-top-color: #e4b9c0;\n}\n.alert-danger .alert-link {\n color: #843534;\n}\n@-webkit-keyframes progress-bar-stripes {\n from {\n background-position: 40px 0;\n }\n to {\n background-position: 0 0;\n }\n}\n@keyframes progress-bar-stripes {\n from {\n background-position: 40px 0;\n }\n to {\n background-position: 0 0;\n }\n}\n.progress {\n overflow: hidden;\n height: 20px;\n margin-bottom: 20px;\n background-color: #f5f5f5;\n border-radius: 4px;\n -webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1);\n box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1);\n}\n.progress-bar {\n float: left;\n width: 0%;\n height: 100%;\n font-size: 12px;\n line-height: 20px;\n color: #ffffff;\n text-align: center;\n background-color: #337ab7;\n -webkit-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15);\n box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15);\n -webkit-transition: width 0.6s ease;\n -o-transition: width 0.6s ease;\n transition: width 0.6s ease;\n}\n.progress-striped .progress-bar,\n.progress-bar-striped {\n background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n background-size: 40px 40px;\n}\n.progress.active .progress-bar,\n.progress-bar.active {\n -webkit-animation: progress-bar-stripes 2s linear infinite;\n -o-animation: progress-bar-stripes 2s linear infinite;\n animation: progress-bar-stripes 2s linear infinite;\n}\n.progress-bar-success {\n background-color: #5cb85c;\n}\n.progress-striped .progress-bar-success {\n background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n}\n.progress-bar-info {\n background-color: #5bc0de;\n}\n.progress-striped .progress-bar-info {\n background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n}\n.progress-bar-warning {\n background-color: #f0ad4e;\n}\n.progress-striped .progress-bar-warning {\n background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n}\n.progress-bar-danger {\n background-color: #d9534f;\n}\n.progress-striped .progress-bar-danger {\n background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n}\n.media {\n margin-top: 15px;\n}\n.media:first-child {\n margin-top: 0;\n}\n.media,\n.media-body {\n zoom: 1;\n overflow: hidden;\n}\n.media-body {\n width: 10000px;\n}\n.media-object {\n display: block;\n}\n.media-object.img-thumbnail {\n max-width: none;\n}\n.media-right,\n.media > .pull-right {\n padding-left: 10px;\n}\n.media-left,\n.media > .pull-left {\n padding-right: 10px;\n}\n.media-left,\n.media-right,\n.media-body {\n display: table-cell;\n vertical-align: top;\n}\n.media-middle {\n vertical-align: middle;\n}\n.media-bottom {\n vertical-align: bottom;\n}\n.media-heading {\n margin-top: 0;\n margin-bottom: 5px;\n}\n.media-list {\n padding-left: 0;\n list-style: none;\n}\n.list-group {\n margin-bottom: 20px;\n padding-left: 0;\n}\n.list-group-item {\n position: relative;\n display: block;\n padding: 10px 15px;\n margin-bottom: -1px;\n background-color: #ffffff;\n border: 1px solid #dddddd;\n}\n.list-group-item:first-child {\n border-top-right-radius: 4px;\n border-top-left-radius: 4px;\n}\n.list-group-item:last-child {\n margin-bottom: 0;\n border-bottom-right-radius: 4px;\n border-bottom-left-radius: 4px;\n}\na.list-group-item,\nbutton.list-group-item {\n color: #555555;\n}\na.list-group-item .list-group-item-heading,\nbutton.list-group-item .list-group-item-heading {\n color: #333333;\n}\na.list-group-item:hover,\nbutton.list-group-item:hover,\na.list-group-item:focus,\nbutton.list-group-item:focus {\n text-decoration: none;\n color: #555555;\n background-color: #f5f5f5;\n}\nbutton.list-group-item {\n width: 100%;\n text-align: left;\n}\n.list-group-item.disabled,\n.list-group-item.disabled:hover,\n.list-group-item.disabled:focus {\n background-color: #eeeeee;\n color: #777777;\n cursor: not-allowed;\n}\n.list-group-item.disabled .list-group-item-heading,\n.list-group-item.disabled:hover .list-group-item-heading,\n.list-group-item.disabled:focus .list-group-item-heading {\n color: inherit;\n}\n.list-group-item.disabled .list-group-item-text,\n.list-group-item.disabled:hover .list-group-item-text,\n.list-group-item.disabled:focus .list-group-item-text {\n color: #777777;\n}\n.list-group-item.active,\n.list-group-item.active:hover,\n.list-group-item.active:focus {\n z-index: 2;\n color: #ffffff;\n background-color: #337ab7;\n border-color: #337ab7;\n}\n.list-group-item.active .list-group-item-heading,\n.list-group-item.active:hover .list-group-item-heading,\n.list-group-item.active:focus .list-group-item-heading,\n.list-group-item.active .list-group-item-heading > small,\n.list-group-item.active:hover .list-group-item-heading > small,\n.list-group-item.active:focus .list-group-item-heading > small,\n.list-group-item.active .list-group-item-heading > .small,\n.list-group-item.active:hover .list-group-item-heading > .small,\n.list-group-item.active:focus .list-group-item-heading > .small {\n color: inherit;\n}\n.list-group-item.active .list-group-item-text,\n.list-group-item.active:hover .list-group-item-text,\n.list-group-item.active:focus .list-group-item-text {\n color: #c7ddef;\n}\n.list-group-item-success {\n color: #3c763d;\n background-color: #dff0d8;\n}\na.list-group-item-success,\nbutton.list-group-item-success {\n color: #3c763d;\n}\na.list-group-item-success .list-group-item-heading,\nbutton.list-group-item-success .list-group-item-heading {\n color: inherit;\n}\na.list-group-item-success:hover,\nbutton.list-group-item-success:hover,\na.list-group-item-success:focus,\nbutton.list-group-item-success:focus {\n color: #3c763d;\n background-color: #d0e9c6;\n}\na.list-group-item-success.active,\nbutton.list-group-item-success.active,\na.list-group-item-success.active:hover,\nbutton.list-group-item-success.active:hover,\na.list-group-item-success.active:focus,\nbutton.list-group-item-success.active:focus {\n color: #fff;\n background-color: #3c763d;\n border-color: #3c763d;\n}\n.list-group-item-info {\n color: #31708f;\n background-color: #d9edf7;\n}\na.list-group-item-info,\nbutton.list-group-item-info {\n color: #31708f;\n}\na.list-group-item-info .list-group-item-heading,\nbutton.list-group-item-info .list-group-item-heading {\n color: inherit;\n}\na.list-group-item-info:hover,\nbutton.list-group-item-info:hover,\na.list-group-item-info:focus,\nbutton.list-group-item-info:focus {\n color: #31708f;\n background-color: #c4e3f3;\n}\na.list-group-item-info.active,\nbutton.list-group-item-info.active,\na.list-group-item-info.active:hover,\nbutton.list-group-item-info.active:hover,\na.list-group-item-info.active:focus,\nbutton.list-group-item-info.active:focus {\n color: #fff;\n background-color: #31708f;\n border-color: #31708f;\n}\n.list-group-item-warning {\n color: #8a6d3b;\n background-color: #fcf8e3;\n}\na.list-group-item-warning,\nbutton.list-group-item-warning {\n color: #8a6d3b;\n}\na.list-group-item-warning .list-group-item-heading,\nbutton.list-group-item-warning .list-group-item-heading {\n color: inherit;\n}\na.list-group-item-warning:hover,\nbutton.list-group-item-warning:hover,\na.list-group-item-warning:focus,\nbutton.list-group-item-warning:focus {\n color: #8a6d3b;\n background-color: #faf2cc;\n}\na.list-group-item-warning.active,\nbutton.list-group-item-warning.active,\na.list-group-item-warning.active:hover,\nbutton.list-group-item-warning.active:hover,\na.list-group-item-warning.active:focus,\nbutton.list-group-item-warning.active:focus {\n color: #fff;\n background-color: #8a6d3b;\n border-color: #8a6d3b;\n}\n.list-group-item-danger {\n color: #a94442;\n background-color: #f2dede;\n}\na.list-group-item-danger,\nbutton.list-group-item-danger {\n color: #a94442;\n}\na.list-group-item-danger .list-group-item-heading,\nbutton.list-group-item-danger .list-group-item-heading {\n color: inherit;\n}\na.list-group-item-danger:hover,\nbutton.list-group-item-danger:hover,\na.list-group-item-danger:focus,\nbutton.list-group-item-danger:focus {\n color: #a94442;\n background-color: #ebcccc;\n}\na.list-group-item-danger.active,\nbutton.list-group-item-danger.active,\na.list-group-item-danger.active:hover,\nbutton.list-group-item-danger.active:hover,\na.list-group-item-danger.active:focus,\nbutton.list-group-item-danger.active:focus {\n color: #fff;\n background-color: #a94442;\n border-color: #a94442;\n}\n.list-group-item-heading {\n margin-top: 0;\n margin-bottom: 5px;\n}\n.list-group-item-text {\n margin-bottom: 0;\n line-height: 1.3;\n}\n.panel {\n margin-bottom: 20px;\n background-color: #ffffff;\n border: 1px solid transparent;\n border-radius: 4px;\n -webkit-box-shadow: 0 1px 1px rgba(0, 0, 0, 0.05);\n box-shadow: 0 1px 1px rgba(0, 0, 0, 0.05);\n}\n.panel-body {\n padding: 15px;\n}\n.panel-heading {\n padding: 10px 15px;\n border-bottom: 1px solid transparent;\n border-top-right-radius: 3px;\n border-top-left-radius: 3px;\n}\n.panel-heading > .dropdown .dropdown-toggle {\n color: inherit;\n}\n.panel-title {\n margin-top: 0;\n margin-bottom: 0;\n font-size: 16px;\n color: inherit;\n}\n.panel-title > a,\n.panel-title > small,\n.panel-title > .small,\n.panel-title > small > a,\n.panel-title > .small > a {\n color: inherit;\n}\n.panel-footer {\n padding: 10px 15px;\n background-color: #f5f5f5;\n border-top: 1px solid #dddddd;\n border-bottom-right-radius: 3px;\n border-bottom-left-radius: 3px;\n}\n.panel > .list-group,\n.panel > .panel-collapse > .list-group {\n margin-bottom: 0;\n}\n.panel > .list-group .list-group-item,\n.panel > .panel-collapse > .list-group .list-group-item {\n border-width: 1px 0;\n border-radius: 0;\n}\n.panel > .list-group:first-child .list-group-item:first-child,\n.panel > .panel-collapse > .list-group:first-child .list-group-item:first-child {\n border-top: 0;\n border-top-right-radius: 3px;\n border-top-left-radius: 3px;\n}\n.panel > .list-group:last-child .list-group-item:last-child,\n.panel > .panel-collapse > .list-group:last-child .list-group-item:last-child {\n border-bottom: 0;\n border-bottom-right-radius: 3px;\n border-bottom-left-radius: 3px;\n}\n.panel > .panel-heading + .panel-collapse > .list-group .list-group-item:first-child {\n border-top-right-radius: 0;\n border-top-left-radius: 0;\n}\n.panel-heading + .list-group .list-group-item:first-child {\n border-top-width: 0;\n}\n.list-group + .panel-footer {\n border-top-width: 0;\n}\n.panel > .table,\n.panel > .table-responsive > .table,\n.panel > .panel-collapse > .table {\n margin-bottom: 0;\n}\n.panel > .table caption,\n.panel > .table-responsive > .table caption,\n.panel > .panel-collapse > .table caption {\n padding-left: 15px;\n padding-right: 15px;\n}\n.panel > .table:first-child,\n.panel > .table-responsive:first-child > .table:first-child {\n border-top-right-radius: 3px;\n border-top-left-radius: 3px;\n}\n.panel > .table:first-child > thead:first-child > tr:first-child,\n.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child,\n.panel > .table:first-child > tbody:first-child > tr:first-child,\n.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child {\n border-top-left-radius: 3px;\n border-top-right-radius: 3px;\n}\n.panel > .table:first-child > thead:first-child > tr:first-child td:first-child,\n.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child td:first-child,\n.panel > .table:first-child > tbody:first-child > tr:first-child td:first-child,\n.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child td:first-child,\n.panel > .table:first-child > thead:first-child > tr:first-child th:first-child,\n.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child th:first-child,\n.panel > .table:first-child > tbody:first-child > tr:first-child th:first-child,\n.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child th:first-child {\n border-top-left-radius: 3px;\n}\n.panel > .table:first-child > thead:first-child > tr:first-child td:last-child,\n.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child td:last-child,\n.panel > .table:first-child > tbody:first-child > tr:first-child td:last-child,\n.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child td:last-child,\n.panel > .table:first-child > thead:first-child > tr:first-child th:last-child,\n.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child th:last-child,\n.panel > .table:first-child > tbody:first-child > tr:first-child th:last-child,\n.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child th:last-child {\n border-top-right-radius: 3px;\n}\n.panel > .table:last-child,\n.panel > .table-responsive:last-child > .table:last-child {\n border-bottom-right-radius: 3px;\n border-bottom-left-radius: 3px;\n}\n.panel > .table:last-child > tbody:last-child > tr:last-child,\n.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child,\n.panel > .table:last-child > tfoot:last-child > tr:last-child,\n.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child {\n border-bottom-left-radius: 3px;\n border-bottom-right-radius: 3px;\n}\n.panel > .table:last-child > tbody:last-child > tr:last-child td:first-child,\n.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child td:first-child,\n.panel > .table:last-child > tfoot:last-child > tr:last-child td:first-child,\n.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child td:first-child,\n.panel > .table:last-child > tbody:last-child > tr:last-child th:first-child,\n.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child th:first-child,\n.panel > .table:last-child > tfoot:last-child > tr:last-child th:first-child,\n.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child th:first-child {\n border-bottom-left-radius: 3px;\n}\n.panel > .table:last-child > tbody:last-child > tr:last-child td:last-child,\n.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child td:last-child,\n.panel > .table:last-child > tfoot:last-child > tr:last-child td:last-child,\n.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child td:last-child,\n.panel > .table:last-child > tbody:last-child > tr:last-child th:last-child,\n.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child th:last-child,\n.panel > .table:last-child > tfoot:last-child > tr:last-child th:last-child,\n.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child th:last-child {\n border-bottom-right-radius: 3px;\n}\n.panel > .panel-body + .table,\n.panel > .panel-body + .table-responsive,\n.panel > .table + .panel-body,\n.panel > .table-responsive + .panel-body {\n border-top: 1px solid #dddddd;\n}\n.panel > .table > tbody:first-child > tr:first-child th,\n.panel > .table > tbody:first-child > tr:first-child td {\n border-top: 0;\n}\n.panel > .table-bordered,\n.panel > .table-responsive > .table-bordered {\n border: 0;\n}\n.panel > .table-bordered > thead > tr > th:first-child,\n.panel > .table-responsive > .table-bordered > thead > tr > th:first-child,\n.panel > .table-bordered > tbody > tr > th:first-child,\n.panel > .table-responsive > .table-bordered > tbody > tr > th:first-child,\n.panel > .table-bordered > tfoot > tr > th:first-child,\n.panel > .table-responsive > .table-bordered > tfoot > tr > th:first-child,\n.panel > .table-bordered > thead > tr > td:first-child,\n.panel > .table-responsive > .table-bordered > thead > tr > td:first-child,\n.panel > .table-bordered > tbody > tr > td:first-child,\n.panel > .table-responsive > .table-bordered > tbody > tr > td:first-child,\n.panel > .table-bordered > tfoot > tr > td:first-child,\n.panel > .table-responsive > .table-bordered > tfoot > tr > td:first-child {\n border-left: 0;\n}\n.panel > .table-bordered > thead > tr > th:last-child,\n.panel > .table-responsive > .table-bordered > thead > tr > th:last-child,\n.panel > .table-bordered > tbody > tr > th:last-child,\n.panel > .table-responsive > .table-bordered > tbody > tr > th:last-child,\n.panel > .table-bordered > tfoot > tr > th:last-child,\n.panel > .table-responsive > .table-bordered > tfoot > tr > th:last-child,\n.panel > .table-bordered > thead > tr > td:last-child,\n.panel > .table-responsive > .table-bordered > thead > tr > td:last-child,\n.panel > .table-bordered > tbody > tr > td:last-child,\n.panel > .table-responsive > .table-bordered > tbody > tr > td:last-child,\n.panel > .table-bordered > tfoot > tr > td:last-child,\n.panel > .table-responsive > .table-bordered > tfoot > tr > td:last-child {\n border-right: 0;\n}\n.panel > .table-bordered > thead > tr:first-child > td,\n.panel > .table-responsive > .table-bordered > thead > tr:first-child > td,\n.panel > .table-bordered > tbody > tr:first-child > td,\n.panel > .table-responsive > .table-bordered > tbody > tr:first-child > td,\n.panel > .table-bordered > thead > tr:first-child > th,\n.panel > .table-responsive > .table-bordered > thead > tr:first-child > th,\n.panel > .table-bordered > tbody > tr:first-child > th,\n.panel > .table-responsive > .table-bordered > tbody > tr:first-child > th {\n border-bottom: 0;\n}\n.panel > .table-bordered > tbody > tr:last-child > td,\n.panel > .table-responsive > .table-bordered > tbody > tr:last-child > td,\n.panel > .table-bordered > tfoot > tr:last-child > td,\n.panel > .table-responsive > .table-bordered > tfoot > tr:last-child > td,\n.panel > .table-bordered > tbody > tr:last-child > th,\n.panel > .table-responsive > .table-bordered > tbody > tr:last-child > th,\n.panel > .table-bordered > tfoot > tr:last-child > th,\n.panel > .table-responsive > .table-bordered > tfoot > tr:last-child > th {\n border-bottom: 0;\n}\n.panel > .table-responsive {\n border: 0;\n margin-bottom: 0;\n}\n.panel-group {\n margin-bottom: 20px;\n}\n.panel-group .panel {\n margin-bottom: 0;\n border-radius: 4px;\n}\n.panel-group .panel + .panel {\n margin-top: 5px;\n}\n.panel-group .panel-heading {\n border-bottom: 0;\n}\n.panel-group .panel-heading + .panel-collapse > .panel-body,\n.panel-group .panel-heading + .panel-collapse > .list-group {\n border-top: 1px solid #dddddd;\n}\n.panel-group .panel-footer {\n border-top: 0;\n}\n.panel-group .panel-footer + .panel-collapse .panel-body {\n border-bottom: 1px solid #dddddd;\n}\n.panel-default {\n border-color: #dddddd;\n}\n.panel-default > .panel-heading {\n color: #333333;\n background-color: #f5f5f5;\n border-color: #dddddd;\n}\n.panel-default > .panel-heading + .panel-collapse > .panel-body {\n border-top-color: #dddddd;\n}\n.panel-default > .panel-heading .badge {\n color: #f5f5f5;\n background-color: #333333;\n}\n.panel-default > .panel-footer + .panel-collapse > .panel-body {\n border-bottom-color: #dddddd;\n}\n.panel-primary {\n border-color: #337ab7;\n}\n.panel-primary > .panel-heading {\n color: #ffffff;\n background-color: #337ab7;\n border-color: #337ab7;\n}\n.panel-primary > .panel-heading + .panel-collapse > .panel-body {\n border-top-color: #337ab7;\n}\n.panel-primary > .panel-heading .badge {\n color: #337ab7;\n background-color: #ffffff;\n}\n.panel-primary > .panel-footer + .panel-collapse > .panel-body {\n border-bottom-color: #337ab7;\n}\n.panel-success {\n border-color: #d6e9c6;\n}\n.panel-success > .panel-heading {\n color: #3c763d;\n background-color: #dff0d8;\n border-color: #d6e9c6;\n}\n.panel-success > .panel-heading + .panel-collapse > .panel-body {\n border-top-color: #d6e9c6;\n}\n.panel-success > .panel-heading .badge {\n color: #dff0d8;\n background-color: #3c763d;\n}\n.panel-success > .panel-footer + .panel-collapse > .panel-body {\n border-bottom-color: #d6e9c6;\n}\n.panel-info {\n border-color: #bce8f1;\n}\n.panel-info > .panel-heading {\n color: #31708f;\n background-color: #d9edf7;\n border-color: #bce8f1;\n}\n.panel-info > .panel-heading + .panel-collapse > .panel-body {\n border-top-color: #bce8f1;\n}\n.panel-info > .panel-heading .badge {\n color: #d9edf7;\n background-color: #31708f;\n}\n.panel-info > .panel-footer + .panel-collapse > .panel-body {\n border-bottom-color: #bce8f1;\n}\n.panel-warning {\n border-color: #faebcc;\n}\n.panel-warning > .panel-heading {\n color: #8a6d3b;\n background-color: #fcf8e3;\n border-color: #faebcc;\n}\n.panel-warning > .panel-heading + .panel-collapse > .panel-body {\n border-top-color: #faebcc;\n}\n.panel-warning > .panel-heading .badge {\n color: #fcf8e3;\n background-color: #8a6d3b;\n}\n.panel-warning > .panel-footer + .panel-collapse > .panel-body {\n border-bottom-color: #faebcc;\n}\n.panel-danger {\n border-color: #ebccd1;\n}\n.panel-danger > .panel-heading {\n color: #a94442;\n background-color: #f2dede;\n border-color: #ebccd1;\n}\n.panel-danger > .panel-heading + .panel-collapse > .panel-body {\n border-top-color: #ebccd1;\n}\n.panel-danger > .panel-heading .badge {\n color: #f2dede;\n background-color: #a94442;\n}\n.panel-danger > .panel-footer + .panel-collapse > .panel-body {\n border-bottom-color: #ebccd1;\n}\n.embed-responsive {\n position: relative;\n display: block;\n height: 0;\n padding: 0;\n overflow: hidden;\n}\n.embed-responsive .embed-responsive-item,\n.embed-responsive iframe,\n.embed-responsive embed,\n.embed-responsive object,\n.embed-responsive video {\n position: absolute;\n top: 0;\n left: 0;\n bottom: 0;\n height: 100%;\n width: 100%;\n border: 0;\n}\n.embed-responsive-16by9 {\n padding-bottom: 56.25%;\n}\n.embed-responsive-4by3 {\n padding-bottom: 75%;\n}\n.well {\n min-height: 20px;\n padding: 19px;\n margin-bottom: 20px;\n background-color: #f5f5f5;\n border: 1px solid #e3e3e3;\n border-radius: 4px;\n -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.05);\n box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.05);\n}\n.well blockquote {\n border-color: #ddd;\n border-color: rgba(0, 0, 0, 0.15);\n}\n.well-lg {\n padding: 24px;\n border-radius: 6px;\n}\n.well-sm {\n padding: 9px;\n border-radius: 3px;\n}\n.close {\n float: right;\n font-size: 21px;\n font-weight: bold;\n line-height: 1;\n color: #000000;\n text-shadow: 0 1px 0 #ffffff;\n opacity: 0.2;\n filter: alpha(opacity=20);\n}\n.close:hover,\n.close:focus {\n color: #000000;\n text-decoration: none;\n cursor: pointer;\n opacity: 0.5;\n filter: alpha(opacity=50);\n}\nbutton.close {\n padding: 0;\n cursor: pointer;\n background: transparent;\n border: 0;\n -webkit-appearance: none;\n}\n.modal-open {\n overflow: hidden;\n}\n.modal {\n display: none;\n overflow: hidden;\n position: fixed;\n top: 0;\n right: 0;\n bottom: 0;\n left: 0;\n z-index: 1050;\n -webkit-overflow-scrolling: touch;\n outline: 0;\n}\n.modal.fade .modal-dialog {\n -webkit-transform: translate(0, -25%);\n -ms-transform: translate(0, -25%);\n -o-transform: translate(0, -25%);\n transform: translate(0, -25%);\n -webkit-transition: -webkit-transform 0.3s ease-out;\n -moz-transition: -moz-transform 0.3s ease-out;\n -o-transition: -o-transform 0.3s ease-out;\n transition: transform 0.3s ease-out;\n}\n.modal.in .modal-dialog {\n -webkit-transform: translate(0, 0);\n -ms-transform: translate(0, 0);\n -o-transform: translate(0, 0);\n transform: translate(0, 0);\n}\n.modal-open .modal {\n overflow-x: hidden;\n overflow-y: auto;\n}\n.modal-dialog {\n position: relative;\n width: auto;\n margin: 10px;\n}\n.modal-content {\n position: relative;\n background-color: #ffffff;\n border: 1px solid #999999;\n border: 1px solid rgba(0, 0, 0, 0.2);\n border-radius: 6px;\n -webkit-box-shadow: 0 3px 9px rgba(0, 0, 0, 0.5);\n box-shadow: 0 3px 9px rgba(0, 0, 0, 0.5);\n background-clip: padding-box;\n outline: 0;\n}\n.modal-backdrop {\n position: fixed;\n top: 0;\n right: 0;\n bottom: 0;\n left: 0;\n z-index: 1040;\n background-color: #000000;\n}\n.modal-backdrop.fade {\n opacity: 0;\n filter: alpha(opacity=0);\n}\n.modal-backdrop.in {\n opacity: 0.5;\n filter: alpha(opacity=50);\n}\n.modal-header {\n padding: 15px;\n border-bottom: 1px solid #e5e5e5;\n min-height: 16.42857143px;\n}\n.modal-header .close {\n margin-top: -2px;\n}\n.modal-title {\n margin: 0;\n line-height: 1.42857143;\n}\n.modal-body {\n position: relative;\n padding: 15px;\n}\n.modal-footer {\n padding: 15px;\n text-align: right;\n border-top: 1px solid #e5e5e5;\n}\n.modal-footer .btn + .btn {\n margin-left: 5px;\n margin-bottom: 0;\n}\n.modal-footer .btn-group .btn + .btn {\n margin-left: -1px;\n}\n.modal-footer .btn-block + .btn-block {\n margin-left: 0;\n}\n.modal-scrollbar-measure {\n position: absolute;\n top: -9999px;\n width: 50px;\n height: 50px;\n overflow: scroll;\n}\n@media (min-width: 768px) {\n .modal-dialog {\n width: 600px;\n margin: 30px auto;\n }\n .modal-content {\n -webkit-box-shadow: 0 5px 15px rgba(0, 0, 0, 0.5);\n box-shadow: 0 5px 15px rgba(0, 0, 0, 0.5);\n }\n .modal-sm {\n width: 300px;\n }\n}\n@media (min-width: 992px) {\n .modal-lg {\n width: 900px;\n }\n}\n.tooltip {\n position: absolute;\n z-index: 1070;\n display: block;\n font-family: \"Helvetica Neue\", Helvetica, Arial, sans-serif;\n font-style: normal;\n font-weight: normal;\n letter-spacing: normal;\n line-break: auto;\n line-height: 1.42857143;\n text-align: left;\n text-align: start;\n text-decoration: none;\n text-shadow: none;\n text-transform: none;\n white-space: normal;\n word-break: normal;\n word-spacing: normal;\n word-wrap: normal;\n font-size: 12px;\n opacity: 0;\n filter: alpha(opacity=0);\n}\n.tooltip.in {\n opacity: 0.9;\n filter: alpha(opacity=90);\n}\n.tooltip.top {\n margin-top: -3px;\n padding: 5px 0;\n}\n.tooltip.right {\n margin-left: 3px;\n padding: 0 5px;\n}\n.tooltip.bottom {\n margin-top: 3px;\n padding: 5px 0;\n}\n.tooltip.left {\n margin-left: -3px;\n padding: 0 5px;\n}\n.tooltip-inner {\n max-width: 200px;\n padding: 3px 8px;\n color: #ffffff;\n text-align: center;\n background-color: #000000;\n border-radius: 4px;\n}\n.tooltip-arrow {\n position: absolute;\n width: 0;\n height: 0;\n border-color: transparent;\n border-style: solid;\n}\n.tooltip.top .tooltip-arrow {\n bottom: 0;\n left: 50%;\n margin-left: -5px;\n border-width: 5px 5px 0;\n border-top-color: #000000;\n}\n.tooltip.top-left .tooltip-arrow {\n bottom: 0;\n right: 5px;\n margin-bottom: -5px;\n border-width: 5px 5px 0;\n border-top-color: #000000;\n}\n.tooltip.top-right .tooltip-arrow {\n bottom: 0;\n left: 5px;\n margin-bottom: -5px;\n border-width: 5px 5px 0;\n border-top-color: #000000;\n}\n.tooltip.right .tooltip-arrow {\n top: 50%;\n left: 0;\n margin-top: -5px;\n border-width: 5px 5px 5px 0;\n border-right-color: #000000;\n}\n.tooltip.left .tooltip-arrow {\n top: 50%;\n right: 0;\n margin-top: -5px;\n border-width: 5px 0 5px 5px;\n border-left-color: #000000;\n}\n.tooltip.bottom .tooltip-arrow {\n top: 0;\n left: 50%;\n margin-left: -5px;\n border-width: 0 5px 5px;\n border-bottom-color: #000000;\n}\n.tooltip.bottom-left .tooltip-arrow {\n top: 0;\n right: 5px;\n margin-top: -5px;\n border-width: 0 5px 5px;\n border-bottom-color: #000000;\n}\n.tooltip.bottom-right .tooltip-arrow {\n top: 0;\n left: 5px;\n margin-top: -5px;\n border-width: 0 5px 5px;\n border-bottom-color: #000000;\n}\n.popover {\n position: absolute;\n top: 0;\n left: 0;\n z-index: 1060;\n display: none;\n max-width: 276px;\n padding: 1px;\n font-family: \"Helvetica Neue\", Helvetica, Arial, sans-serif;\n font-style: normal;\n font-weight: normal;\n letter-spacing: normal;\n line-break: auto;\n line-height: 1.42857143;\n text-align: left;\n text-align: start;\n text-decoration: none;\n text-shadow: none;\n text-transform: none;\n white-space: normal;\n word-break: normal;\n word-spacing: normal;\n word-wrap: normal;\n font-size: 14px;\n background-color: #ffffff;\n background-clip: padding-box;\n border: 1px solid #cccccc;\n border: 1px solid rgba(0, 0, 0, 0.2);\n border-radius: 6px;\n -webkit-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);\n box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);\n}\n.popover.top {\n margin-top: -10px;\n}\n.popover.right {\n margin-left: 10px;\n}\n.popover.bottom {\n margin-top: 10px;\n}\n.popover.left {\n margin-left: -10px;\n}\n.popover-title {\n margin: 0;\n padding: 8px 14px;\n font-size: 14px;\n background-color: #f7f7f7;\n border-bottom: 1px solid #ebebeb;\n border-radius: 5px 5px 0 0;\n}\n.popover-content {\n padding: 9px 14px;\n}\n.popover > .arrow,\n.popover > .arrow:after {\n position: absolute;\n display: block;\n width: 0;\n height: 0;\n border-color: transparent;\n border-style: solid;\n}\n.popover > .arrow {\n border-width: 11px;\n}\n.popover > .arrow:after {\n border-width: 10px;\n content: \"\";\n}\n.popover.top > .arrow {\n left: 50%;\n margin-left: -11px;\n border-bottom-width: 0;\n border-top-color: #999999;\n border-top-color: rgba(0, 0, 0, 0.25);\n bottom: -11px;\n}\n.popover.top > .arrow:after {\n content: \" \";\n bottom: 1px;\n margin-left: -10px;\n border-bottom-width: 0;\n border-top-color: #ffffff;\n}\n.popover.right > .arrow {\n top: 50%;\n left: -11px;\n margin-top: -11px;\n border-left-width: 0;\n border-right-color: #999999;\n border-right-color: rgba(0, 0, 0, 0.25);\n}\n.popover.right > .arrow:after {\n content: \" \";\n left: 1px;\n bottom: -10px;\n border-left-width: 0;\n border-right-color: #ffffff;\n}\n.popover.bottom > .arrow {\n left: 50%;\n margin-left: -11px;\n border-top-width: 0;\n border-bottom-color: #999999;\n border-bottom-color: rgba(0, 0, 0, 0.25);\n top: -11px;\n}\n.popover.bottom > .arrow:after {\n content: \" \";\n top: 1px;\n margin-left: -10px;\n border-top-width: 0;\n border-bottom-color: #ffffff;\n}\n.popover.left > .arrow {\n top: 50%;\n right: -11px;\n margin-top: -11px;\n border-right-width: 0;\n border-left-color: #999999;\n border-left-color: rgba(0, 0, 0, 0.25);\n}\n.popover.left > .arrow:after {\n content: \" \";\n right: 1px;\n border-right-width: 0;\n border-left-color: #ffffff;\n bottom: -10px;\n}\n.carousel {\n position: relative;\n}\n.carousel-inner {\n position: relative;\n overflow: hidden;\n width: 100%;\n}\n.carousel-inner > .item {\n display: none;\n position: relative;\n -webkit-transition: 0.6s ease-in-out left;\n -o-transition: 0.6s ease-in-out left;\n transition: 0.6s ease-in-out left;\n}\n.carousel-inner > .item > img,\n.carousel-inner > .item > a > img {\n line-height: 1;\n}\n@media all and (transform-3d), (-webkit-transform-3d) {\n .carousel-inner > .item {\n -webkit-transition: -webkit-transform 0.6s ease-in-out;\n -moz-transition: -moz-transform 0.6s ease-in-out;\n -o-transition: -o-transform 0.6s ease-in-out;\n transition: transform 0.6s ease-in-out;\n -webkit-backface-visibility: hidden;\n -moz-backface-visibility: hidden;\n backface-visibility: hidden;\n -webkit-perspective: 1000px;\n -moz-perspective: 1000px;\n perspective: 1000px;\n }\n .carousel-inner > .item.next,\n .carousel-inner > .item.active.right {\n -webkit-transform: translate3d(100%, 0, 0);\n transform: translate3d(100%, 0, 0);\n left: 0;\n }\n .carousel-inner > .item.prev,\n .carousel-inner > .item.active.left {\n -webkit-transform: translate3d(-100%, 0, 0);\n transform: translate3d(-100%, 0, 0);\n left: 0;\n }\n .carousel-inner > .item.next.left,\n .carousel-inner > .item.prev.right,\n .carousel-inner > .item.active {\n -webkit-transform: translate3d(0, 0, 0);\n transform: translate3d(0, 0, 0);\n left: 0;\n }\n}\n.carousel-inner > .active,\n.carousel-inner > .next,\n.carousel-inner > .prev {\n display: block;\n}\n.carousel-inner > .active {\n left: 0;\n}\n.carousel-inner > .next,\n.carousel-inner > .prev {\n position: absolute;\n top: 0;\n width: 100%;\n}\n.carousel-inner > .next {\n left: 100%;\n}\n.carousel-inner > .prev {\n left: -100%;\n}\n.carousel-inner > .next.left,\n.carousel-inner > .prev.right {\n left: 0;\n}\n.carousel-inner > .active.left {\n left: -100%;\n}\n.carousel-inner > .active.right {\n left: 100%;\n}\n.carousel-control {\n position: absolute;\n top: 0;\n left: 0;\n bottom: 0;\n width: 15%;\n opacity: 0.5;\n filter: alpha(opacity=50);\n font-size: 20px;\n color: #ffffff;\n text-align: center;\n text-shadow: 0 1px 2px rgba(0, 0, 0, 0.6);\n}\n.carousel-control.left {\n background-image: -webkit-linear-gradient(left, rgba(0, 0, 0, 0.5) 0%, rgba(0, 0, 0, 0.0001) 100%);\n background-image: -o-linear-gradient(left, rgba(0, 0, 0, 0.5) 0%, rgba(0, 0, 0, 0.0001) 100%);\n background-image: linear-gradient(to right, rgba(0, 0, 0, 0.5) 0%, rgba(0, 0, 0, 0.0001) 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#80000000', endColorstr='#00000000', GradientType=1);\n}\n.carousel-control.right {\n left: auto;\n right: 0;\n background-image: -webkit-linear-gradient(left, rgba(0, 0, 0, 0.0001) 0%, rgba(0, 0, 0, 0.5) 100%);\n background-image: -o-linear-gradient(left, rgba(0, 0, 0, 0.0001) 0%, rgba(0, 0, 0, 0.5) 100%);\n background-image: linear-gradient(to right, rgba(0, 0, 0, 0.0001) 0%, rgba(0, 0, 0, 0.5) 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#00000000', endColorstr='#80000000', GradientType=1);\n}\n.carousel-control:hover,\n.carousel-control:focus {\n outline: 0;\n color: #ffffff;\n text-decoration: none;\n opacity: 0.9;\n filter: alpha(opacity=90);\n}\n.carousel-control .icon-prev,\n.carousel-control .icon-next,\n.carousel-control .glyphicon-chevron-left,\n.carousel-control .glyphicon-chevron-right {\n position: absolute;\n top: 50%;\n margin-top: -10px;\n z-index: 5;\n display: inline-block;\n}\n.carousel-control .icon-prev,\n.carousel-control .glyphicon-chevron-left {\n left: 50%;\n margin-left: -10px;\n}\n.carousel-control .icon-next,\n.carousel-control .glyphicon-chevron-right {\n right: 50%;\n margin-right: -10px;\n}\n.carousel-control .icon-prev,\n.carousel-control .icon-next {\n width: 20px;\n height: 20px;\n line-height: 1;\n font-family: serif;\n}\n.carousel-control .icon-prev:before {\n content: '\\2039';\n}\n.carousel-control .icon-next:before {\n content: '\\203a';\n}\n.carousel-indicators {\n position: absolute;\n bottom: 10px;\n left: 50%;\n z-index: 15;\n width: 60%;\n margin-left: -30%;\n padding-left: 0;\n list-style: none;\n text-align: center;\n}\n.carousel-indicators li {\n display: inline-block;\n width: 10px;\n height: 10px;\n margin: 1px;\n text-indent: -999px;\n border: 1px solid #ffffff;\n border-radius: 10px;\n cursor: pointer;\n background-color: #000 \\9;\n background-color: rgba(0, 0, 0, 0);\n}\n.carousel-indicators .active {\n margin: 0;\n width: 12px;\n height: 12px;\n background-color: #ffffff;\n}\n.carousel-caption {\n position: absolute;\n left: 15%;\n right: 15%;\n bottom: 20px;\n z-index: 10;\n padding-top: 20px;\n padding-bottom: 20px;\n color: #ffffff;\n text-align: center;\n text-shadow: 0 1px 2px rgba(0, 0, 0, 0.6);\n}\n.carousel-caption .btn {\n text-shadow: none;\n}\n@media screen and (min-width: 768px) {\n .carousel-control .glyphicon-chevron-left,\n .carousel-control .glyphicon-chevron-right,\n .carousel-control .icon-prev,\n .carousel-control .icon-next {\n width: 30px;\n height: 30px;\n margin-top: -15px;\n font-size: 30px;\n }\n .carousel-control .glyphicon-chevron-left,\n .carousel-control .icon-prev {\n margin-left: -15px;\n }\n .carousel-control .glyphicon-chevron-right,\n .carousel-control .icon-next {\n margin-right: -15px;\n }\n .carousel-caption {\n left: 20%;\n right: 20%;\n padding-bottom: 30px;\n }\n .carousel-indicators {\n bottom: 20px;\n }\n}\n.clearfix:before,\n.clearfix:after,\n.dl-horizontal dd:before,\n.dl-horizontal dd:after,\n.container:before,\n.container:after,\n.container-fluid:before,\n.container-fluid:after,\n.row:before,\n.row:after,\n.form-horizontal .form-group:before,\n.form-horizontal .form-group:after,\n.btn-toolbar:before,\n.btn-toolbar:after,\n.btn-group-vertical > .btn-group:before,\n.btn-group-vertical > .btn-group:after,\n.nav:before,\n.nav:after,\n.navbar:before,\n.navbar:after,\n.navbar-header:before,\n.navbar-header:after,\n.navbar-collapse:before,\n.navbar-collapse:after,\n.pager:before,\n.pager:after,\n.panel-body:before,\n.panel-body:after,\n.modal-footer:before,\n.modal-footer:after {\n content: \" \";\n display: table;\n}\n.clearfix:after,\n.dl-horizontal dd:after,\n.container:after,\n.container-fluid:after,\n.row:after,\n.form-horizontal .form-group:after,\n.btn-toolbar:after,\n.btn-group-vertical > .btn-group:after,\n.nav:after,\n.navbar:after,\n.navbar-header:after,\n.navbar-collapse:after,\n.pager:after,\n.panel-body:after,\n.modal-footer:after {\n clear: both;\n}\n.center-block {\n display: block;\n margin-left: auto;\n margin-right: auto;\n}\n.pull-right {\n float: right !important;\n}\n.pull-left {\n float: left !important;\n}\n.hide {\n display: none !important;\n}\n.show {\n display: block !important;\n}\n.invisible {\n visibility: hidden;\n}\n.text-hide {\n font: 0/0 a;\n color: transparent;\n text-shadow: none;\n background-color: transparent;\n border: 0;\n}\n.hidden {\n display: none !important;\n}\n.affix {\n position: fixed;\n}\n@-ms-viewport {\n width: device-width;\n}\n.visible-xs,\n.visible-sm,\n.visible-md,\n.visible-lg {\n display: none !important;\n}\n.visible-xs-block,\n.visible-xs-inline,\n.visible-xs-inline-block,\n.visible-sm-block,\n.visible-sm-inline,\n.visible-sm-inline-block,\n.visible-md-block,\n.visible-md-inline,\n.visible-md-inline-block,\n.visible-lg-block,\n.visible-lg-inline,\n.visible-lg-inline-block {\n display: none !important;\n}\n@media (max-width: 767px) {\n .visible-xs {\n display: block !important;\n }\n table.visible-xs {\n display: table !important;\n }\n tr.visible-xs {\n display: table-row !important;\n }\n th.visible-xs,\n td.visible-xs {\n display: table-cell !important;\n }\n}\n@media (max-width: 767px) {\n .visible-xs-block {\n display: block !important;\n }\n}\n@media (max-width: 767px) {\n .visible-xs-inline {\n display: inline !important;\n }\n}\n@media (max-width: 767px) {\n .visible-xs-inline-block {\n display: inline-block !important;\n }\n}\n@media (min-width: 768px) and (max-width: 991px) {\n .visible-sm {\n display: block !important;\n }\n table.visible-sm {\n display: table !important;\n }\n tr.visible-sm {\n display: table-row !important;\n }\n th.visible-sm,\n td.visible-sm {\n display: table-cell !important;\n }\n}\n@media (min-width: 768px) and (max-width: 991px) {\n .visible-sm-block {\n display: block !important;\n }\n}\n@media (min-width: 768px) and (max-width: 991px) {\n .visible-sm-inline {\n display: inline !important;\n }\n}\n@media (min-width: 768px) and (max-width: 991px) {\n .visible-sm-inline-block {\n display: inline-block !important;\n }\n}\n@media (min-width: 992px) and (max-width: 1199px) {\n .visible-md {\n display: block !important;\n }\n table.visible-md {\n display: table !important;\n }\n tr.visible-md {\n display: table-row !important;\n }\n th.visible-md,\n td.visible-md {\n display: table-cell !important;\n }\n}\n@media (min-width: 992px) and (max-width: 1199px) {\n .visible-md-block {\n display: block !important;\n }\n}\n@media (min-width: 992px) and (max-width: 1199px) {\n .visible-md-inline {\n display: inline !important;\n }\n}\n@media (min-width: 992px) and (max-width: 1199px) {\n .visible-md-inline-block {\n display: inline-block !important;\n }\n}\n@media (min-width: 1200px) {\n .visible-lg {\n display: block !important;\n }\n table.visible-lg {\n display: table !important;\n }\n tr.visible-lg {\n display: table-row !important;\n }\n th.visible-lg,\n td.visible-lg {\n display: table-cell !important;\n }\n}\n@media (min-width: 1200px) {\n .visible-lg-block {\n display: block !important;\n }\n}\n@media (min-width: 1200px) {\n .visible-lg-inline {\n display: inline !important;\n }\n}\n@media (min-width: 1200px) {\n .visible-lg-inline-block {\n display: inline-block !important;\n }\n}\n@media (max-width: 767px) {\n .hidden-xs {\n display: none !important;\n }\n}\n@media (min-width: 768px) and (max-width: 991px) {\n .hidden-sm {\n display: none !important;\n }\n}\n@media (min-width: 992px) and (max-width: 1199px) {\n .hidden-md {\n display: none !important;\n }\n}\n@media (min-width: 1200px) {\n .hidden-lg {\n display: none !important;\n }\n}\n.visible-print {\n display: none !important;\n}\n@media print {\n .visible-print {\n display: block !important;\n }\n table.visible-print {\n display: table !important;\n }\n tr.visible-print {\n display: table-row !important;\n }\n th.visible-print,\n td.visible-print {\n display: table-cell !important;\n }\n}\n.visible-print-block {\n display: none !important;\n}\n@media print {\n .visible-print-block {\n display: block !important;\n }\n}\n.visible-print-inline {\n display: none !important;\n}\n@media print {\n .visible-print-inline {\n display: inline !important;\n }\n}\n.visible-print-inline-block {\n display: none !important;\n}\n@media print {\n .visible-print-inline-block {\n display: inline-block !important;\n }\n}\n@media print {\n .hidden-print {\n display: none !important;\n }\n}\n/*# sourceMappingURL=bootstrap.css.map */","/*! normalize.css v3.0.3 | MIT License | github.com/necolas/normalize.css */\n\n//\n// 1. Set default font family to sans-serif.\n// 2. Prevent iOS and IE text size adjust after device orientation change,\n// without disabling user zoom.\n//\n\nhtml {\n font-family: sans-serif; // 1\n -ms-text-size-adjust: 100%; // 2\n -webkit-text-size-adjust: 100%; // 2\n}\n\n//\n// Remove default margin.\n//\n\nbody {\n margin: 0;\n}\n\n// HTML5 display definitions\n// ==========================================================================\n\n//\n// Correct `block` display not defined for any HTML5 element in IE 8/9.\n// Correct `block` display not defined for `details` or `summary` in IE 10/11\n// and Firefox.\n// Correct `block` display not defined for `main` in IE 11.\n//\n\narticle,\naside,\ndetails,\nfigcaption,\nfigure,\nfooter,\nheader,\nhgroup,\nmain,\nmenu,\nnav,\nsection,\nsummary {\n display: block;\n}\n\n//\n// 1. Correct `inline-block` display not defined in IE 8/9.\n// 2. Normalize vertical alignment of `progress` in Chrome, Firefox, and Opera.\n//\n\naudio,\ncanvas,\nprogress,\nvideo {\n display: inline-block; // 1\n vertical-align: baseline; // 2\n}\n\n//\n// Prevent modern browsers from displaying `audio` without controls.\n// Remove excess height in iOS 5 devices.\n//\n\naudio:not([controls]) {\n display: none;\n height: 0;\n}\n\n//\n// Address `[hidden]` styling not present in IE 8/9/10.\n// Hide the `template` element in IE 8/9/10/11, Safari, and Firefox < 22.\n//\n\n[hidden],\ntemplate {\n display: none;\n}\n\n// Links\n// ==========================================================================\n\n//\n// Remove the gray background color from active links in IE 10.\n//\n\na {\n background-color: transparent;\n}\n\n//\n// Improve readability of focused elements when they are also in an\n// active/hover state.\n//\n\na:active,\na:hover {\n outline: 0;\n}\n\n// Text-level semantics\n// ==========================================================================\n\n//\n// Address styling not present in IE 8/9/10/11, Safari, and Chrome.\n//\n\nabbr[title] {\n border-bottom: 1px dotted;\n}\n\n//\n// Address style set to `bolder` in Firefox 4+, Safari, and Chrome.\n//\n\nb,\nstrong {\n font-weight: bold;\n}\n\n//\n// Address styling not present in Safari and Chrome.\n//\n\ndfn {\n font-style: italic;\n}\n\n//\n// Address variable `h1` font-size and margin within `section` and `article`\n// contexts in Firefox 4+, Safari, and Chrome.\n//\n\nh1 {\n font-size: 2em;\n margin: 0.67em 0;\n}\n\n//\n// Address styling not present in IE 8/9.\n//\n\nmark {\n background: #ff0;\n color: #000;\n}\n\n//\n// Address inconsistent and variable font size in all browsers.\n//\n\nsmall {\n font-size: 80%;\n}\n\n//\n// Prevent `sub` and `sup` affecting `line-height` in all browsers.\n//\n\nsub,\nsup {\n font-size: 75%;\n line-height: 0;\n position: relative;\n vertical-align: baseline;\n}\n\nsup {\n top: -0.5em;\n}\n\nsub {\n bottom: -0.25em;\n}\n\n// Embedded content\n// ==========================================================================\n\n//\n// Remove border when inside `a` element in IE 8/9/10.\n//\n\nimg {\n border: 0;\n}\n\n//\n// Correct overflow not hidden in IE 9/10/11.\n//\n\nsvg:not(:root) {\n overflow: hidden;\n}\n\n// Grouping content\n// ==========================================================================\n\n//\n// Address margin not present in IE 8/9 and Safari.\n//\n\nfigure {\n margin: 1em 40px;\n}\n\n//\n// Address differences between Firefox and other browsers.\n//\n\nhr {\n box-sizing: content-box;\n height: 0;\n}\n\n//\n// Contain overflow in all browsers.\n//\n\npre {\n overflow: auto;\n}\n\n//\n// Address odd `em`-unit font size rendering in all browsers.\n//\n\ncode,\nkbd,\npre,\nsamp {\n font-family: monospace, monospace;\n font-size: 1em;\n}\n\n// Forms\n// ==========================================================================\n\n//\n// Known limitation: by default, Chrome and Safari on OS X allow very limited\n// styling of `select`, unless a `border` property is set.\n//\n\n//\n// 1. Correct color not being inherited.\n// Known issue: affects color of disabled elements.\n// 2. Correct font properties not being inherited.\n// 3. Address margins set differently in Firefox 4+, Safari, and Chrome.\n//\n\nbutton,\ninput,\noptgroup,\nselect,\ntextarea {\n color: inherit; // 1\n font: inherit; // 2\n margin: 0; // 3\n}\n\n//\n// Address `overflow` set to `hidden` in IE 8/9/10/11.\n//\n\nbutton {\n overflow: visible;\n}\n\n//\n// Address inconsistent `text-transform` inheritance for `button` and `select`.\n// All other form control elements do not inherit `text-transform` values.\n// Correct `button` style inheritance in Firefox, IE 8/9/10/11, and Opera.\n// Correct `select` style inheritance in Firefox.\n//\n\nbutton,\nselect {\n text-transform: none;\n}\n\n//\n// 1. Avoid the WebKit bug in Android 4.0.* where (2) destroys native `audio`\n// and `video` controls.\n// 2. Correct inability to style clickable `input` types in iOS.\n// 3. Improve usability and consistency of cursor style between image-type\n// `input` and others.\n//\n\nbutton,\nhtml input[type=\"button\"], // 1\ninput[type=\"reset\"],\ninput[type=\"submit\"] {\n -webkit-appearance: button; // 2\n cursor: pointer; // 3\n}\n\n//\n// Re-set default cursor for disabled elements.\n//\n\nbutton[disabled],\nhtml input[disabled] {\n cursor: default;\n}\n\n//\n// Remove inner padding and border in Firefox 4+.\n//\n\nbutton::-moz-focus-inner,\ninput::-moz-focus-inner {\n border: 0;\n padding: 0;\n}\n\n//\n// Address Firefox 4+ setting `line-height` on `input` using `!important` in\n// the UA stylesheet.\n//\n\ninput {\n line-height: normal;\n}\n\n//\n// It's recommended that you don't attempt to style these elements.\n// Firefox's implementation doesn't respect box-sizing, padding, or width.\n//\n// 1. Address box sizing set to `content-box` in IE 8/9/10.\n// 2. Remove excess padding in IE 8/9/10.\n//\n\ninput[type=\"checkbox\"],\ninput[type=\"radio\"] {\n box-sizing: border-box; // 1\n padding: 0; // 2\n}\n\n//\n// Fix the cursor style for Chrome's increment/decrement buttons. For certain\n// `font-size` values of the `input`, it causes the cursor style of the\n// decrement button to change from `default` to `text`.\n//\n\ninput[type=\"number\"]::-webkit-inner-spin-button,\ninput[type=\"number\"]::-webkit-outer-spin-button {\n height: auto;\n}\n\n//\n// 1. Address `appearance` set to `searchfield` in Safari and Chrome.\n// 2. Address `box-sizing` set to `border-box` in Safari and Chrome.\n//\n\ninput[type=\"search\"] {\n -webkit-appearance: textfield; // 1\n box-sizing: content-box; //2\n}\n\n//\n// Remove inner padding and search cancel button in Safari and Chrome on OS X.\n// Safari (but not Chrome) clips the cancel button when the search input has\n// padding (and `textfield` appearance).\n//\n\ninput[type=\"search\"]::-webkit-search-cancel-button,\ninput[type=\"search\"]::-webkit-search-decoration {\n -webkit-appearance: none;\n}\n\n//\n// Define consistent border, margin, and padding.\n//\n\nfieldset {\n border: 1px solid #c0c0c0;\n margin: 0 2px;\n padding: 0.35em 0.625em 0.75em;\n}\n\n//\n// 1. Correct `color` not being inherited in IE 8/9/10/11.\n// 2. Remove padding so people aren't caught out if they zero out fieldsets.\n//\n\nlegend {\n border: 0; // 1\n padding: 0; // 2\n}\n\n//\n// Remove default vertical scrollbar in IE 8/9/10/11.\n//\n\ntextarea {\n overflow: auto;\n}\n\n//\n// Don't inherit the `font-weight` (applied by a rule above).\n// NOTE: the default cannot safely be changed in Chrome and Safari on OS X.\n//\n\noptgroup {\n font-weight: bold;\n}\n\n// Tables\n// ==========================================================================\n\n//\n// Remove most spacing between table cells.\n//\n\ntable {\n border-collapse: collapse;\n border-spacing: 0;\n}\n\ntd,\nth {\n padding: 0;\n}\n","/*! Source: https://github.com/h5bp/html5-boilerplate/blob/master/src/css/main.css */\n\n// ==========================================================================\n// Print styles.\n// Inlined to avoid the additional HTTP request: h5bp.com/r\n// ==========================================================================\n\n@media print {\n *,\n *:before,\n *:after {\n background: transparent !important;\n color: #000 !important; // Black prints faster: h5bp.com/s\n box-shadow: none !important;\n text-shadow: none !important;\n }\n\n a,\n a:visited {\n text-decoration: underline;\n }\n\n a[href]:after {\n content: \" (\" attr(href) \")\";\n }\n\n abbr[title]:after {\n content: \" (\" attr(title) \")\";\n }\n\n // Don't show links that are fragment identifiers,\n // or use the `javascript:` pseudo protocol\n a[href^=\"#\"]:after,\n a[href^=\"javascript:\"]:after {\n content: \"\";\n }\n\n pre,\n blockquote {\n border: 1px solid #999;\n page-break-inside: avoid;\n }\n\n thead {\n display: table-header-group; // h5bp.com/t\n }\n\n tr,\n img {\n page-break-inside: avoid;\n }\n\n img {\n max-width: 100% !important;\n }\n\n p,\n h2,\n h3 {\n orphans: 3;\n widows: 3;\n }\n\n h2,\n h3 {\n page-break-after: avoid;\n }\n\n // Bootstrap specific changes start\n\n // Bootstrap components\n .navbar {\n display: none;\n }\n .btn,\n .dropup > .btn {\n > .caret {\n border-top-color: #000 !important;\n }\n }\n .label {\n border: 1px solid #000;\n }\n\n .table {\n border-collapse: collapse !important;\n\n td,\n th {\n background-color: #fff !important;\n }\n }\n .table-bordered {\n th,\n td {\n border: 1px solid #ddd !important;\n }\n }\n\n // Bootstrap specific changes end\n}\n","//\n// Glyphicons for Bootstrap\n//\n// Since icons are fonts, they can be placed anywhere text is placed and are\n// thus automatically sized to match the surrounding child. To use, create an\n// inline element with the appropriate classes, like so:\n//\n// Star\n\n// Import the fonts\n@font-face {\n font-family: 'Glyphicons Halflings';\n src: url('@{icon-font-path}@{icon-font-name}.eot');\n src: url('@{icon-font-path}@{icon-font-name}.eot?#iefix') format('embedded-opentype'),\n url('@{icon-font-path}@{icon-font-name}.woff2') format('woff2'),\n url('@{icon-font-path}@{icon-font-name}.woff') format('woff'),\n url('@{icon-font-path}@{icon-font-name}.ttf') format('truetype'),\n url('@{icon-font-path}@{icon-font-name}.svg#@{icon-font-svg-id}') format('svg');\n}\n\n// Catchall baseclass\n.glyphicon {\n position: relative;\n top: 1px;\n display: inline-block;\n font-family: 'Glyphicons Halflings';\n font-style: normal;\n font-weight: normal;\n line-height: 1;\n -webkit-font-smoothing: antialiased;\n -moz-osx-font-smoothing: grayscale;\n}\n\n// Individual icons\n.glyphicon-asterisk { &:before { content: \"\\2a\"; } }\n.glyphicon-plus { &:before { content: \"\\2b\"; } }\n.glyphicon-euro,\n.glyphicon-eur { &:before { content: \"\\20ac\"; } }\n.glyphicon-minus { &:before { content: \"\\2212\"; } }\n.glyphicon-cloud { &:before { content: \"\\2601\"; } }\n.glyphicon-envelope { &:before { content: \"\\2709\"; } }\n.glyphicon-pencil { &:before { content: \"\\270f\"; } }\n.glyphicon-glass { &:before { content: \"\\e001\"; } }\n.glyphicon-music { &:before { content: \"\\e002\"; } }\n.glyphicon-search { &:before { content: \"\\e003\"; } }\n.glyphicon-heart { &:before { content: \"\\e005\"; } }\n.glyphicon-star { &:before { content: \"\\e006\"; } }\n.glyphicon-star-empty { &:before { content: \"\\e007\"; } }\n.glyphicon-user { &:before { content: \"\\e008\"; } }\n.glyphicon-film { &:before { content: \"\\e009\"; } }\n.glyphicon-th-large { &:before { content: \"\\e010\"; } }\n.glyphicon-th { &:before { content: \"\\e011\"; } }\n.glyphicon-th-list { &:before { content: \"\\e012\"; } }\n.glyphicon-ok { &:before { content: \"\\e013\"; } }\n.glyphicon-remove { &:before { content: \"\\e014\"; } }\n.glyphicon-zoom-in { &:before { content: \"\\e015\"; } }\n.glyphicon-zoom-out { &:before { content: \"\\e016\"; } }\n.glyphicon-off { &:before { content: \"\\e017\"; } }\n.glyphicon-signal { &:before { content: \"\\e018\"; } }\n.glyphicon-cog { &:before { content: \"\\e019\"; } }\n.glyphicon-trash { &:before { content: \"\\e020\"; } }\n.glyphicon-home { &:before { content: \"\\e021\"; } }\n.glyphicon-file { &:before { content: \"\\e022\"; } }\n.glyphicon-time { &:before { content: \"\\e023\"; } }\n.glyphicon-road { &:before { content: \"\\e024\"; } }\n.glyphicon-download-alt { &:before { content: \"\\e025\"; } }\n.glyphicon-download { &:before { content: \"\\e026\"; } }\n.glyphicon-upload { &:before { content: \"\\e027\"; } }\n.glyphicon-inbox { &:before { content: \"\\e028\"; } }\n.glyphicon-play-circle { &:before { content: \"\\e029\"; } }\n.glyphicon-repeat { &:before { content: \"\\e030\"; } }\n.glyphicon-refresh { &:before { content: \"\\e031\"; } }\n.glyphicon-list-alt { &:before { content: \"\\e032\"; } }\n.glyphicon-lock { &:before { content: \"\\e033\"; } }\n.glyphicon-flag { &:before { content: \"\\e034\"; } }\n.glyphicon-headphones { &:before { content: \"\\e035\"; } }\n.glyphicon-volume-off { &:before { content: \"\\e036\"; } }\n.glyphicon-volume-down { &:before { content: \"\\e037\"; } }\n.glyphicon-volume-up { &:before { content: \"\\e038\"; } }\n.glyphicon-qrcode { &:before { content: \"\\e039\"; } }\n.glyphicon-barcode { &:before { content: \"\\e040\"; } }\n.glyphicon-tag { &:before { content: \"\\e041\"; } }\n.glyphicon-tags { &:before { content: \"\\e042\"; } }\n.glyphicon-book { &:before { content: \"\\e043\"; } }\n.glyphicon-bookmark { &:before { content: \"\\e044\"; } }\n.glyphicon-print { &:before { content: \"\\e045\"; } }\n.glyphicon-camera { &:before { content: \"\\e046\"; } }\n.glyphicon-font { &:before { content: \"\\e047\"; } }\n.glyphicon-bold { &:before { content: \"\\e048\"; } }\n.glyphicon-italic { &:before { content: \"\\e049\"; } }\n.glyphicon-text-height { &:before { content: \"\\e050\"; } }\n.glyphicon-text-width { &:before { content: \"\\e051\"; } }\n.glyphicon-align-left { &:before { content: \"\\e052\"; } }\n.glyphicon-align-center { &:before { content: \"\\e053\"; } }\n.glyphicon-align-right { &:before { content: \"\\e054\"; } }\n.glyphicon-align-justify { &:before { content: \"\\e055\"; } }\n.glyphicon-list { &:before { content: \"\\e056\"; } }\n.glyphicon-indent-left { &:before { content: \"\\e057\"; } }\n.glyphicon-indent-right { &:before { content: \"\\e058\"; } }\n.glyphicon-facetime-video { &:before { content: \"\\e059\"; } }\n.glyphicon-picture { &:before { content: \"\\e060\"; } }\n.glyphicon-map-marker { &:before { content: \"\\e062\"; } }\n.glyphicon-adjust { &:before { content: \"\\e063\"; } }\n.glyphicon-tint { &:before { content: \"\\e064\"; } }\n.glyphicon-edit { &:before { content: \"\\e065\"; } }\n.glyphicon-share { &:before { content: \"\\e066\"; } }\n.glyphicon-check { &:before { content: \"\\e067\"; } }\n.glyphicon-move { &:before { content: \"\\e068\"; } }\n.glyphicon-step-backward { &:before { content: \"\\e069\"; } }\n.glyphicon-fast-backward { &:before { content: \"\\e070\"; } }\n.glyphicon-backward { &:before { content: \"\\e071\"; } }\n.glyphicon-play { &:before { content: \"\\e072\"; } }\n.glyphicon-pause { &:before { content: \"\\e073\"; } }\n.glyphicon-stop { &:before { content: \"\\e074\"; } }\n.glyphicon-forward { &:before { content: \"\\e075\"; } }\n.glyphicon-fast-forward { &:before { content: \"\\e076\"; } }\n.glyphicon-step-forward { &:before { content: \"\\e077\"; } }\n.glyphicon-eject { &:before { content: \"\\e078\"; } }\n.glyphicon-chevron-left { &:before { content: \"\\e079\"; } }\n.glyphicon-chevron-right { &:before { content: \"\\e080\"; } }\n.glyphicon-plus-sign { &:before { content: \"\\e081\"; } }\n.glyphicon-minus-sign { &:before { content: \"\\e082\"; } }\n.glyphicon-remove-sign { &:before { content: \"\\e083\"; } }\n.glyphicon-ok-sign { &:before { content: \"\\e084\"; } }\n.glyphicon-question-sign { &:before { content: \"\\e085\"; } }\n.glyphicon-info-sign { &:before { content: \"\\e086\"; } }\n.glyphicon-screenshot { &:before { content: \"\\e087\"; } }\n.glyphicon-remove-circle { &:before { content: \"\\e088\"; } }\n.glyphicon-ok-circle { &:before { content: \"\\e089\"; } }\n.glyphicon-ban-circle { &:before { content: \"\\e090\"; } }\n.glyphicon-arrow-left { &:before { content: \"\\e091\"; } }\n.glyphicon-arrow-right { &:before { content: \"\\e092\"; } }\n.glyphicon-arrow-up { &:before { content: \"\\e093\"; } }\n.glyphicon-arrow-down { &:before { content: \"\\e094\"; } }\n.glyphicon-share-alt { &:before { content: \"\\e095\"; } }\n.glyphicon-resize-full { &:before { content: \"\\e096\"; } }\n.glyphicon-resize-small { &:before { content: \"\\e097\"; } }\n.glyphicon-exclamation-sign { &:before { content: \"\\e101\"; } }\n.glyphicon-gift { &:before { content: \"\\e102\"; } }\n.glyphicon-leaf { &:before { content: \"\\e103\"; } }\n.glyphicon-fire { &:before { content: \"\\e104\"; } }\n.glyphicon-eye-open { &:before { content: \"\\e105\"; } }\n.glyphicon-eye-close { &:before { content: \"\\e106\"; } }\n.glyphicon-warning-sign { &:before { content: \"\\e107\"; } }\n.glyphicon-plane { &:before { content: \"\\e108\"; } }\n.glyphicon-calendar { &:before { content: \"\\e109\"; } }\n.glyphicon-random { &:before { content: \"\\e110\"; } }\n.glyphicon-comment { &:before { content: \"\\e111\"; } }\n.glyphicon-magnet { &:before { content: \"\\e112\"; } }\n.glyphicon-chevron-up { &:before { content: \"\\e113\"; } }\n.glyphicon-chevron-down { &:before { content: \"\\e114\"; } }\n.glyphicon-retweet { &:before { content: \"\\e115\"; } }\n.glyphicon-shopping-cart { &:before { content: \"\\e116\"; } }\n.glyphicon-folder-close { &:before { content: \"\\e117\"; } }\n.glyphicon-folder-open { &:before { content: \"\\e118\"; } }\n.glyphicon-resize-vertical { &:before { content: \"\\e119\"; } }\n.glyphicon-resize-horizontal { &:before { content: \"\\e120\"; } }\n.glyphicon-hdd { &:before { content: \"\\e121\"; } }\n.glyphicon-bullhorn { &:before { content: \"\\e122\"; } }\n.glyphicon-bell { &:before { content: \"\\e123\"; } }\n.glyphicon-certificate { &:before { content: \"\\e124\"; } }\n.glyphicon-thumbs-up { &:before { content: \"\\e125\"; } }\n.glyphicon-thumbs-down { &:before { content: \"\\e126\"; } }\n.glyphicon-hand-right { &:before { content: \"\\e127\"; } }\n.glyphicon-hand-left { &:before { content: \"\\e128\"; } }\n.glyphicon-hand-up { &:before { content: \"\\e129\"; } }\n.glyphicon-hand-down { &:before { content: \"\\e130\"; } }\n.glyphicon-circle-arrow-right { &:before { content: \"\\e131\"; } }\n.glyphicon-circle-arrow-left { &:before { content: \"\\e132\"; } }\n.glyphicon-circle-arrow-up { &:before { content: \"\\e133\"; } }\n.glyphicon-circle-arrow-down { &:before { content: \"\\e134\"; } }\n.glyphicon-globe { &:before { content: \"\\e135\"; } }\n.glyphicon-wrench { &:before { content: \"\\e136\"; } }\n.glyphicon-tasks { &:before { content: \"\\e137\"; } }\n.glyphicon-filter { &:before { content: \"\\e138\"; } }\n.glyphicon-briefcase { &:before { content: \"\\e139\"; } }\n.glyphicon-fullscreen { &:before { content: \"\\e140\"; } }\n.glyphicon-dashboard { &:before { content: \"\\e141\"; } }\n.glyphicon-paperclip { &:before { content: \"\\e142\"; } }\n.glyphicon-heart-empty { &:before { content: \"\\e143\"; } }\n.glyphicon-link { &:before { content: \"\\e144\"; } }\n.glyphicon-phone { &:before { content: \"\\e145\"; } }\n.glyphicon-pushpin { &:before { content: \"\\e146\"; } }\n.glyphicon-usd { &:before { content: \"\\e148\"; } }\n.glyphicon-gbp { &:before { content: \"\\e149\"; } }\n.glyphicon-sort { &:before { content: \"\\e150\"; } }\n.glyphicon-sort-by-alphabet { &:before { content: \"\\e151\"; } }\n.glyphicon-sort-by-alphabet-alt { &:before { content: \"\\e152\"; } }\n.glyphicon-sort-by-order { &:before { content: \"\\e153\"; } }\n.glyphicon-sort-by-order-alt { &:before { content: \"\\e154\"; } }\n.glyphicon-sort-by-attributes { &:before { content: \"\\e155\"; } }\n.glyphicon-sort-by-attributes-alt { &:before { content: \"\\e156\"; } }\n.glyphicon-unchecked { &:before { content: \"\\e157\"; } }\n.glyphicon-expand { &:before { content: \"\\e158\"; } }\n.glyphicon-collapse-down { &:before { content: \"\\e159\"; } }\n.glyphicon-collapse-up { &:before { content: \"\\e160\"; } }\n.glyphicon-log-in { &:before { content: \"\\e161\"; } }\n.glyphicon-flash { &:before { content: \"\\e162\"; } }\n.glyphicon-log-out { &:before { content: \"\\e163\"; } }\n.glyphicon-new-window { &:before { content: \"\\e164\"; } }\n.glyphicon-record { &:before { content: \"\\e165\"; } }\n.glyphicon-save { &:before { content: \"\\e166\"; } }\n.glyphicon-open { &:before { content: \"\\e167\"; } }\n.glyphicon-saved { &:before { content: \"\\e168\"; } }\n.glyphicon-import { &:before { content: \"\\e169\"; } }\n.glyphicon-export { &:before { content: \"\\e170\"; } }\n.glyphicon-send { &:before { content: \"\\e171\"; } }\n.glyphicon-floppy-disk { &:before { content: \"\\e172\"; } }\n.glyphicon-floppy-saved { &:before { content: \"\\e173\"; } }\n.glyphicon-floppy-remove { &:before { content: \"\\e174\"; } }\n.glyphicon-floppy-save { &:before { content: \"\\e175\"; } }\n.glyphicon-floppy-open { &:before { content: \"\\e176\"; } }\n.glyphicon-credit-card { &:before { content: \"\\e177\"; } }\n.glyphicon-transfer { &:before { content: \"\\e178\"; } }\n.glyphicon-cutlery { &:before { content: \"\\e179\"; } }\n.glyphicon-header { &:before { content: \"\\e180\"; } }\n.glyphicon-compressed { &:before { content: \"\\e181\"; } }\n.glyphicon-earphone { &:before { content: \"\\e182\"; } }\n.glyphicon-phone-alt { &:before { content: \"\\e183\"; } }\n.glyphicon-tower { &:before { content: \"\\e184\"; } }\n.glyphicon-stats { &:before { content: \"\\e185\"; } }\n.glyphicon-sd-video { &:before { content: \"\\e186\"; } }\n.glyphicon-hd-video { &:before { content: \"\\e187\"; } }\n.glyphicon-subtitles { &:before { content: \"\\e188\"; } }\n.glyphicon-sound-stereo { &:before { content: \"\\e189\"; } }\n.glyphicon-sound-dolby { &:before { content: \"\\e190\"; } }\n.glyphicon-sound-5-1 { &:before { content: \"\\e191\"; } }\n.glyphicon-sound-6-1 { &:before { content: \"\\e192\"; } }\n.glyphicon-sound-7-1 { &:before { content: \"\\e193\"; } }\n.glyphicon-copyright-mark { &:before { content: \"\\e194\"; } }\n.glyphicon-registration-mark { &:before { content: \"\\e195\"; } }\n.glyphicon-cloud-download { &:before { content: \"\\e197\"; } }\n.glyphicon-cloud-upload { &:before { content: \"\\e198\"; } }\n.glyphicon-tree-conifer { &:before { content: \"\\e199\"; } }\n.glyphicon-tree-deciduous { &:before { content: \"\\e200\"; } }\n.glyphicon-cd { &:before { content: \"\\e201\"; } }\n.glyphicon-save-file { &:before { content: \"\\e202\"; } }\n.glyphicon-open-file { &:before { content: \"\\e203\"; } }\n.glyphicon-level-up { &:before { content: \"\\e204\"; } }\n.glyphicon-copy { &:before { content: \"\\e205\"; } }\n.glyphicon-paste { &:before { content: \"\\e206\"; } }\n// The following 2 Glyphicons are omitted for the time being because\n// they currently use Unicode codepoints that are outside the\n// Basic Multilingual Plane (BMP). Older buggy versions of WebKit can't handle\n// non-BMP codepoints in CSS string escapes, and thus can't display these two icons.\n// Notably, the bug affects some older versions of the Android Browser.\n// More info: https://github.com/twbs/bootstrap/issues/10106\n// .glyphicon-door { &:before { content: \"\\1f6aa\"; } }\n// .glyphicon-key { &:before { content: \"\\1f511\"; } }\n.glyphicon-alert { &:before { content: \"\\e209\"; } }\n.glyphicon-equalizer { &:before { content: \"\\e210\"; } }\n.glyphicon-king { &:before { content: \"\\e211\"; } }\n.glyphicon-queen { &:before { content: \"\\e212\"; } }\n.glyphicon-pawn { &:before { content: \"\\e213\"; } }\n.glyphicon-bishop { &:before { content: \"\\e214\"; } }\n.glyphicon-knight { &:before { content: \"\\e215\"; } }\n.glyphicon-baby-formula { &:before { content: \"\\e216\"; } }\n.glyphicon-tent { &:before { content: \"\\26fa\"; } }\n.glyphicon-blackboard { &:before { content: \"\\e218\"; } }\n.glyphicon-bed { &:before { content: \"\\e219\"; } }\n.glyphicon-apple { &:before { content: \"\\f8ff\"; } }\n.glyphicon-erase { &:before { content: \"\\e221\"; } }\n.glyphicon-hourglass { &:before { content: \"\\231b\"; } }\n.glyphicon-lamp { &:before { content: \"\\e223\"; } }\n.glyphicon-duplicate { &:before { content: \"\\e224\"; } }\n.glyphicon-piggy-bank { &:before { content: \"\\e225\"; } }\n.glyphicon-scissors { &:before { content: \"\\e226\"; } }\n.glyphicon-bitcoin { &:before { content: \"\\e227\"; } }\n.glyphicon-btc { &:before { content: \"\\e227\"; } }\n.glyphicon-xbt { &:before { content: \"\\e227\"; } }\n.glyphicon-yen { &:before { content: \"\\00a5\"; } }\n.glyphicon-jpy { &:before { content: \"\\00a5\"; } }\n.glyphicon-ruble { &:before { content: \"\\20bd\"; } }\n.glyphicon-rub { &:before { content: \"\\20bd\"; } }\n.glyphicon-scale { &:before { content: \"\\e230\"; } }\n.glyphicon-ice-lolly { &:before { content: \"\\e231\"; } }\n.glyphicon-ice-lolly-tasted { &:before { content: \"\\e232\"; } }\n.glyphicon-education { &:before { content: \"\\e233\"; } }\n.glyphicon-option-horizontal { &:before { content: \"\\e234\"; } }\n.glyphicon-option-vertical { &:before { content: \"\\e235\"; } }\n.glyphicon-menu-hamburger { &:before { content: \"\\e236\"; } }\n.glyphicon-modal-window { &:before { content: \"\\e237\"; } }\n.glyphicon-oil { &:before { content: \"\\e238\"; } }\n.glyphicon-grain { &:before { content: \"\\e239\"; } }\n.glyphicon-sunglasses { &:before { content: \"\\e240\"; } }\n.glyphicon-text-size { &:before { content: \"\\e241\"; } }\n.glyphicon-text-color { &:before { content: \"\\e242\"; } }\n.glyphicon-text-background { &:before { content: \"\\e243\"; } }\n.glyphicon-object-align-top { &:before { content: \"\\e244\"; } }\n.glyphicon-object-align-bottom { &:before { content: \"\\e245\"; } }\n.glyphicon-object-align-horizontal{ &:before { content: \"\\e246\"; } }\n.glyphicon-object-align-left { &:before { content: \"\\e247\"; } }\n.glyphicon-object-align-vertical { &:before { content: \"\\e248\"; } }\n.glyphicon-object-align-right { &:before { content: \"\\e249\"; } }\n.glyphicon-triangle-right { &:before { content: \"\\e250\"; } }\n.glyphicon-triangle-left { &:before { content: \"\\e251\"; } }\n.glyphicon-triangle-bottom { &:before { content: \"\\e252\"; } }\n.glyphicon-triangle-top { &:before { content: \"\\e253\"; } }\n.glyphicon-console { &:before { content: \"\\e254\"; } }\n.glyphicon-superscript { &:before { content: \"\\e255\"; } }\n.glyphicon-subscript { &:before { content: \"\\e256\"; } }\n.glyphicon-menu-left { &:before { content: \"\\e257\"; } }\n.glyphicon-menu-right { &:before { content: \"\\e258\"; } }\n.glyphicon-menu-down { &:before { content: \"\\e259\"; } }\n.glyphicon-menu-up { &:before { content: \"\\e260\"; } }\n","//\n// Scaffolding\n// --------------------------------------------------\n\n\n// Reset the box-sizing\n//\n// Heads up! This reset may cause conflicts with some third-party widgets.\n// For recommendations on resolving such conflicts, see\n// http://getbootstrap.com/getting-started/#third-box-sizing\n* {\n .box-sizing(border-box);\n}\n*:before,\n*:after {\n .box-sizing(border-box);\n}\n\n\n// Body reset\n\nhtml {\n font-size: 10px;\n -webkit-tap-highlight-color: rgba(0,0,0,0);\n}\n\nbody {\n font-family: @font-family-base;\n font-size: @font-size-base;\n line-height: @line-height-base;\n color: @text-color;\n background-color: @body-bg;\n}\n\n// Reset fonts for relevant elements\ninput,\nbutton,\nselect,\ntextarea {\n font-family: inherit;\n font-size: inherit;\n line-height: inherit;\n}\n\n\n// Links\n\na {\n color: @link-color;\n text-decoration: none;\n\n &:hover,\n &:focus {\n color: @link-hover-color;\n text-decoration: @link-hover-decoration;\n }\n\n &:focus {\n .tab-focus();\n }\n}\n\n\n// Figures\n//\n// We reset this here because previously Normalize had no `figure` margins. This\n// ensures we don't break anyone's use of the element.\n\nfigure {\n margin: 0;\n}\n\n\n// Images\n\nimg {\n vertical-align: middle;\n}\n\n// Responsive images (ensure images don't scale beyond their parents)\n.img-responsive {\n .img-responsive();\n}\n\n// Rounded corners\n.img-rounded {\n border-radius: @border-radius-large;\n}\n\n// Image thumbnails\n//\n// Heads up! This is mixin-ed into thumbnails.less for `.thumbnail`.\n.img-thumbnail {\n padding: @thumbnail-padding;\n line-height: @line-height-base;\n background-color: @thumbnail-bg;\n border: 1px solid @thumbnail-border;\n border-radius: @thumbnail-border-radius;\n .transition(all .2s ease-in-out);\n\n // Keep them at most 100% wide\n .img-responsive(inline-block);\n}\n\n// Perfect circle\n.img-circle {\n border-radius: 50%; // set radius in percents\n}\n\n\n// Horizontal rules\n\nhr {\n margin-top: @line-height-computed;\n margin-bottom: @line-height-computed;\n border: 0;\n border-top: 1px solid @hr-border;\n}\n\n\n// Only display content to screen readers\n//\n// See: http://a11yproject.com/posts/how-to-hide-content/\n\n.sr-only {\n position: absolute;\n width: 1px;\n height: 1px;\n margin: -1px;\n padding: 0;\n overflow: hidden;\n clip: rect(0,0,0,0);\n border: 0;\n}\n\n// Use in conjunction with .sr-only to only display content when it's focused.\n// Useful for \"Skip to main content\" links; see http://www.w3.org/TR/2013/NOTE-WCAG20-TECHS-20130905/G1\n// Credit: HTML5 Boilerplate\n\n.sr-only-focusable {\n &:active,\n &:focus {\n position: static;\n width: auto;\n height: auto;\n margin: 0;\n overflow: visible;\n clip: auto;\n }\n}\n\n\n// iOS \"clickable elements\" fix for role=\"button\"\n//\n// Fixes \"clickability\" issue (and more generally, the firing of events such as focus as well)\n// for traditionally non-focusable elements with role=\"button\"\n// see https://developer.mozilla.org/en-US/docs/Web/Events/click#Safari_Mobile\n\n[role=\"button\"] {\n cursor: pointer;\n}\n","// Vendor Prefixes\n//\n// All vendor mixins are deprecated as of v3.2.0 due to the introduction of\n// Autoprefixer in our Gruntfile. They will be removed in v4.\n\n// - Animations\n// - Backface visibility\n// - Box shadow\n// - Box sizing\n// - Content columns\n// - Hyphens\n// - Placeholder text\n// - Transformations\n// - Transitions\n// - User Select\n\n\n// Animations\n.animation(@animation) {\n -webkit-animation: @animation;\n -o-animation: @animation;\n animation: @animation;\n}\n.animation-name(@name) {\n -webkit-animation-name: @name;\n animation-name: @name;\n}\n.animation-duration(@duration) {\n -webkit-animation-duration: @duration;\n animation-duration: @duration;\n}\n.animation-timing-function(@timing-function) {\n -webkit-animation-timing-function: @timing-function;\n animation-timing-function: @timing-function;\n}\n.animation-delay(@delay) {\n -webkit-animation-delay: @delay;\n animation-delay: @delay;\n}\n.animation-iteration-count(@iteration-count) {\n -webkit-animation-iteration-count: @iteration-count;\n animation-iteration-count: @iteration-count;\n}\n.animation-direction(@direction) {\n -webkit-animation-direction: @direction;\n animation-direction: @direction;\n}\n.animation-fill-mode(@fill-mode) {\n -webkit-animation-fill-mode: @fill-mode;\n animation-fill-mode: @fill-mode;\n}\n\n// Backface visibility\n// Prevent browsers from flickering when using CSS 3D transforms.\n// Default value is `visible`, but can be changed to `hidden`\n\n.backface-visibility(@visibility){\n -webkit-backface-visibility: @visibility;\n -moz-backface-visibility: @visibility;\n backface-visibility: @visibility;\n}\n\n// Drop shadows\n//\n// Note: Deprecated `.box-shadow()` as of v3.1.0 since all of Bootstrap's\n// supported browsers that have box shadow capabilities now support it.\n\n.box-shadow(@shadow) {\n -webkit-box-shadow: @shadow; // iOS <4.3 & Android <4.1\n box-shadow: @shadow;\n}\n\n// Box sizing\n.box-sizing(@boxmodel) {\n -webkit-box-sizing: @boxmodel;\n -moz-box-sizing: @boxmodel;\n box-sizing: @boxmodel;\n}\n\n// CSS3 Content Columns\n.content-columns(@column-count; @column-gap: @grid-gutter-width) {\n -webkit-column-count: @column-count;\n -moz-column-count: @column-count;\n column-count: @column-count;\n -webkit-column-gap: @column-gap;\n -moz-column-gap: @column-gap;\n column-gap: @column-gap;\n}\n\n// Optional hyphenation\n.hyphens(@mode: auto) {\n word-wrap: break-word;\n -webkit-hyphens: @mode;\n -moz-hyphens: @mode;\n -ms-hyphens: @mode; // IE10+\n -o-hyphens: @mode;\n hyphens: @mode;\n}\n\n// Placeholder text\n.placeholder(@color: @input-color-placeholder) {\n // Firefox\n &::-moz-placeholder {\n color: @color;\n opacity: 1; // Override Firefox's unusual default opacity; see https://github.com/twbs/bootstrap/pull/11526\n }\n &:-ms-input-placeholder { color: @color; } // Internet Explorer 10+\n &::-webkit-input-placeholder { color: @color; } // Safari and Chrome\n}\n\n// Transformations\n.scale(@ratio) {\n -webkit-transform: scale(@ratio);\n -ms-transform: scale(@ratio); // IE9 only\n -o-transform: scale(@ratio);\n transform: scale(@ratio);\n}\n.scale(@ratioX; @ratioY) {\n -webkit-transform: scale(@ratioX, @ratioY);\n -ms-transform: scale(@ratioX, @ratioY); // IE9 only\n -o-transform: scale(@ratioX, @ratioY);\n transform: scale(@ratioX, @ratioY);\n}\n.scaleX(@ratio) {\n -webkit-transform: scaleX(@ratio);\n -ms-transform: scaleX(@ratio); // IE9 only\n -o-transform: scaleX(@ratio);\n transform: scaleX(@ratio);\n}\n.scaleY(@ratio) {\n -webkit-transform: scaleY(@ratio);\n -ms-transform: scaleY(@ratio); // IE9 only\n -o-transform: scaleY(@ratio);\n transform: scaleY(@ratio);\n}\n.skew(@x; @y) {\n -webkit-transform: skewX(@x) skewY(@y);\n -ms-transform: skewX(@x) skewY(@y); // See https://github.com/twbs/bootstrap/issues/4885; IE9+\n -o-transform: skewX(@x) skewY(@y);\n transform: skewX(@x) skewY(@y);\n}\n.translate(@x; @y) {\n -webkit-transform: translate(@x, @y);\n -ms-transform: translate(@x, @y); // IE9 only\n -o-transform: translate(@x, @y);\n transform: translate(@x, @y);\n}\n.translate3d(@x; @y; @z) {\n -webkit-transform: translate3d(@x, @y, @z);\n transform: translate3d(@x, @y, @z);\n}\n.rotate(@degrees) {\n -webkit-transform: rotate(@degrees);\n -ms-transform: rotate(@degrees); // IE9 only\n -o-transform: rotate(@degrees);\n transform: rotate(@degrees);\n}\n.rotateX(@degrees) {\n -webkit-transform: rotateX(@degrees);\n -ms-transform: rotateX(@degrees); // IE9 only\n -o-transform: rotateX(@degrees);\n transform: rotateX(@degrees);\n}\n.rotateY(@degrees) {\n -webkit-transform: rotateY(@degrees);\n -ms-transform: rotateY(@degrees); // IE9 only\n -o-transform: rotateY(@degrees);\n transform: rotateY(@degrees);\n}\n.perspective(@perspective) {\n -webkit-perspective: @perspective;\n -moz-perspective: @perspective;\n perspective: @perspective;\n}\n.perspective-origin(@perspective) {\n -webkit-perspective-origin: @perspective;\n -moz-perspective-origin: @perspective;\n perspective-origin: @perspective;\n}\n.transform-origin(@origin) {\n -webkit-transform-origin: @origin;\n -moz-transform-origin: @origin;\n -ms-transform-origin: @origin; // IE9 only\n transform-origin: @origin;\n}\n\n\n// Transitions\n\n.transition(@transition) {\n -webkit-transition: @transition;\n -o-transition: @transition;\n transition: @transition;\n}\n.transition-property(@transition-property) {\n -webkit-transition-property: @transition-property;\n transition-property: @transition-property;\n}\n.transition-delay(@transition-delay) {\n -webkit-transition-delay: @transition-delay;\n transition-delay: @transition-delay;\n}\n.transition-duration(@transition-duration) {\n -webkit-transition-duration: @transition-duration;\n transition-duration: @transition-duration;\n}\n.transition-timing-function(@timing-function) {\n -webkit-transition-timing-function: @timing-function;\n transition-timing-function: @timing-function;\n}\n.transition-transform(@transition) {\n -webkit-transition: -webkit-transform @transition;\n -moz-transition: -moz-transform @transition;\n -o-transition: -o-transform @transition;\n transition: transform @transition;\n}\n\n\n// User select\n// For selecting text on the page\n\n.user-select(@select) {\n -webkit-user-select: @select;\n -moz-user-select: @select;\n -ms-user-select: @select; // IE10+\n user-select: @select;\n}\n","// WebKit-style focus\n\n.tab-focus() {\n // Default\n outline: thin dotted;\n // WebKit\n outline: 5px auto -webkit-focus-ring-color;\n outline-offset: -2px;\n}\n","// Image Mixins\n// - Responsive image\n// - Retina image\n\n\n// Responsive image\n//\n// Keep images from scaling beyond the width of their parents.\n.img-responsive(@display: block) {\n display: @display;\n max-width: 100%; // Part 1: Set a maximum relative to the parent\n height: auto; // Part 2: Scale the height according to the width, otherwise you get stretching\n}\n\n\n// Retina image\n//\n// Short retina mixin for setting background-image and -size. Note that the\n// spelling of `min--moz-device-pixel-ratio` is intentional.\n.img-retina(@file-1x; @file-2x; @width-1x; @height-1x) {\n background-image: url(\"@{file-1x}\");\n\n @media\n only screen and (-webkit-min-device-pixel-ratio: 2),\n only screen and ( min--moz-device-pixel-ratio: 2),\n only screen and ( -o-min-device-pixel-ratio: 2/1),\n only screen and ( min-device-pixel-ratio: 2),\n only screen and ( min-resolution: 192dpi),\n only screen and ( min-resolution: 2dppx) {\n background-image: url(\"@{file-2x}\");\n background-size: @width-1x @height-1x;\n }\n}\n","//\n// Typography\n// --------------------------------------------------\n\n\n// Headings\n// -------------------------\n\nh1, h2, h3, h4, h5, h6,\n.h1, .h2, .h3, .h4, .h5, .h6 {\n font-family: @headings-font-family;\n font-weight: @headings-font-weight;\n line-height: @headings-line-height;\n color: @headings-color;\n\n small,\n .small {\n font-weight: normal;\n line-height: 1;\n color: @headings-small-color;\n }\n}\n\nh1, .h1,\nh2, .h2,\nh3, .h3 {\n margin-top: @line-height-computed;\n margin-bottom: (@line-height-computed / 2);\n\n small,\n .small {\n font-size: 65%;\n }\n}\nh4, .h4,\nh5, .h5,\nh6, .h6 {\n margin-top: (@line-height-computed / 2);\n margin-bottom: (@line-height-computed / 2);\n\n small,\n .small {\n font-size: 75%;\n }\n}\n\nh1, .h1 { font-size: @font-size-h1; }\nh2, .h2 { font-size: @font-size-h2; }\nh3, .h3 { font-size: @font-size-h3; }\nh4, .h4 { font-size: @font-size-h4; }\nh5, .h5 { font-size: @font-size-h5; }\nh6, .h6 { font-size: @font-size-h6; }\n\n\n// Body text\n// -------------------------\n\np {\n margin: 0 0 (@line-height-computed / 2);\n}\n\n.lead {\n margin-bottom: @line-height-computed;\n font-size: floor((@font-size-base * 1.15));\n font-weight: 300;\n line-height: 1.4;\n\n @media (min-width: @screen-sm-min) {\n font-size: (@font-size-base * 1.5);\n }\n}\n\n\n// Emphasis & misc\n// -------------------------\n\n// Ex: (12px small font / 14px base font) * 100% = about 85%\nsmall,\n.small {\n font-size: floor((100% * @font-size-small / @font-size-base));\n}\n\nmark,\n.mark {\n background-color: @state-warning-bg;\n padding: .2em;\n}\n\n// Alignment\n.text-left { text-align: left; }\n.text-right { text-align: right; }\n.text-center { text-align: center; }\n.text-justify { text-align: justify; }\n.text-nowrap { white-space: nowrap; }\n\n// Transformation\n.text-lowercase { text-transform: lowercase; }\n.text-uppercase { text-transform: uppercase; }\n.text-capitalize { text-transform: capitalize; }\n\n// Contextual colors\n.text-muted {\n color: @text-muted;\n}\n.text-primary {\n .text-emphasis-variant(@brand-primary);\n}\n.text-success {\n .text-emphasis-variant(@state-success-text);\n}\n.text-info {\n .text-emphasis-variant(@state-info-text);\n}\n.text-warning {\n .text-emphasis-variant(@state-warning-text);\n}\n.text-danger {\n .text-emphasis-variant(@state-danger-text);\n}\n\n// Contextual backgrounds\n// For now we'll leave these alongside the text classes until v4 when we can\n// safely shift things around (per SemVer rules).\n.bg-primary {\n // Given the contrast here, this is the only class to have its color inverted\n // automatically.\n color: #fff;\n .bg-variant(@brand-primary);\n}\n.bg-success {\n .bg-variant(@state-success-bg);\n}\n.bg-info {\n .bg-variant(@state-info-bg);\n}\n.bg-warning {\n .bg-variant(@state-warning-bg);\n}\n.bg-danger {\n .bg-variant(@state-danger-bg);\n}\n\n\n// Page header\n// -------------------------\n\n.page-header {\n padding-bottom: ((@line-height-computed / 2) - 1);\n margin: (@line-height-computed * 2) 0 @line-height-computed;\n border-bottom: 1px solid @page-header-border-color;\n}\n\n\n// Lists\n// -------------------------\n\n// Unordered and Ordered lists\nul,\nol {\n margin-top: 0;\n margin-bottom: (@line-height-computed / 2);\n ul,\n ol {\n margin-bottom: 0;\n }\n}\n\n// List options\n\n// Unstyled keeps list items block level, just removes default browser padding and list-style\n.list-unstyled {\n padding-left: 0;\n list-style: none;\n}\n\n// Inline turns list items into inline-block\n.list-inline {\n .list-unstyled();\n margin-left: -5px;\n\n > li {\n display: inline-block;\n padding-left: 5px;\n padding-right: 5px;\n }\n}\n\n// Description Lists\ndl {\n margin-top: 0; // Remove browser default\n margin-bottom: @line-height-computed;\n}\ndt,\ndd {\n line-height: @line-height-base;\n}\ndt {\n font-weight: bold;\n}\ndd {\n margin-left: 0; // Undo browser default\n}\n\n// Horizontal description lists\n//\n// Defaults to being stacked without any of the below styles applied, until the\n// grid breakpoint is reached (default of ~768px).\n\n.dl-horizontal {\n dd {\n &:extend(.clearfix all); // Clear the floated `dt` if an empty `dd` is present\n }\n\n @media (min-width: @grid-float-breakpoint) {\n dt {\n float: left;\n width: (@dl-horizontal-offset - 20);\n clear: left;\n text-align: right;\n .text-overflow();\n }\n dd {\n margin-left: @dl-horizontal-offset;\n }\n }\n}\n\n\n// Misc\n// -------------------------\n\n// Abbreviations and acronyms\nabbr[title],\n// Add data-* attribute to help out our tooltip plugin, per https://github.com/twbs/bootstrap/issues/5257\nabbr[data-original-title] {\n cursor: help;\n border-bottom: 1px dotted @abbr-border-color;\n}\n.initialism {\n font-size: 90%;\n .text-uppercase();\n}\n\n// Blockquotes\nblockquote {\n padding: (@line-height-computed / 2) @line-height-computed;\n margin: 0 0 @line-height-computed;\n font-size: @blockquote-font-size;\n border-left: 5px solid @blockquote-border-color;\n\n p,\n ul,\n ol {\n &:last-child {\n margin-bottom: 0;\n }\n }\n\n // Note: Deprecated small and .small as of v3.1.0\n // Context: https://github.com/twbs/bootstrap/issues/11660\n footer,\n small,\n .small {\n display: block;\n font-size: 80%; // back to default font-size\n line-height: @line-height-base;\n color: @blockquote-small-color;\n\n &:before {\n content: '\\2014 \\00A0'; // em dash, nbsp\n }\n }\n}\n\n// Opposite alignment of blockquote\n//\n// Heads up: `blockquote.pull-right` has been deprecated as of v3.1.0.\n.blockquote-reverse,\nblockquote.pull-right {\n padding-right: 15px;\n padding-left: 0;\n border-right: 5px solid @blockquote-border-color;\n border-left: 0;\n text-align: right;\n\n // Account for citation\n footer,\n small,\n .small {\n &:before { content: ''; }\n &:after {\n content: '\\00A0 \\2014'; // nbsp, em dash\n }\n }\n}\n\n// Addresses\naddress {\n margin-bottom: @line-height-computed;\n font-style: normal;\n line-height: @line-height-base;\n}\n","// Typography\n\n.text-emphasis-variant(@color) {\n color: @color;\n a&:hover,\n a&:focus {\n color: darken(@color, 10%);\n }\n}\n","// Contextual backgrounds\n\n.bg-variant(@color) {\n background-color: @color;\n a&:hover,\n a&:focus {\n background-color: darken(@color, 10%);\n }\n}\n","// Text overflow\n// Requires inline-block or block for proper styling\n\n.text-overflow() {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n","//\n// Code (inline and block)\n// --------------------------------------------------\n\n\n// Inline and block code styles\ncode,\nkbd,\npre,\nsamp {\n font-family: @font-family-monospace;\n}\n\n// Inline code\ncode {\n padding: 2px 4px;\n font-size: 90%;\n color: @code-color;\n background-color: @code-bg;\n border-radius: @border-radius-base;\n}\n\n// User input typically entered via keyboard\nkbd {\n padding: 2px 4px;\n font-size: 90%;\n color: @kbd-color;\n background-color: @kbd-bg;\n border-radius: @border-radius-small;\n box-shadow: inset 0 -1px 0 rgba(0,0,0,.25);\n\n kbd {\n padding: 0;\n font-size: 100%;\n font-weight: bold;\n box-shadow: none;\n }\n}\n\n// Blocks of code\npre {\n display: block;\n padding: ((@line-height-computed - 1) / 2);\n margin: 0 0 (@line-height-computed / 2);\n font-size: (@font-size-base - 1); // 14px to 13px\n line-height: @line-height-base;\n word-break: break-all;\n word-wrap: break-word;\n color: @pre-color;\n background-color: @pre-bg;\n border: 1px solid @pre-border-color;\n border-radius: @border-radius-base;\n\n // Account for some code outputs that place code tags in pre tags\n code {\n padding: 0;\n font-size: inherit;\n color: inherit;\n white-space: pre-wrap;\n background-color: transparent;\n border-radius: 0;\n }\n}\n\n// Enable scrollable blocks of code\n.pre-scrollable {\n max-height: @pre-scrollable-max-height;\n overflow-y: scroll;\n}\n","//\n// Grid system\n// --------------------------------------------------\n\n\n// Container widths\n//\n// Set the container width, and override it for fixed navbars in media queries.\n\n.container {\n .container-fixed();\n\n @media (min-width: @screen-sm-min) {\n width: @container-sm;\n }\n @media (min-width: @screen-md-min) {\n width: @container-md;\n }\n @media (min-width: @screen-lg-min) {\n width: @container-lg;\n }\n}\n\n\n// Fluid container\n//\n// Utilizes the mixin meant for fixed width containers, but without any defined\n// width for fluid, full width layouts.\n\n.container-fluid {\n .container-fixed();\n}\n\n\n// Row\n//\n// Rows contain and clear the floats of your columns.\n\n.row {\n .make-row();\n}\n\n\n// Columns\n//\n// Common styles for small and large grid columns\n\n.make-grid-columns();\n\n\n// Extra small grid\n//\n// Columns, offsets, pushes, and pulls for extra small devices like\n// smartphones.\n\n.make-grid(xs);\n\n\n// Small grid\n//\n// Columns, offsets, pushes, and pulls for the small device range, from phones\n// to tablets.\n\n@media (min-width: @screen-sm-min) {\n .make-grid(sm);\n}\n\n\n// Medium grid\n//\n// Columns, offsets, pushes, and pulls for the desktop device range.\n\n@media (min-width: @screen-md-min) {\n .make-grid(md);\n}\n\n\n// Large grid\n//\n// Columns, offsets, pushes, and pulls for the large desktop device range.\n\n@media (min-width: @screen-lg-min) {\n .make-grid(lg);\n}\n","// Grid system\n//\n// Generate semantic grid columns with these mixins.\n\n// Centered container element\n.container-fixed(@gutter: @grid-gutter-width) {\n margin-right: auto;\n margin-left: auto;\n padding-left: (@gutter / 2);\n padding-right: (@gutter / 2);\n &:extend(.clearfix all);\n}\n\n// Creates a wrapper for a series of columns\n.make-row(@gutter: @grid-gutter-width) {\n margin-left: ceil((@gutter / -2));\n margin-right: floor((@gutter / -2));\n &:extend(.clearfix all);\n}\n\n// Generate the extra small columns\n.make-xs-column(@columns; @gutter: @grid-gutter-width) {\n position: relative;\n float: left;\n width: percentage((@columns / @grid-columns));\n min-height: 1px;\n padding-left: (@gutter / 2);\n padding-right: (@gutter / 2);\n}\n.make-xs-column-offset(@columns) {\n margin-left: percentage((@columns / @grid-columns));\n}\n.make-xs-column-push(@columns) {\n left: percentage((@columns / @grid-columns));\n}\n.make-xs-column-pull(@columns) {\n right: percentage((@columns / @grid-columns));\n}\n\n// Generate the small columns\n.make-sm-column(@columns; @gutter: @grid-gutter-width) {\n position: relative;\n min-height: 1px;\n padding-left: (@gutter / 2);\n padding-right: (@gutter / 2);\n\n @media (min-width: @screen-sm-min) {\n float: left;\n width: percentage((@columns / @grid-columns));\n }\n}\n.make-sm-column-offset(@columns) {\n @media (min-width: @screen-sm-min) {\n margin-left: percentage((@columns / @grid-columns));\n }\n}\n.make-sm-column-push(@columns) {\n @media (min-width: @screen-sm-min) {\n left: percentage((@columns / @grid-columns));\n }\n}\n.make-sm-column-pull(@columns) {\n @media (min-width: @screen-sm-min) {\n right: percentage((@columns / @grid-columns));\n }\n}\n\n// Generate the medium columns\n.make-md-column(@columns; @gutter: @grid-gutter-width) {\n position: relative;\n min-height: 1px;\n padding-left: (@gutter / 2);\n padding-right: (@gutter / 2);\n\n @media (min-width: @screen-md-min) {\n float: left;\n width: percentage((@columns / @grid-columns));\n }\n}\n.make-md-column-offset(@columns) {\n @media (min-width: @screen-md-min) {\n margin-left: percentage((@columns / @grid-columns));\n }\n}\n.make-md-column-push(@columns) {\n @media (min-width: @screen-md-min) {\n left: percentage((@columns / @grid-columns));\n }\n}\n.make-md-column-pull(@columns) {\n @media (min-width: @screen-md-min) {\n right: percentage((@columns / @grid-columns));\n }\n}\n\n// Generate the large columns\n.make-lg-column(@columns; @gutter: @grid-gutter-width) {\n position: relative;\n min-height: 1px;\n padding-left: (@gutter / 2);\n padding-right: (@gutter / 2);\n\n @media (min-width: @screen-lg-min) {\n float: left;\n width: percentage((@columns / @grid-columns));\n }\n}\n.make-lg-column-offset(@columns) {\n @media (min-width: @screen-lg-min) {\n margin-left: percentage((@columns / @grid-columns));\n }\n}\n.make-lg-column-push(@columns) {\n @media (min-width: @screen-lg-min) {\n left: percentage((@columns / @grid-columns));\n }\n}\n.make-lg-column-pull(@columns) {\n @media (min-width: @screen-lg-min) {\n right: percentage((@columns / @grid-columns));\n }\n}\n","// Framework grid generation\n//\n// Used only by Bootstrap to generate the correct number of grid classes given\n// any value of `@grid-columns`.\n\n.make-grid-columns() {\n // Common styles for all sizes of grid columns, widths 1-12\n .col(@index) { // initial\n @item: ~\".col-xs-@{index}, .col-sm-@{index}, .col-md-@{index}, .col-lg-@{index}\";\n .col((@index + 1), @item);\n }\n .col(@index, @list) when (@index =< @grid-columns) { // general; \"=<\" isn't a typo\n @item: ~\".col-xs-@{index}, .col-sm-@{index}, .col-md-@{index}, .col-lg-@{index}\";\n .col((@index + 1), ~\"@{list}, @{item}\");\n }\n .col(@index, @list) when (@index > @grid-columns) { // terminal\n @{list} {\n position: relative;\n // Prevent columns from collapsing when empty\n min-height: 1px;\n // Inner gutter via padding\n padding-left: ceil((@grid-gutter-width / 2));\n padding-right: floor((@grid-gutter-width / 2));\n }\n }\n .col(1); // kickstart it\n}\n\n.float-grid-columns(@class) {\n .col(@index) { // initial\n @item: ~\".col-@{class}-@{index}\";\n .col((@index + 1), @item);\n }\n .col(@index, @list) when (@index =< @grid-columns) { // general\n @item: ~\".col-@{class}-@{index}\";\n .col((@index + 1), ~\"@{list}, @{item}\");\n }\n .col(@index, @list) when (@index > @grid-columns) { // terminal\n @{list} {\n float: left;\n }\n }\n .col(1); // kickstart it\n}\n\n.calc-grid-column(@index, @class, @type) when (@type = width) and (@index > 0) {\n .col-@{class}-@{index} {\n width: percentage((@index / @grid-columns));\n }\n}\n.calc-grid-column(@index, @class, @type) when (@type = push) and (@index > 0) {\n .col-@{class}-push-@{index} {\n left: percentage((@index / @grid-columns));\n }\n}\n.calc-grid-column(@index, @class, @type) when (@type = push) and (@index = 0) {\n .col-@{class}-push-0 {\n left: auto;\n }\n}\n.calc-grid-column(@index, @class, @type) when (@type = pull) and (@index > 0) {\n .col-@{class}-pull-@{index} {\n right: percentage((@index / @grid-columns));\n }\n}\n.calc-grid-column(@index, @class, @type) when (@type = pull) and (@index = 0) {\n .col-@{class}-pull-0 {\n right: auto;\n }\n}\n.calc-grid-column(@index, @class, @type) when (@type = offset) {\n .col-@{class}-offset-@{index} {\n margin-left: percentage((@index / @grid-columns));\n }\n}\n\n// Basic looping in LESS\n.loop-grid-columns(@index, @class, @type) when (@index >= 0) {\n .calc-grid-column(@index, @class, @type);\n // next iteration\n .loop-grid-columns((@index - 1), @class, @type);\n}\n\n// Create grid for specific class\n.make-grid(@class) {\n .float-grid-columns(@class);\n .loop-grid-columns(@grid-columns, @class, width);\n .loop-grid-columns(@grid-columns, @class, pull);\n .loop-grid-columns(@grid-columns, @class, push);\n .loop-grid-columns(@grid-columns, @class, offset);\n}\n","//\n// Tables\n// --------------------------------------------------\n\n\ntable {\n background-color: @table-bg;\n}\ncaption {\n padding-top: @table-cell-padding;\n padding-bottom: @table-cell-padding;\n color: @text-muted;\n text-align: left;\n}\nth {\n text-align: left;\n}\n\n\n// Baseline styles\n\n.table {\n width: 100%;\n max-width: 100%;\n margin-bottom: @line-height-computed;\n // Cells\n > thead,\n > tbody,\n > tfoot {\n > tr {\n > th,\n > td {\n padding: @table-cell-padding;\n line-height: @line-height-base;\n vertical-align: top;\n border-top: 1px solid @table-border-color;\n }\n }\n }\n // Bottom align for column headings\n > thead > tr > th {\n vertical-align: bottom;\n border-bottom: 2px solid @table-border-color;\n }\n // Remove top border from thead by default\n > caption + thead,\n > colgroup + thead,\n > thead:first-child {\n > tr:first-child {\n > th,\n > td {\n border-top: 0;\n }\n }\n }\n // Account for multiple tbody instances\n > tbody + tbody {\n border-top: 2px solid @table-border-color;\n }\n\n // Nesting\n .table {\n background-color: @body-bg;\n }\n}\n\n\n// Condensed table w/ half padding\n\n.table-condensed {\n > thead,\n > tbody,\n > tfoot {\n > tr {\n > th,\n > td {\n padding: @table-condensed-cell-padding;\n }\n }\n }\n}\n\n\n// Bordered version\n//\n// Add borders all around the table and between all the columns.\n\n.table-bordered {\n border: 1px solid @table-border-color;\n > thead,\n > tbody,\n > tfoot {\n > tr {\n > th,\n > td {\n border: 1px solid @table-border-color;\n }\n }\n }\n > thead > tr {\n > th,\n > td {\n border-bottom-width: 2px;\n }\n }\n}\n\n\n// Zebra-striping\n//\n// Default zebra-stripe styles (alternating gray and transparent backgrounds)\n\n.table-striped {\n > tbody > tr:nth-of-type(odd) {\n background-color: @table-bg-accent;\n }\n}\n\n\n// Hover effect\n//\n// Placed here since it has to come after the potential zebra striping\n\n.table-hover {\n > tbody > tr:hover {\n background-color: @table-bg-hover;\n }\n}\n\n\n// Table cell sizing\n//\n// Reset default table behavior\n\ntable col[class*=\"col-\"] {\n position: static; // Prevent border hiding in Firefox and IE9-11 (see https://github.com/twbs/bootstrap/issues/11623)\n float: none;\n display: table-column;\n}\ntable {\n td,\n th {\n &[class*=\"col-\"] {\n position: static; // Prevent border hiding in Firefox and IE9-11 (see https://github.com/twbs/bootstrap/issues/11623)\n float: none;\n display: table-cell;\n }\n }\n}\n\n\n// Table backgrounds\n//\n// Exact selectors below required to override `.table-striped` and prevent\n// inheritance to nested tables.\n\n// Generate the contextual variants\n.table-row-variant(active; @table-bg-active);\n.table-row-variant(success; @state-success-bg);\n.table-row-variant(info; @state-info-bg);\n.table-row-variant(warning; @state-warning-bg);\n.table-row-variant(danger; @state-danger-bg);\n\n\n// Responsive tables\n//\n// Wrap your tables in `.table-responsive` and we'll make them mobile friendly\n// by enabling horizontal scrolling. Only applies <768px. Everything above that\n// will display normally.\n\n.table-responsive {\n overflow-x: auto;\n min-height: 0.01%; // Workaround for IE9 bug (see https://github.com/twbs/bootstrap/issues/14837)\n\n @media screen and (max-width: @screen-xs-max) {\n width: 100%;\n margin-bottom: (@line-height-computed * 0.75);\n overflow-y: hidden;\n -ms-overflow-style: -ms-autohiding-scrollbar;\n border: 1px solid @table-border-color;\n\n // Tighten up spacing\n > .table {\n margin-bottom: 0;\n\n // Ensure the content doesn't wrap\n > thead,\n > tbody,\n > tfoot {\n > tr {\n > th,\n > td {\n white-space: nowrap;\n }\n }\n }\n }\n\n // Special overrides for the bordered tables\n > .table-bordered {\n border: 0;\n\n // Nuke the appropriate borders so that the parent can handle them\n > thead,\n > tbody,\n > tfoot {\n > tr {\n > th:first-child,\n > td:first-child {\n border-left: 0;\n }\n > th:last-child,\n > td:last-child {\n border-right: 0;\n }\n }\n }\n\n // Only nuke the last row's bottom-border in `tbody` and `tfoot` since\n // chances are there will be only one `tr` in a `thead` and that would\n // remove the border altogether.\n > tbody,\n > tfoot {\n > tr:last-child {\n > th,\n > td {\n border-bottom: 0;\n }\n }\n }\n\n }\n }\n}\n","// Tables\n\n.table-row-variant(@state; @background) {\n // Exact selectors below required to override `.table-striped` and prevent\n // inheritance to nested tables.\n .table > thead > tr,\n .table > tbody > tr,\n .table > tfoot > tr {\n > td.@{state},\n > th.@{state},\n &.@{state} > td,\n &.@{state} > th {\n background-color: @background;\n }\n }\n\n // Hover states for `.table-hover`\n // Note: this is not available for cells or rows within `thead` or `tfoot`.\n .table-hover > tbody > tr {\n > td.@{state}:hover,\n > th.@{state}:hover,\n &.@{state}:hover > td,\n &:hover > .@{state},\n &.@{state}:hover > th {\n background-color: darken(@background, 5%);\n }\n }\n}\n","//\n// Forms\n// --------------------------------------------------\n\n\n// Normalize non-controls\n//\n// Restyle and baseline non-control form elements.\n\nfieldset {\n padding: 0;\n margin: 0;\n border: 0;\n // Chrome and Firefox set a `min-width: min-content;` on fieldsets,\n // so we reset that to ensure it behaves more like a standard block element.\n // See https://github.com/twbs/bootstrap/issues/12359.\n min-width: 0;\n}\n\nlegend {\n display: block;\n width: 100%;\n padding: 0;\n margin-bottom: @line-height-computed;\n font-size: (@font-size-base * 1.5);\n line-height: inherit;\n color: @legend-color;\n border: 0;\n border-bottom: 1px solid @legend-border-color;\n}\n\nlabel {\n display: inline-block;\n max-width: 100%; // Force IE8 to wrap long content (see https://github.com/twbs/bootstrap/issues/13141)\n margin-bottom: 5px;\n font-weight: bold;\n}\n\n\n// Normalize form controls\n//\n// While most of our form styles require extra classes, some basic normalization\n// is required to ensure optimum display with or without those classes to better\n// address browser inconsistencies.\n\n// Override content-box in Normalize (* isn't specific enough)\ninput[type=\"search\"] {\n .box-sizing(border-box);\n}\n\n// Position radios and checkboxes better\ninput[type=\"radio\"],\ninput[type=\"checkbox\"] {\n margin: 4px 0 0;\n margin-top: 1px \\9; // IE8-9\n line-height: normal;\n}\n\ninput[type=\"file\"] {\n display: block;\n}\n\n// Make range inputs behave like textual form controls\ninput[type=\"range\"] {\n display: block;\n width: 100%;\n}\n\n// Make multiple select elements height not fixed\nselect[multiple],\nselect[size] {\n height: auto;\n}\n\n// Focus for file, radio, and checkbox\ninput[type=\"file\"]:focus,\ninput[type=\"radio\"]:focus,\ninput[type=\"checkbox\"]:focus {\n .tab-focus();\n}\n\n// Adjust output element\noutput {\n display: block;\n padding-top: (@padding-base-vertical + 1);\n font-size: @font-size-base;\n line-height: @line-height-base;\n color: @input-color;\n}\n\n\n// Common form controls\n//\n// Shared size and type resets for form controls. Apply `.form-control` to any\n// of the following form controls:\n//\n// select\n// textarea\n// input[type=\"text\"]\n// input[type=\"password\"]\n// input[type=\"datetime\"]\n// input[type=\"datetime-local\"]\n// input[type=\"date\"]\n// input[type=\"month\"]\n// input[type=\"time\"]\n// input[type=\"week\"]\n// input[type=\"number\"]\n// input[type=\"email\"]\n// input[type=\"url\"]\n// input[type=\"search\"]\n// input[type=\"tel\"]\n// input[type=\"color\"]\n\n.form-control {\n display: block;\n width: 100%;\n height: @input-height-base; // Make inputs at least the height of their button counterpart (base line-height + padding + border)\n padding: @padding-base-vertical @padding-base-horizontal;\n font-size: @font-size-base;\n line-height: @line-height-base;\n color: @input-color;\n background-color: @input-bg;\n background-image: none; // Reset unusual Firefox-on-Android default style; see https://github.com/necolas/normalize.css/issues/214\n border: 1px solid @input-border;\n border-radius: @input-border-radius; // Note: This has no effect on s in CSS.\n .box-shadow(inset 0 1px 1px rgba(0,0,0,.075));\n .transition(~\"border-color ease-in-out .15s, box-shadow ease-in-out .15s\");\n\n // Customize the `:focus` state to imitate native WebKit styles.\n .form-control-focus();\n\n // Placeholder\n .placeholder();\n\n // Disabled and read-only inputs\n //\n // HTML5 says that controls under a fieldset > legend:first-child won't be\n // disabled if the fieldset is disabled. Due to implementation difficulty, we\n // don't honor that edge case; we style them as disabled anyway.\n &[disabled],\n &[readonly],\n fieldset[disabled] & {\n background-color: @input-bg-disabled;\n opacity: 1; // iOS fix for unreadable disabled content; see https://github.com/twbs/bootstrap/issues/11655\n }\n\n &[disabled],\n fieldset[disabled] & {\n cursor: @cursor-disabled;\n }\n\n // Reset height for `textarea`s\n textarea& {\n height: auto;\n }\n}\n\n\n// Search inputs in iOS\n//\n// This overrides the extra rounded corners on search inputs in iOS so that our\n// `.form-control` class can properly style them. Note that this cannot simply\n// be added to `.form-control` as it's not specific enough. For details, see\n// https://github.com/twbs/bootstrap/issues/11586.\n\ninput[type=\"search\"] {\n -webkit-appearance: none;\n}\n\n\n// Special styles for iOS temporal inputs\n//\n// In Mobile Safari, setting `display: block` on temporal inputs causes the\n// text within the input to become vertically misaligned. As a workaround, we\n// set a pixel line-height that matches the given height of the input, but only\n// for Safari. See https://bugs.webkit.org/show_bug.cgi?id=139848\n//\n// Note that as of 8.3, iOS doesn't support `datetime` or `week`.\n\n@media screen and (-webkit-min-device-pixel-ratio: 0) {\n input[type=\"date\"],\n input[type=\"time\"],\n input[type=\"datetime-local\"],\n input[type=\"month\"] {\n &.form-control {\n line-height: @input-height-base;\n }\n\n &.input-sm,\n .input-group-sm & {\n line-height: @input-height-small;\n }\n\n &.input-lg,\n .input-group-lg & {\n line-height: @input-height-large;\n }\n }\n}\n\n\n// Form groups\n//\n// Designed to help with the organization and spacing of vertical forms. For\n// horizontal forms, use the predefined grid classes.\n\n.form-group {\n margin-bottom: @form-group-margin-bottom;\n}\n\n\n// Checkboxes and radios\n//\n// Indent the labels to position radios/checkboxes as hanging controls.\n\n.radio,\n.checkbox {\n position: relative;\n display: block;\n margin-top: 10px;\n margin-bottom: 10px;\n\n label {\n min-height: @line-height-computed; // Ensure the input doesn't jump when there is no text\n padding-left: 20px;\n margin-bottom: 0;\n font-weight: normal;\n cursor: pointer;\n }\n}\n.radio input[type=\"radio\"],\n.radio-inline input[type=\"radio\"],\n.checkbox input[type=\"checkbox\"],\n.checkbox-inline input[type=\"checkbox\"] {\n position: absolute;\n margin-left: -20px;\n margin-top: 4px \\9;\n}\n\n.radio + .radio,\n.checkbox + .checkbox {\n margin-top: -5px; // Move up sibling radios or checkboxes for tighter spacing\n}\n\n// Radios and checkboxes on same line\n.radio-inline,\n.checkbox-inline {\n position: relative;\n display: inline-block;\n padding-left: 20px;\n margin-bottom: 0;\n vertical-align: middle;\n font-weight: normal;\n cursor: pointer;\n}\n.radio-inline + .radio-inline,\n.checkbox-inline + .checkbox-inline {\n margin-top: 0;\n margin-left: 10px; // space out consecutive inline controls\n}\n\n// Apply same disabled cursor tweak as for inputs\n// Some special care is needed because