添加项目文件。
This commit is contained in:
108
common/Tnb.Common/Net/IUserAgent.cs
Normal file
108
common/Tnb.Common/Net/IUserAgent.cs
Normal file
@@ -0,0 +1,108 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
namespace JNPF.Common.Net;
|
||||
|
||||
public interface IUserAgent
|
||||
{
|
||||
string RawValue { get; set; }
|
||||
|
||||
UserAgentInfo userAgent { get; }
|
||||
|
||||
DeviceInfo Device { get; }
|
||||
|
||||
OSInfo OS { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 是否移动端.
|
||||
/// </summary>
|
||||
bool IsMobileDevice { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 是否平板.
|
||||
/// </summary>
|
||||
bool IsTablet { get; }
|
||||
}
|
||||
|
||||
public sealed class DeviceInfo
|
||||
{
|
||||
public DeviceInfo(string family, bool isBot)
|
||||
{
|
||||
this.Family = family;
|
||||
this.IsBot = isBot;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return this.Family;
|
||||
}
|
||||
|
||||
public string Family { get; private set; }
|
||||
public bool IsBot { get; private set; }
|
||||
}
|
||||
|
||||
public sealed class OSInfo
|
||||
{
|
||||
public OSInfo(string family, string major, string minor, string patch, string patchMinor)
|
||||
{
|
||||
this.Family = family;
|
||||
this.Major = major;
|
||||
this.Minor = minor;
|
||||
this.Patch = patch;
|
||||
this.PatchMinor = patchMinor;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
var str = VersionString.Format(Major, Minor, Patch, PatchMinor);
|
||||
return (this.Family + (!string.IsNullOrEmpty(str) ? (" " + str) : null));
|
||||
}
|
||||
|
||||
public string Family { get; private set; }
|
||||
public string Major { get; private set; }
|
||||
public string Minor { get; private set; }
|
||||
public string Patch { get; private set; }
|
||||
public string PatchMinor { get; private set; }
|
||||
|
||||
private static string FormatVersionString(params string[] parts)
|
||||
{
|
||||
return string.Join(".", (from v in parts
|
||||
where !string.IsNullOrEmpty(v)
|
||||
select v).ToArray<string>());
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class UserAgentInfo
|
||||
{
|
||||
public UserAgentInfo(string family, string major, string minor, string patch)
|
||||
{
|
||||
this.Family = family;
|
||||
this.Major = major;
|
||||
this.Minor = minor;
|
||||
this.Patch = patch;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
var str = VersionString.Format(Major, Minor, Patch);
|
||||
return (this.Family + (!string.IsNullOrEmpty(str) ? (" " + str) : null));
|
||||
}
|
||||
|
||||
public string Family { get; private set; }
|
||||
|
||||
public string Major { get; private set; }
|
||||
|
||||
public string Minor { get; private set; }
|
||||
|
||||
public string Patch { get; private set; }
|
||||
}
|
||||
|
||||
internal static class VersionString
|
||||
{
|
||||
public static string Format(params string[] parts)
|
||||
{
|
||||
return string.Join(".", (from v in parts
|
||||
where !string.IsNullOrEmpty(v)
|
||||
select v).ToArray<string>());
|
||||
}
|
||||
}
|
||||
25
common/Tnb.Common/Net/IpLocation.cs
Normal file
25
common/Tnb.Common/Net/IpLocation.cs
Normal file
@@ -0,0 +1,25 @@
|
||||
using JNPF.DependencyInjection;
|
||||
|
||||
namespace JNPF.Common.Net;
|
||||
|
||||
/// <summary>
|
||||
/// IP位置信息类.
|
||||
/// </summary>
|
||||
[SuppressSniffer]
|
||||
public class IpLocation
|
||||
{
|
||||
/// <summary>
|
||||
/// IP地址.
|
||||
/// </summary>
|
||||
public string? Ip { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// IP地址所属国家.
|
||||
/// </summary>
|
||||
public string? Country { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 位置信息.
|
||||
/// </summary>
|
||||
public string? Local { get; set; }
|
||||
}
|
||||
296
common/Tnb.Common/Net/IpLocator.cs
Normal file
296
common/Tnb.Common/Net/IpLocator.cs
Normal file
@@ -0,0 +1,296 @@
|
||||
using System.Globalization;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using JNPF.DependencyInjection;
|
||||
|
||||
namespace JNPF.Common.Net
|
||||
{
|
||||
/// <summary>
|
||||
/// IP位置查找操作类.
|
||||
/// </summary>
|
||||
[SuppressSniffer]
|
||||
public class IpLocator
|
||||
{
|
||||
private readonly byte[] _data;
|
||||
private readonly long _firstStartIpOffset;
|
||||
private static readonly Regex Regex = new Regex(@"^(((\d{1,2})|(1\d{2})|(2[0-4]\d)|(25[0-5]))\.){3}((\d{1,2})|(1\d{2})|(2[0-4]\d)|(25[0-5]))$");
|
||||
|
||||
/// <summary>
|
||||
/// 初始化一个IP位置查找操作类的实例.
|
||||
/// </summary>
|
||||
/// <param name="dataPath"> IP信息数据文件路径.</param>
|
||||
/// <exception cref="ArgumentException"></exception>
|
||||
public IpLocator(string dataPath)
|
||||
{
|
||||
using (FileStream fs = new FileStream(dataPath, FileMode.Open, FileAccess.Read, FileShare.Read))
|
||||
{
|
||||
_data = new byte[fs.Length];
|
||||
fs.Read(_data, 0, _data.Length);
|
||||
}
|
||||
|
||||
byte[] buffer = new byte[8];
|
||||
Array.Copy(_data, 0, buffer, 0, 8);
|
||||
_firstStartIpOffset = ((buffer[0] + (buffer[1] * 0x100)) + ((buffer[2] * 0x100) * 0x100)) + (((buffer[3] * 0x100) * 0x100) * 0x100);
|
||||
long lastStartIpOffset = ((buffer[4] + (buffer[5] * 0x100)) + ((buffer[6] * 0x100) * 0x100)) + (((buffer[7] * 0x100) * 0x100) * 0x100);
|
||||
Count = Convert.ToInt64((lastStartIpOffset - _firstStartIpOffset) / 7.0);
|
||||
|
||||
if (Count <= 1L)
|
||||
throw new ArgumentException("IP信息数据文件异常。");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 数据文件中信息数量.
|
||||
/// </summary>
|
||||
public long Count { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// IP地址转化成整数.
|
||||
/// </summary>
|
||||
/// <param name="ip"></param>
|
||||
/// <returns></returns>
|
||||
public static long IpToInt(string ip)
|
||||
{
|
||||
char[] separator = new[] { '.' };
|
||||
if (ip.Split(separator).Length == 3)
|
||||
{
|
||||
ip = ip + ".0";
|
||||
}
|
||||
|
||||
if (!Regex.Match(ip).Success)
|
||||
throw new ArgumentException("IP格式错误");
|
||||
string[] nums = ip.Split(separator);
|
||||
long num1 = ((long.Parse(nums[0]) * 0x100L) * 0x100L) * 0x100L;
|
||||
long num2 = (long.Parse(nums[1]) * 0x100L) * 0x100L;
|
||||
long num3 = long.Parse(nums[2]) * 0x100L;
|
||||
long num4 = long.Parse(nums[3]);
|
||||
return ((num1 + num2) + num3) + num4;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// IP地址从整数类型转化为正常IP类型.
|
||||
/// </summary>
|
||||
/// <param name="ipInt"></param>
|
||||
/// <returns></returns>
|
||||
public static string IntToIp(long ipInt)
|
||||
{
|
||||
long num1 = (ipInt & 0xff000000L) >> 0x18;
|
||||
if (num1 < 0L)
|
||||
num1 += 0x100L;
|
||||
long num2 = (ipInt & 0xff0000L) >> 0x10;
|
||||
if (num2 < 0L)
|
||||
num2 += 0x100L;
|
||||
long num3 = (ipInt & 0xff00L) >> 8;
|
||||
if (num3 < 0L)
|
||||
num3 += 0x100L;
|
||||
long num4 = ipInt & 0xffL;
|
||||
if (num4 < 0L)
|
||||
num4 += 0x100L;
|
||||
string ip = string.Concat(new[]
|
||||
{
|
||||
num1.ToString(CultureInfo.InvariantCulture),
|
||||
".",
|
||||
num2.ToString(CultureInfo.InvariantCulture),
|
||||
".",
|
||||
num3.ToString(CultureInfo.InvariantCulture),
|
||||
".",
|
||||
num4.ToString(CultureInfo.InvariantCulture)
|
||||
});
|
||||
|
||||
if (!Regex.Match(ip).Success)
|
||||
throw new ArgumentException("IP格式错误");
|
||||
return ip;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 由IP地址查找对应的位置信息.
|
||||
/// </summary>
|
||||
/// <param name="ip"> 要查找的IP地址. </param>
|
||||
/// <returns> </returns>
|
||||
/// <exception cref="ArgumentException"></exception>
|
||||
public IpLocation Query(string ip)
|
||||
{
|
||||
if (ip == "::1")
|
||||
ip = "127.0.0.1";
|
||||
if (!Regex.Match(ip).Success)
|
||||
throw new ArgumentException("IP格式错误");
|
||||
IpLocation ipLocation = new IpLocation { Ip = ip };
|
||||
long intIp = IpToInt(ip);
|
||||
if (intIp >= IpToInt("127.0.0.1") && (intIp <= IpToInt("127.255.255.255")))
|
||||
{
|
||||
ipLocation.Country = "本机内部环回地址";
|
||||
ipLocation.Local = string.Empty;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (((intIp >= IpToInt("0.0.0.0")) && (intIp <= IpToInt("2.255.255.255"))) ||
|
||||
((intIp >= IpToInt("64.0.0.0")) && (intIp <= IpToInt("126.255.255.255"))) ||
|
||||
((intIp >= IpToInt("58.0.0.0")) && (intIp <= IpToInt("60.255.255.255"))))
|
||||
{
|
||||
ipLocation.Country = "网络保留地址";
|
||||
ipLocation.Local = string.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
long right = Count;
|
||||
long left = 0L;
|
||||
long startIp;
|
||||
long endIpOff;
|
||||
int countryFlag;
|
||||
while (left < (right - 1L))
|
||||
{
|
||||
long middle = (right + left) / 2L;
|
||||
startIp = GetStartIp(middle, out endIpOff);
|
||||
if (intIp == startIp)
|
||||
{
|
||||
left = middle;
|
||||
break;
|
||||
}
|
||||
|
||||
if (intIp > startIp)
|
||||
{
|
||||
left = middle;
|
||||
}
|
||||
else
|
||||
{
|
||||
right = middle;
|
||||
}
|
||||
}
|
||||
|
||||
startIp = GetStartIp(left, out endIpOff);
|
||||
long endIp = GetEndIp(endIpOff, out countryFlag);
|
||||
if ((startIp <= intIp) && (endIp >= intIp))
|
||||
{
|
||||
string local;
|
||||
ipLocation.Country = GetCountry(endIpOff, countryFlag, out local);
|
||||
ipLocation.Local = local.Replace("(我们一定要解放台湾!!!)", string.Empty);
|
||||
}
|
||||
else
|
||||
{
|
||||
ipLocation.Country = "未知";
|
||||
ipLocation.Local = string.Empty;
|
||||
}
|
||||
|
||||
return ipLocation;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 由IP地址查找对应的位置信息的字符串.
|
||||
/// </summary>
|
||||
public string Query2(string ip)
|
||||
{
|
||||
IpLocation result = Query(ip);
|
||||
return (result.Country + result.Local).Replace("CZ88.NET", string.Empty);
|
||||
}
|
||||
|
||||
private long GetStartIp(long left, out long endIpOff)
|
||||
{
|
||||
long leftOffset = _firstStartIpOffset + (left * 7L);
|
||||
byte[] buffer = new byte[7];
|
||||
Array.Copy(_data, leftOffset, buffer, 0, 7);
|
||||
endIpOff = (Convert.ToInt64(buffer[4].ToString(CultureInfo.InvariantCulture)) +
|
||||
(Convert.ToInt64(buffer[5].ToString(CultureInfo.InvariantCulture)) * 0x100L)) +
|
||||
((Convert.ToInt64(buffer[6].ToString(CultureInfo.InvariantCulture)) * 0x100L) * 0x100L);
|
||||
return ((Convert.ToInt64(buffer[0].ToString(CultureInfo.InvariantCulture)) +
|
||||
(Convert.ToInt64(buffer[1].ToString(CultureInfo.InvariantCulture)) * 0x100L)) +
|
||||
((Convert.ToInt64(buffer[2].ToString(CultureInfo.InvariantCulture)) * 0x100L) * 0x100L)) +
|
||||
(((Convert.ToInt64(buffer[3].ToString(CultureInfo.InvariantCulture)) * 0x100L) * 0x100L) * 0x100L);
|
||||
}
|
||||
|
||||
private long GetEndIp(long endIpOff, out int countryFlag)
|
||||
{
|
||||
byte[] buffer = new byte[5];
|
||||
Array.Copy(_data, endIpOff, buffer, 0, 5);
|
||||
countryFlag = buffer[4];
|
||||
return ((Convert.ToInt64(buffer[0].ToString(CultureInfo.InvariantCulture)) +
|
||||
(Convert.ToInt64(buffer[1].ToString(CultureInfo.InvariantCulture)) * 0x100L)) +
|
||||
((Convert.ToInt64(buffer[2].ToString(CultureInfo.InvariantCulture)) * 0x100L) * 0x100L)) +
|
||||
(((Convert.ToInt64(buffer[3].ToString(CultureInfo.InvariantCulture)) * 0x100L) * 0x100L) * 0x100L);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the country.
|
||||
/// </summary>
|
||||
/// <param name="endIpOff"> The end ip off. </param>
|
||||
/// <param name="countryFlag"> The country flag. </param>
|
||||
/// <param name="local"> The local. </param>
|
||||
/// <returns> country.</returns>
|
||||
private string GetCountry(long endIpOff, int countryFlag, out string local)
|
||||
{
|
||||
string country;
|
||||
long offset = endIpOff + 4L;
|
||||
switch (countryFlag)
|
||||
{
|
||||
case 1:
|
||||
case 2:
|
||||
country = GetFlagStr(ref offset, ref countryFlag, ref endIpOff);
|
||||
offset = endIpOff + 8L;
|
||||
local = (countryFlag == 1) ? string.Empty : GetFlagStr(ref offset, ref countryFlag, ref endIpOff);
|
||||
break;
|
||||
default:
|
||||
country = GetFlagStr(ref offset, ref countryFlag, ref endIpOff);
|
||||
local = GetFlagStr(ref offset, ref countryFlag, ref endIpOff);
|
||||
break;
|
||||
}
|
||||
|
||||
return country;
|
||||
}
|
||||
|
||||
private string GetFlagStr(ref long offset, ref int countryFlag, ref long endIpOff)
|
||||
{
|
||||
byte[] buffer = new byte[3];
|
||||
|
||||
while (true)
|
||||
{
|
||||
// 用于向前累加偏移量
|
||||
long forwardOffset = offset;
|
||||
int flag = _data[forwardOffset++];
|
||||
|
||||
// 没有重定向
|
||||
if (flag != 1 && flag != 2)
|
||||
break;
|
||||
Array.Copy(_data, forwardOffset, buffer, 0, 3);
|
||||
if (flag == 2)
|
||||
{
|
||||
countryFlag = 2;
|
||||
endIpOff = offset - 4L;
|
||||
}
|
||||
|
||||
offset = (Convert.ToInt64(buffer[0].ToString()) + (Convert.ToInt64(buffer[1].ToString()) * 0x100L)) +
|
||||
((Convert.ToInt64(buffer[2].ToString()) * 0x100L) * 0x100L);
|
||||
}
|
||||
|
||||
if (offset < 12L)
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
return GetStr(ref offset);
|
||||
}
|
||||
|
||||
private string GetStr(ref long offset)
|
||||
{
|
||||
StringBuilder stringBuilder = new StringBuilder();
|
||||
byte[] bytes = new byte[2];
|
||||
Encoding encoding = Encoding.GetEncoding("GB2312");
|
||||
while (true)
|
||||
{
|
||||
byte lowByte = _data[offset++];
|
||||
if (lowByte == 0)
|
||||
return stringBuilder.ToString();
|
||||
if (lowByte > 0x7f)
|
||||
{
|
||||
byte highByte = _data[offset++];
|
||||
bytes[0] = lowByte;
|
||||
bytes[1] = highByte;
|
||||
if (highByte == 0)
|
||||
return stringBuilder.ToString();
|
||||
stringBuilder.Append(encoding.GetString(bytes));
|
||||
}
|
||||
else
|
||||
{
|
||||
stringBuilder.Append((char)lowByte);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
229
common/Tnb.Common/Net/UserAgent.cs
Normal file
229
common/Tnb.Common/Net/UserAgent.cs
Normal file
@@ -0,0 +1,229 @@
|
||||
using System.Text.RegularExpressions;
|
||||
using JNPF.DependencyInjection;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using UAParser;
|
||||
|
||||
namespace JNPF.Common.Net;
|
||||
|
||||
/// <summary>
|
||||
/// UserAgent操作类.
|
||||
/// </summary>
|
||||
[SuppressSniffer]
|
||||
public class UserAgent : IUserAgent
|
||||
{
|
||||
private readonly static Parser s_uap;
|
||||
|
||||
#region Mobile UAs, OS & Devices
|
||||
|
||||
private static readonly HashSet<string> s_MobileOS = new HashSet<string>
|
||||
{
|
||||
"Android",
|
||||
"iOS",
|
||||
"Windows Mobile",
|
||||
"Windows Phone",
|
||||
"Windows CE",
|
||||
"Symbian OS",
|
||||
"BlackBerry OS",
|
||||
"BlackBerry Tablet OS",
|
||||
"Firefox OS",
|
||||
"Brew MP",
|
||||
"webOS",
|
||||
"Bada",
|
||||
"Kindle",
|
||||
"Maemo"
|
||||
};
|
||||
|
||||
private static readonly HashSet<string> s_MobileBrowsers = new HashSet<string>
|
||||
{
|
||||
"Android",
|
||||
"Firefox Mobile",
|
||||
"Opera Mobile",
|
||||
"Opera Mini",
|
||||
"Mobile Safari",
|
||||
"Amazon Silk",
|
||||
"webOS Browser",
|
||||
"MicroB",
|
||||
"Ovi Browser",
|
||||
"NetFront",
|
||||
"NetFront NX",
|
||||
"Chrome Mobile",
|
||||
"Chrome Mobile iOS",
|
||||
"UC Browser",
|
||||
"Tizen Browser",
|
||||
"Baidu Explorer",
|
||||
"QQ Browser Mini",
|
||||
"QQ Browser Mobile",
|
||||
"IE Mobile",
|
||||
"Polaris",
|
||||
"ONE Browser",
|
||||
"iBrowser Mini",
|
||||
"Nokia Services (WAP) Browser",
|
||||
"Nokia Browser",
|
||||
"Nokia OSS Browser",
|
||||
"BlackBerry WebKit",
|
||||
"BlackBerry",
|
||||
"Palm",
|
||||
"Palm Blazer",
|
||||
"Palm Pre",
|
||||
"Teleca Browser",
|
||||
"SEMC-Browser",
|
||||
"PlayStation Portable",
|
||||
"Nokia",
|
||||
"Maemo Browser",
|
||||
"Obigo",
|
||||
"Bolt",
|
||||
"Iris",
|
||||
"UP.Browser",
|
||||
"Minimo",
|
||||
"Bunjaloo",
|
||||
"Jasmine",
|
||||
"Dolfin",
|
||||
"Polaris",
|
||||
"Skyfire"
|
||||
};
|
||||
|
||||
private static readonly HashSet<string> s_MobileDevices = new HashSet<string>
|
||||
{
|
||||
"BlackBerry",
|
||||
"MI PAD",
|
||||
"iPhone",
|
||||
"iPad",
|
||||
"iPod",
|
||||
"Kindle",
|
||||
"Kindle Fire",
|
||||
"Nokia",
|
||||
"Lumia",
|
||||
"Palm",
|
||||
"DoCoMo",
|
||||
"HP TouchPad",
|
||||
"Xoom",
|
||||
"Motorola",
|
||||
"Generic Feature Phone",
|
||||
"Generic Smartphone"
|
||||
};
|
||||
|
||||
#endregion
|
||||
|
||||
private readonly HttpContext _httpContext;
|
||||
|
||||
private string _rawValue;
|
||||
private UserAgentInfo _userAgent;
|
||||
private DeviceInfo _device;
|
||||
private OSInfo _os;
|
||||
|
||||
private bool? _isBot;
|
||||
private bool? _isMobileDevice;
|
||||
private bool? _isTablet;
|
||||
private bool? _isPdfConverter;
|
||||
|
||||
static UserAgent()
|
||||
{
|
||||
s_uap = Parser.GetDefault();
|
||||
}
|
||||
|
||||
public UserAgent(HttpContext httpContext)
|
||||
{
|
||||
this._httpContext = httpContext;
|
||||
}
|
||||
|
||||
public string RawValue
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_rawValue == null)
|
||||
{
|
||||
if (_httpContext.Request != null)
|
||||
{
|
||||
_rawValue = _httpContext.Request?.Headers["User-Agent"];
|
||||
}
|
||||
else
|
||||
{
|
||||
_rawValue = "";
|
||||
}
|
||||
}
|
||||
|
||||
return _rawValue;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
_rawValue = value;
|
||||
_userAgent = null;
|
||||
_device = null;
|
||||
_os = null;
|
||||
_isBot = null;
|
||||
_isMobileDevice = null;
|
||||
_isTablet = null;
|
||||
_isPdfConverter = null;
|
||||
}
|
||||
}
|
||||
|
||||
public virtual UserAgentInfo userAgent
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_userAgent == null)
|
||||
{
|
||||
var tmp = s_uap.ParseUserAgent(this.RawValue);
|
||||
_userAgent = new UserAgentInfo(tmp.Family, tmp.Major, tmp.Minor, tmp.Patch);
|
||||
}
|
||||
return _userAgent;
|
||||
}
|
||||
}
|
||||
|
||||
public virtual DeviceInfo Device
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_device == null)
|
||||
{
|
||||
var tmp = s_uap.ParseDevice(this.RawValue);
|
||||
_device = new DeviceInfo(tmp.Family, tmp.IsSpider);
|
||||
}
|
||||
return _device;
|
||||
}
|
||||
}
|
||||
|
||||
public virtual OSInfo OS
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_os == null)
|
||||
{
|
||||
var tmp = s_uap.ParseOS(this.RawValue);
|
||||
_os = new OSInfo(tmp.Family, tmp.Major, tmp.Minor, tmp.Patch, tmp.PatchMinor);
|
||||
}
|
||||
return _os;
|
||||
}
|
||||
}
|
||||
|
||||
public virtual bool IsMobileDevice
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!_isMobileDevice.HasValue)
|
||||
{
|
||||
_isMobileDevice =
|
||||
s_MobileOS.Contains(this.OS.Family) ||
|
||||
s_MobileBrowsers.Contains(this.userAgent.Family) ||
|
||||
s_MobileDevices.Contains(this.Device.Family);
|
||||
}
|
||||
return _isMobileDevice.Value;
|
||||
}
|
||||
}
|
||||
|
||||
public virtual bool IsTablet
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!_isTablet.HasValue)
|
||||
{
|
||||
_isTablet =
|
||||
Regex.IsMatch(this.Device.Family, "iPad|Kindle Fire|Nexus 10|Xoom|Transformer|MI PAD|IdeaTab", RegexOptions.CultureInvariant) ||
|
||||
this.OS.Family == "BlackBerry Tablet OS";
|
||||
}
|
||||
|
||||
return _isTablet.Value;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user