diff --git a/AppInstallerReset.sln b/AppInstallerReset.sln index b505453..d5d1e22 100644 --- a/AppInstallerReset.sln +++ b/AppInstallerReset.sln @@ -3,12 +3,6 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 14 VisualStudioVersion = 14.0.25420.1 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "appinstaller", "AppInstallerReset\AppInstallerReset.vcxproj", "{C96219BE-8AFF-4914-8933-B6B7047A94D8}" - ProjectSection(ProjectDependencies) = postProject - {33D91B58-1981-4A3C-B4D1-86EE406CDE12} = {33D91B58-1981-4A3C-B4D1-86EE406CDE12} - {A7753282-AA16-43D9-8ACA-7065239DD702} = {A7753282-AA16-43D9-8ACA-7065239DD702} - EndProjectSection -EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pkgread", "pkgread\pkgread.vcxproj", "{A7753282-AA16-43D9-8ACA-7065239DD702}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "dlltest", "dlltest\dlltest.vcxproj", "{F5CCB3AB-AC43-432A-862D-4F264760B09B}" @@ -30,6 +24,16 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PriFile", "PriFileFormat\Pr EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "certmgr", "certmgr\certmgr.vcxproj", "{E04CCAB9-35DB-495C-A279-5B483C707CD0}" EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "appinstaller", "appinstaller\appinstaller.vcxproj", "{F0C84812-0CDF-4AA0-A0F8-F37AC833F39B}" + ProjectSection(ProjectDependencies) = postProject + {8EAC0230-4990-4E41-8E0F-D641D1561396} = {8EAC0230-4990-4E41-8E0F-D641D1561396} + {33D91B58-1981-4A3C-B4D1-86EE406CDE12} = {33D91B58-1981-4A3C-B4D1-86EE406CDE12} + {A7753282-AA16-43D9-8ACA-7065239DD702} = {A7753282-AA16-43D9-8ACA-7065239DD702} + {798ED492-EECE-457D-8FD8-129DA93CE126} = {798ED492-EECE-457D-8FD8-129DA93CE126} + {F5CCB3AB-AC43-432A-862D-4F264760B09B} = {F5CCB3AB-AC43-432A-862D-4F264760B09B} + {E04CCAB9-35DB-495C-A279-5B483C707CD0} = {E04CCAB9-35DB-495C-A279-5B483C707CD0} + EndProjectSection +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -40,16 +44,6 @@ Global Release|x86 = Release|x86 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {C96219BE-8AFF-4914-8933-B6B7047A94D8}.Debug|Any CPU.ActiveCfg = Debug|Win32 - {C96219BE-8AFF-4914-8933-B6B7047A94D8}.Debug|x64.ActiveCfg = Debug|x64 - {C96219BE-8AFF-4914-8933-B6B7047A94D8}.Debug|x64.Build.0 = Debug|x64 - {C96219BE-8AFF-4914-8933-B6B7047A94D8}.Debug|x86.ActiveCfg = Debug|Win32 - {C96219BE-8AFF-4914-8933-B6B7047A94D8}.Debug|x86.Build.0 = Debug|Win32 - {C96219BE-8AFF-4914-8933-B6B7047A94D8}.Release|Any CPU.ActiveCfg = Release|Win32 - {C96219BE-8AFF-4914-8933-B6B7047A94D8}.Release|x64.ActiveCfg = Release|x64 - {C96219BE-8AFF-4914-8933-B6B7047A94D8}.Release|x64.Build.0 = Release|x64 - {C96219BE-8AFF-4914-8933-B6B7047A94D8}.Release|x86.ActiveCfg = Release|Win32 - {C96219BE-8AFF-4914-8933-B6B7047A94D8}.Release|x86.Build.0 = Release|Win32 {A7753282-AA16-43D9-8ACA-7065239DD702}.Debug|Any CPU.ActiveCfg = Debug|Win32 {A7753282-AA16-43D9-8ACA-7065239DD702}.Debug|x64.ActiveCfg = Debug|x64 {A7753282-AA16-43D9-8ACA-7065239DD702}.Debug|x64.Build.0 = Debug|x64 @@ -118,6 +112,16 @@ Global {E04CCAB9-35DB-495C-A279-5B483C707CD0}.Release|x64.Build.0 = Release|x64 {E04CCAB9-35DB-495C-A279-5B483C707CD0}.Release|x86.ActiveCfg = Release|Win32 {E04CCAB9-35DB-495C-A279-5B483C707CD0}.Release|x86.Build.0 = Release|Win32 + {F0C84812-0CDF-4AA0-A0F8-F37AC833F39B}.Debug|Any CPU.ActiveCfg = Debug|Win32 + {F0C84812-0CDF-4AA0-A0F8-F37AC833F39B}.Debug|x64.ActiveCfg = Debug|x64 + {F0C84812-0CDF-4AA0-A0F8-F37AC833F39B}.Debug|x64.Build.0 = Debug|x64 + {F0C84812-0CDF-4AA0-A0F8-F37AC833F39B}.Debug|x86.ActiveCfg = Debug|Win32 + {F0C84812-0CDF-4AA0-A0F8-F37AC833F39B}.Debug|x86.Build.0 = Debug|Win32 + {F0C84812-0CDF-4AA0-A0F8-F37AC833F39B}.Release|Any CPU.ActiveCfg = Release|Win32 + {F0C84812-0CDF-4AA0-A0F8-F37AC833F39B}.Release|x64.ActiveCfg = Release|x64 + {F0C84812-0CDF-4AA0-A0F8-F37AC833F39B}.Release|x64.Build.0 = Release|x64 + {F0C84812-0CDF-4AA0-A0F8-F37AC833F39B}.Release|x86.ActiveCfg = Release|Win32 + {F0C84812-0CDF-4AA0-A0F8-F37AC833F39B}.Release|x86.Build.0 = Release|Win32 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/AppInstallerReset/AppInstallerReset.vcxproj.filters b/AppInstallerReset/AppInstallerReset.vcxproj.filters deleted file mode 100644 index 03c8628..0000000 --- a/AppInstallerReset/AppInstallerReset.vcxproj.filters +++ /dev/null @@ -1,17 +0,0 @@ - - - - - {4FC737F1-C7A5-4376-A066-2A32D752A2FF} - cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx - - - {93995380-89BD-4b04-88EB-625FBE52EBFB} - h;hh;hpp;hxx;hm;inl;inc;xsd - - - {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} - rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms - - - \ No newline at end of file diff --git a/appinstaller/appinstaller.rc b/appinstaller/appinstaller.rc new file mode 100644 index 0000000..fe168ac Binary files /dev/null and b/appinstaller/appinstaller.rc differ diff --git a/AppInstallerReset/AppInstallerReset.vcxproj b/appinstaller/appinstaller.vcxproj similarity index 68% rename from AppInstallerReset/AppInstallerReset.vcxproj rename to appinstaller/appinstaller.vcxproj index 0ce832c..2f20286 100644 --- a/AppInstallerReset/AppInstallerReset.vcxproj +++ b/appinstaller/appinstaller.vcxproj @@ -19,11 +19,10 @@ - {C96219BE-8AFF-4914-8933-B6B7047A94D8} + {F0C84812-0CDF-4AA0-A0F8-F37AC833F39B} Win32Proj - AppInstallerReset + appinstaller 8.1 - appinstaller @@ -31,6 +30,7 @@ true v140 Unicode + true Application @@ -38,6 +38,7 @@ v140 true Unicode + true Application @@ -88,12 +89,13 @@ Level3 Disabled - WIN32;_DEBUG;_WINDOWS;%(PreprocessorDefinitions) + WIN32;_DEBUG;_WINDOWS;HMODULE_MODE_EXE;_CRT_SECURE_NO_WARNINGS;_CRT_NON_CONFORMING_SWPRINTFS;%(PreprocessorDefinitions) true Windows true + shlwapi.lib;version.lib;dwmapi.lib;$(OutDir)pkgread.lib;$(OutDir)pkgmgr.lib;$(OutDir)certmgr.lib;$(OutDir)priformatcli.lib;$(OutDir)notice.lib;%(AdditionalDependencies) @@ -118,7 +120,7 @@ MaxSpeed true true - WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions) + WIN32;NDEBUG;_WINDOWS;HMODULE_MODE_EXE;_CRT_SECURE_NO_WARNINGS;_CRT_NON_CONFORMING_SWPRINTFS;%(PreprocessorDefinitions) true @@ -126,6 +128,7 @@ true true true + shlwapi.lib;version.lib;dwmapi.lib;$(OutDir)pkgread.lib;$(OutDir)pkgmgr.lib;$(OutDir)certmgr.lib;$(OutDir)priformatcli.lib;$(OutDir)notice.lib;%(AdditionalDependencies) @@ -147,8 +150,55 @@ + + + + + + + + + + + + + + + + + + + + + + + + C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.0\System.dll + + + + + + + + + + + + + + + + + + + + 这台计算机上缺少此项目引用的 NuGet 程序包。使用“NuGet 程序包还原”可下载这些程序包。有关更多信息,请参见 http://go.microsoft.com/fwlink/?LinkID=322105。缺少的文件是 {0}。 + + + \ No newline at end of file diff --git a/appinstaller/appinstaller.vcxproj.filters b/appinstaller/appinstaller.vcxproj.filters new file mode 100644 index 0000000..8c0b91e --- /dev/null +++ b/appinstaller/appinstaller.vcxproj.filters @@ -0,0 +1,100 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + 头文件 + + + 头文件 + + + 头文件 + + + 头文件 + + + 头文件 + + + 头文件 + + + 头文件 + + + 头文件 + + + 头文件 + + + 头文件 + + + 头文件 + + + 头文件 + + + 头文件 + + + 头文件 + + + 头文件 + + + 头文件 + + + 头文件 + + + + + 源文件 + + + + + 资源文件 + + + + + 资源文件 + + + 资源文件 + + + 资源文件 + + + 资源文件 + + + 资源文件 + + + + + + \ No newline at end of file diff --git a/appinstaller/cmdargs.h b/appinstaller/cmdargs.h new file mode 100644 index 0000000..1a14889 --- /dev/null +++ b/appinstaller/cmdargs.h @@ -0,0 +1,161 @@ +#pragma once +#include +#include +#include +#include +#include "nstring.h" +#include "rctools.h" +#include "filepath.h" +#include "raii.h" + +// ǰ׺統ǰ׺Ϊ-"/"ʱҿ롰-args"/args""args" ǵЧ +#define CMDARG_IGNOREPREFIXS 0b001 +// ֵ硰/args=fileҪú׺Чַ +#define CMDARG_ENABLEPARAMS 0b010 +// CMDARG_ENABLEPARAMS ʱΪա /args=file"/args" +#define CMDARG_IGNOREPARAMS 0b100 +// в +struct cmdarg +{ + std::vector prefixs; // ǰ׺Ϊ 0 + std::vector commands; // ȲΪ 0Ϊգ + std::vector postfixs; // ޺׺Ϊ 0 + std::wstring uniquelabel; // Ψһʶ + std::wstring description; // ɰı + DWORD flags; // ־ +}; +#define CMDARG_PREFIXS_DEFAULT {L"-", L"/"} +#define CMDARG_POSTFIXS_DEFAULT {} +std::vector g_argslist = { + {CMDARG_PREFIXS_DEFAULT, {L"silent", L"quiet", L"passive"}, CMDARG_POSTFIXS_DEFAULT, L"silent", GetRCStringSW (0), CMDARG_IGNOREPREFIXS}, + {CMDARG_PREFIXS_DEFAULT, {L"verysilent", L"veryquiet"}, CMDARG_POSTFIXS_DEFAULT, L"verysilent", GetRCStringSW (0), CMDARG_IGNOREPREFIXS}, + {CMDARG_PREFIXS_DEFAULT, {L"multiple", L"filelist"}, {L"="}, L"multiple", GetRCStringSW (0), CMDARG_IGNOREPREFIXS | CMDARG_ENABLEPARAMS} +}; +bool IsFile (const std::wstring &path) +{ + return IsPathExists (path); +} +bool IsURI (const std::wstring &str) +{ + try + { + auto uristr = gcnew System::String (str.c_str ()); + auto uri = gcnew System::Uri (uristr); + return uri != nullptr; + } + catch (...) { return false; } +} +enum class paramtype +{ + string, + file, + uri +}; +struct cmdkey +{ + std::wnstring key; // + paramtype type; // ͣIJǴڵļ·ʱ硰C:\1.txtΪļͬҲΪ uri + operator std::wnstring () { return key; } + operator LPCWSTR () { return key.c_str (); } + cmdkey (const std::wstring &k): key (k) + { + if (IsFile (k)) type = paramtype::file; + else if (IsURI (k)) type = paramtype::uri; + else type = paramtype::string; + } + cmdkey (const std::wstring &k, paramtype pt): key (k), type (pt) {} + bool operator == (const cmdkey &r) const { return key == r.key; } + bool operator > (const cmdkey &r) const { return key > r.key; } + bool operator < (const cmdkey &r) const { return key < r.key; } +}; +struct cmdvalue +{ + std::wstring value; // ֵ + paramtype type; + bool isnull; // ֵָ֧ʱʱֵΪʱΪ档 +}; +// ڴлȡ argv argcargc СΪ 1argv ĵһԪָ startpos Ϊ 1 +void ParseCmdArgs (LPWSTR *argv, DWORD argc, std::map &parseresult, DWORD startpos = 1) +{ + for (size_t i = startpos; i < argc; i ++) + { + std::wnstring arg = argv [i]; + arg = arg.trim (); + if (IsFile (arg)) parseresult [cmdkey (arg, paramtype::file)] = cmdvalue {L"", paramtype::file, true}; + else if (IsURI (arg)) parseresult [cmdkey (arg, paramtype::uri)] = cmdvalue {L"", paramtype::uri, true}; + else + { + for (auto &it : g_argslist) + { + std::set prefixs; + for (auto &it_s : it.prefixs) prefixs.insert (it_s); + int cmdhead = -1; + for (auto &it_s : prefixs) + { + auto plen = GetNormalizeStringLength (it_s); + auto parg = GetStringLeft (arg, plen); + if (it_s == parg) { cmdhead = plen; break; } + } + if (((it.flags & CMDARG_IGNOREPREFIXS) || !it.prefixs.size () || !prefixs.size ()) && cmdhead < 0) + { + if (!arg.length ()) continue; + cmdhead = 0; + } + if (cmdhead < 0) continue; + int postfixhead = -1; + std::set commands; + for (auto &it_s : it.commands) if (GetNormalizeStringLength (it_s)) commands.insert (it_s); + if (commands.empty ()) continue; + for (auto &it_s : commands) + { + auto clen = it_s.length (); + auto carg = GetStringLeft (arg.substr (cmdhead), clen); + if (it_s == carg) { postfixhead = cmdhead + clen; } + } + if (postfixhead < 0) continue; + std::set postfixs; + for (auto &it_s : it.postfixs) if (GetNormalizeStringLength (it_s)) postfixs.insert (it_s); + if (!(it.flags & CMDARG_ENABLEPARAMS)) it.postfixs.clear (); + if ((it.flags & CMDARG_ENABLEPARAMS) && postfixs.size ()) + { + int valuehead = -1; + auto rightpart = StringTrim (GetStringRight (arg, lstrlenW (arg.c_str ()) - postfixhead)); + if (it.flags & CMDARG_IGNOREPARAMS) + { + if (!rightpart.length ()) { parseresult [cmdkey (it.uniquelabel, paramtype::string)] = cmdvalue {L"", paramtype::string, true}; break; } + } + for (auto &it_s : postfixs) + { + auto plen = it_s.length (); + auto parg = GetStringLeft (rightpart, plen); + if (it_s == parg) { valuehead = plen; break; } + } + if (valuehead < 0) continue; + else + { + auto value = rightpart.substr (valuehead); + paramtype ptype = paramtype::string; + if (IsFile (value)) ptype = paramtype::file; + else if (IsURI (StringTrim (value))) ptype = paramtype::uri; + parseresult [cmdkey (it.uniquelabel, paramtype::string)] = cmdvalue {value, ptype, false}; + } + break; + } + else + { + parseresult [cmdkey (it.uniquelabel, paramtype::string)] = cmdvalue {L"", paramtype::string, true}; + break; + } + } + } + } +} +void ParseCmdLine (LPCWSTR lpCommandLine, std::map &parseresult) +{ + int argc = 0; + LPWSTR *alpstr = CommandLineToArgvW (lpCommandLine, &argc); + destruct relt ([&alpstr] () { + if (alpstr) LocalFree (alpstr); + }); + ParseCmdArgs (alpstr, argc, parseresult, 0); +} \ No newline at end of file diff --git a/appinstaller/dynarr.h b/appinstaller/dynarr.h new file mode 100644 index 0000000..7e69982 --- /dev/null +++ b/appinstaller/dynarr.h @@ -0,0 +1,77 @@ +#pragma once +#include +#include +#include + +template bool compare_default (const T &l, const T &r) { return l == r; } +template bool find_vec (std::vector &vec, const T &value, const std::function &callback, std::function compare = compare_default , bool sorted = false) +{ + const size_t n = vec.size (); + if (!compare) compare = compare_default; + if (sorted) + { + size_t left = 0, right = n; + while (left < right) + { + size_t mid = left + (right - left) / 2; + if (compare (vec [mid], value)) + { + callback (mid); + return true; + } + if (vec [mid] < value) left = mid + 1; + else right = mid; + } + return false; + } + if (n < 64) + { + for (size_t i = 0; i < n; i++) + { + if (compare (vec [i], value)) + { + callback (i); + return true; + } + } + return false; + } + const size_t blockSize = 8; + size_t i = 0; + for (; i + blockSize <= n; i += blockSize) + { + for (size_t j = 0; j < blockSize; j ++) + { + if (compare (vec [i + j], value)) + { + callback (i + j); + return true; + } + } + } + for (; i < n; i ++) + { + if (compare (vec [i], value)) + { + callback (i); + return true; + } + } + return false; +} +template void push_unique (std::vector &vec, const T &value, std::function compare = compare_default ) +{ + bool found = find_vec ( + vec, value, + [] (size_t) {}, + compare); + if (!found) vec.push_back (value); +} +template void push_normal (std::vector &vec, const T &value) +{ + vec.push_back (value); +} +template void push_normal (std::vector &target, const std::vector &another) +{ + target.insert (target.end (), another.begin (), another.end ()); +} \ No newline at end of file diff --git a/appinstaller/filepath.h b/appinstaller/filepath.h new file mode 100644 index 0000000..173dd4e --- /dev/null +++ b/appinstaller/filepath.h @@ -0,0 +1,871 @@ +#pragma once +#include +#include +#include +#include +#include +#include +#include +#include +#include "strcmp.h" +#include "version.h" +#include "module.h" +typedef version VERSION; +template constexpr T Max (T l, T r) { return l > r ? l : r; } +template constexpr T Max (T l, T m, T r) { return Max (Max (l, r), m); } +template constexpr T Max (T l, T ml, T mr, T r) { return Max (Max (l, ml), Max (mr, r)); } +template std::basic_string replace_substring +( + const std::basic_string &str, + const std::basic_string &from, + const std::basic_string &to +) +{ + if (from.empty ()) return str; + std::basic_string result; + size_t pos = 0; + size_t start_pos; + while ((start_pos = str.find (from, pos)) != std::basic_string::npos) + { + result.append (str, pos, start_pos - pos); + result.append (to); + pos = start_pos + from.length (); + } + result.append (str, pos, str.length () - pos); + return result; +} +std::string GetProgramRootDirectoryA (HMODULE hModule hModule_DefaultParam) +{ + char path [MAX_PATH]; + if (GetModuleFileNameA (hModule, path, MAX_PATH)) + { + std::string dir (path); + size_t pos = dir.find_last_of ("\\/"); + if (pos != std::string::npos) + { + dir = dir.substr (0, pos); + } + return dir; + } + return ""; +} +std::wstring GetProgramRootDirectoryW (HMODULE hModule hModule_DefaultParam) +{ + wchar_t path [MAX_PATH]; + if (GetModuleFileNameW (hModule, path, MAX_PATH)) + { + std::wstring dir (path); + size_t pos = dir.find_last_of (L"\\/"); + if (pos != std::wstring::npos) + { + dir = dir.substr (0, pos); + } + return dir; + } + return L""; +} +std::string EnsureTrailingSlash (const std::string &path) +{ + if (path.empty ()) return path; // ·ֱӷ + + char lastChar = path.back (); + if (lastChar == '\\' || lastChar == '/') + return path; // зֱָӷ + // ϵͳԭ·ʽʵķָ + char separator = (path.find ('/') != std::string::npos) ? '/' : '\\'; + return path + separator; +} +std::wstring EnsureTrailingSlash (const std::wstring &path) +{ + if (path.empty ()) return path; + + wchar_t lastChar = path.back (); + if (lastChar == L'\\' || lastChar == L'/') + return path; + + wchar_t separator = (path.find (L'/') != std::wstring::npos) ? L'/' : L'\\'; + return path + separator; +} +bool IsFileExistsW (LPCWSTR filename) +{ + DWORD dwAttrib = GetFileAttributesW (filename); + return (dwAttrib != INVALID_FILE_ATTRIBUTES && !(dwAttrib & FILE_ATTRIBUTE_DIRECTORY)); +} +bool IsFileExistsA (LPCSTR filename) +{ + DWORD dwAttrib = GetFileAttributesA (filename); + return (dwAttrib != INVALID_FILE_ATTRIBUTES && !(dwAttrib & FILE_ATTRIBUTE_DIRECTORY)); +} +bool IsFileExists (LPWSTR filePath) { return IsFileExistsW (filePath); } +bool IsFileExists (LPCSTR filePath) { return IsFileExistsA (filePath); } +bool IsFileExists (const std::string &filePath) { return IsFileExistsA (filePath.c_str ()); } +bool IsFileExists (const std::wstring &filePath) { return IsFileExistsW (filePath.c_str ()); } +bool IsDirectoryExistsA (LPCSTR path) +{ + DWORD attributes = GetFileAttributesA (path); + return (attributes != INVALID_FILE_ATTRIBUTES && (attributes & FILE_ATTRIBUTE_DIRECTORY)); +} +bool IsDirectoryExistsW (LPCWSTR path) +{ + DWORD attributes = GetFileAttributesW (path); + return (attributes != INVALID_FILE_ATTRIBUTES && (attributes & FILE_ATTRIBUTE_DIRECTORY)); +} +bool IsDirectoryExists (const std::string &path) { return IsDirectoryExistsA (path.c_str ()); } +bool IsDirectoryExists (const std::wstring &path) { return IsDirectoryExistsW (path.c_str ()); } +bool IsDirectoryExists (LPCSTR path) { return IsDirectoryExistsA (path); } +bool IsDirectoryExists (LPCWSTR path) { return IsDirectoryExistsW (path); } +bool IsPathExistsW (LPCWSTR filename) +{ + DWORD dwAttrib = GetFileAttributesW (filename); + return (dwAttrib != INVALID_FILE_ATTRIBUTES); +} +bool IsPathExistsA (LPCSTR filename) +{ + DWORD dwAttrib = GetFileAttributesA (filename); + return (dwAttrib != INVALID_FILE_ATTRIBUTES); +} +bool IsPathExists (const std::string &path) { return IsPathExistsA (path.c_str ()); } +bool IsPathExists (const std::wstring &path) { return IsPathExistsW (path.c_str ()); } +bool IsPathExists (LPCSTR path) { return IsPathExistsA (path); } +bool IsPathExists (LPCWSTR path) { return IsPathExistsW (path); } +std::string NormalizePath (const std::string &path) +{ + if (!path.empty () && path.back () == '\\') + return path.substr (0, path.size () - 1); + return path.c_str (); +} +std::wstring NormalizePath (const std::wstring &path) +{ + if (!path.empty () && path.back () == L'\\') + return path.substr (0, path.size () - 1); + return path.c_str (); +} +std::vector EnumSubdirectories (const std::string &directory, bool includeParentPath) +{ + std::vector subdirs; + std::string normPath = NormalizePath (directory); + std::string searchPath = normPath + "\\*"; + WIN32_FIND_DATAA findData; + HANDLE hFind = FindFirstFileA (searchPath.c_str (), &findData); + if (hFind == INVALID_HANDLE_VALUE) return subdirs; + do + { + // "." ".." + if (strcmp (findData.cFileName, ".") == 0 || strcmp (findData.cFileName, "..") == 0) + continue; + // жǷΪĿ¼ + if (findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) + { + if (includeParentPath) + subdirs.push_back (normPath + "\\" + findData.cFileName); + else + subdirs.push_back (findData.cFileName); + } + } while (FindNextFileA (hFind, &findData)); + FindClose (hFind); + return subdirs; +} +std::vector EnumSubdirectories (const std::wstring &directory, bool includeParentPath) +{ + std::vector subdirs; + std::wstring normPath = NormalizePath (directory); + std::wstring searchPath = normPath + L"\\*"; + WIN32_FIND_DATAW findData; + HANDLE hFind = FindFirstFileW (searchPath.c_str (), &findData); + if (hFind == INVALID_HANDLE_VALUE) return subdirs; + do + { + if (wcscmp (findData.cFileName, L".") == 0 || wcscmp (findData.cFileName, L"..") == 0) + continue; + if (findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) + { + if (includeParentPath) + subdirs.push_back (normPath + L"\\" + findData.cFileName); + else + subdirs.push_back (findData.cFileName); + } + } while (FindNextFileW (hFind, &findData)); + FindClose (hFind); + return subdirs; +} +std::string GetCurrentProgramPathA (HMODULE hModule hModule_DefaultParam) +{ + std::vector buf (Max (MAX_PATH, GetModuleFileNameA (hModule, nullptr, 0)) + 1); + GetModuleFileNameA (hModule, buf.data (), buf.capacity ()); + return buf.data (); +} +std::wstring GetCurrentProgramPathW (HMODULE hModule hModule_DefaultParam) +{ + std::vector buf (Max (MAX_PATH, GetModuleFileNameW (hModule, nullptr, 0)) + 1); + GetModuleFileNameW (hModule, buf.data (), buf.capacity ()); + return buf.data (); +} +std::string GetCurrentProgramNameA (HMODULE hModule hModule_DefaultParam) { return PathFindFileNameA (GetCurrentProgramPathA (hModule).c_str ()); } +std::wstring GetCurrentProgramNameW (HMODULE hModule hModule_DefaultParam) { return PathFindFileNameW (GetCurrentProgramPathW (hModule).c_str ()); } +VERSION GetExeFileVersion (LPCSTR lpszFilePath) +{ + VERSION ver (0); + DWORD dummy; + DWORD size = GetFileVersionInfoSizeA (lpszFilePath, &dummy); + std::vector pVersionInfo (size); + if (!GetFileVersionInfoA (lpszFilePath, 0, size, pVersionInfo.data ())) + { + return ver; + } + VS_FIXEDFILEINFO* pFileInfo = nullptr; + UINT len = 0; + if (!VerQueryValueA (pVersionInfo.data (), "\\", (LPVOID *)&pFileInfo, &len)) + { + return ver; + } + if (len == 0 || pFileInfo == nullptr) + { + return ver; + } + ver = VERSION ( + HIWORD (pFileInfo->dwFileVersionMS), + LOWORD (pFileInfo->dwFileVersionMS), + HIWORD (pFileInfo->dwFileVersionLS), + LOWORD (pFileInfo->dwFileVersionLS) + ); + return ver; +} +VERSION GetExeFileVersion (LPCWSTR lpswFilePath) +{ + VERSION ver (0); + DWORD dummy; + DWORD size = GetFileVersionInfoSizeW (lpswFilePath, &dummy); + std::vector pVersionInfo (size); + if (!GetFileVersionInfoW (lpswFilePath, 0, size, pVersionInfo.data ())) + { + return ver; + } + VS_FIXEDFILEINFO* pFileInfo = nullptr; + UINT len = 0; + if (!VerQueryValueA (pVersionInfo.data (), "\\", (LPVOID *)&pFileInfo, &len)) + { + return ver; + } + if (len == 0 || pFileInfo == nullptr) + { + return ver; + } + ver = VERSION ( + HIWORD (pFileInfo->dwFileVersionMS), + LOWORD (pFileInfo->dwFileVersionMS), + HIWORD (pFileInfo->dwFileVersionLS), + LOWORD (pFileInfo->dwFileVersionLS) + ); + return ver; +} +VERSION GetExeFileVersion (std::wstring objswFilePath) +{ + return GetExeFileVersion (objswFilePath.c_str ()); +} +VERSION GetExeFileVersion (std::string objszFilePath) +{ + return GetExeFileVersion (objszFilePath.c_str ()); +} +// õǰ̵ĻRunPathProgramPath +void SetupInstanceEnvironment (HMODULE hModule hModule_DefaultParam) +{ + // RunPathΪǰĿ¼޽βбܣ + std::vector currentDir (Max (GetCurrentDirectoryW (0, nullptr), MAX_PATH) + 1); + DWORD len = GetCurrentDirectoryW (currentDir.capacity (), currentDir.data ()); + if (len > 0) + { + std::wstring runPath (currentDir.data ()); + if (!runPath.empty () && (runPath.back () == L'\\' || runPath.back () == L'/')) + { + runPath.pop_back (); + } + SetEnvironmentVariableW (L"RunPath", runPath.c_str ()); + } + // ProgramPathΪĿ¼޽βбܣ + std::vector modulePath (Max (GetModuleFileNameW (hModule, nullptr, 0), MAX_PATH) + 1); + len = GetModuleFileNameW (hModule, modulePath.data (), MAX_PATH); + if (len > 0 && len < MAX_PATH) + { + wchar_t* lastSlash = wcsrchr (modulePath.data (), L'\\'); + if (!lastSlash) lastSlash = wcsrchr (modulePath.data (), L'/'); + if (lastSlash) *lastSlash = L'\0'; + std::wstring programPath (modulePath.data ()); + if (!programPath.empty () && (programPath.back () == L'\\' || programPath.back () == L'/')) + { + programPath.pop_back (); + } + SetEnvironmentVariableW (L"ProgramPath", programPath.c_str ()); + } +} +// ַչ +std::wstring ProcessEnvVars (const std::wstring &input) +{ + DWORD requiredSize = ExpandEnvironmentStringsW (input.c_str (), nullptr, 0); + if (requiredSize == 0) return input; + std::wstring buffer (requiredSize, L'\0'); + if (!ExpandEnvironmentStringsW (input.c_str (), &buffer [0], requiredSize)) + { + return input; + } + buffer.resize (requiredSize - 1); // ȥַֹ + return buffer.c_str (); +} +std::wstring ProcessEnvVars (LPCWSTR input) +{ + return ProcessEnvVars (std::wstring (input)); +} +// ANSIַչ +std::string ProcessEnvVars (const std::string &input) +{ + DWORD requiredSize = ExpandEnvironmentStringsA (input.c_str (), nullptr, 0); + if (requiredSize == 0) return input; + std::string buffer (requiredSize, '\0'); + if (!ExpandEnvironmentStringsA (input.c_str (), &buffer [0], requiredSize)) + { + return input; + } + buffer.resize (requiredSize - 1); // ȥַֹ + return buffer.c_str (); +} +std::string ProcessEnvVars (LPCSTR input) +{ + return ProcessEnvVars (std::string (input)); +} +std::string GetCurrentDirectoryA () +{ + std::vector buf (Max (GetCurrentDirectoryA (0, nullptr), MAX_PATH) + 1); + GetCurrentDirectoryA (buf.size (), buf.data ()); + return buf.data (); +} +std::wstring GetCurrentDirectoryW () +{ + std::vector buf (Max (GetCurrentDirectoryW (0, nullptr), MAX_PATH) + 1); + GetCurrentDirectoryW (buf.size (), buf.data ()); + return buf.data (); +} +std::wstring GetFileDirectoryW (const std::wstring &filePath) +{ + std::vector buf (filePath.capacity () + 1); + lstrcpyW (buf.data (), filePath.c_str ()); + PathRemoveFileSpecW (buf.data ()); + return buf.data (); +} +std::string GetFileDirectoryA (const std::string &filePath) +{ + std::vector buf (filePath.capacity () + 1); + lstrcpyA (buf.data (), filePath.c_str ()); + PathRemoveFileSpecA (buf.data ()); + return buf.data (); +} +size_t EnumerateFilesW (const std::wstring &directory, const std::wstring &filter, + std::vector &outFiles, bool recursive = false) +{ + std::wstring searchPath = directory; + if (!searchPath.empty () && searchPath.back () != L'\\') + { + searchPath += L'\\'; + } + searchPath += filter; + WIN32_FIND_DATAW findData; + HANDLE hFind = FindFirstFileW (searchPath.c_str (), &findData); + if (hFind != INVALID_HANDLE_VALUE) + { + do { + if (!(findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) + { + outFiles.push_back (directory + L"\\" + findData.cFileName); + } + } while (FindNextFileW (hFind, &findData)); + FindClose (hFind); + } + if (recursive) { + std::wstring subDirSearchPath = directory + L"\\*"; + hFind = FindFirstFileW (subDirSearchPath.c_str (), &findData); + if (hFind != INVALID_HANDLE_VALUE) + { + do { + if ((findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) && + wcscmp (findData.cFileName, L".") != 0 && wcscmp (findData.cFileName, L"..") != 0) + { + EnumerateFilesW (directory + L"\\" + findData.cFileName, filter, outFiles, true); + } + } while (FindNextFileW (hFind, &findData)); + FindClose (hFind); + } + } + return outFiles.size (); +} +// ǷΪ Windows 豸СдУ +bool IsReservedName (const std::wstring &name) +{ + static const wchar_t* reserved [] = { + L"CON", L"PRN", L"AUX", L"NUL", L"COM1", L"COM2", L"COM3", L"COM4", L"COM5", L"COM6", L"COM7", L"COM8", L"COM9", + L"LPT1", L"LPT2", L"LPT3", L"LPT4", L"LPT5", L"LPT6", L"LPT7", L"LPT8", L"LPT9" + }; + std::wstring upperName = StringToUpper (name); + for (const auto& res : reserved) + { + if (upperName == res || (upperName.rfind (res, 0) == 0 && upperName.length () > wcslen (res) && upperName [wcslen (res)] == L'.')) + { + return true; + } + } + return false; +} +// Windows ļ淶 (Unicode) +bool IsValidWindowsNameW (LPCWSTR name) +{ + if (!name || !*name) return false; + std::wstring wname (name); + if (wname.find_first_of (L"<>:\"/\\|?*") != std::wstring::npos) return false; + if (IsReservedName (wname)) return false; + if (wname.back () == L' ' || wname.back () == L'.') return false; + return true; +} +// Windows ļ淶 (ANSI) +bool IsValidWindowsNameA (LPCSTR name) +{ + if (!name || !*name) return false; + std::string str (name); + if (str.find_first_of ("<>:\"/\\|?*") != std::string::npos) return false; + + // ת ANSI ַ + int len = MultiByteToWideChar (CP_ACP, 0, name, -1, NULL, 0); + if (len <= 0) return false; + std::wstring wname (len - 1, L'\0'); + MultiByteToWideChar (CP_ACP, 0, name, -1, &wname [0], len); + if (IsReservedName (wname)) return false; + if (str.back () == ' ' || str.back () == '.') return false; + return true; +} +bool IsValidWindowsName (LPCSTR name) { return IsValidWindowsNameA (name); } +bool IsValidWindowsName (LPCWSTR name) { return IsValidWindowsNameW (name); } +bool IsValidWindowsName (const std::wstring &name) { return IsValidWindowsName (name.c_str ()); } +bool IsValidWindowsName (const std::string &name) { return IsValidWindowsName (name.c_str ()); } +std::wstring GetRootFolderNameFromFilePath (const std::wstring &lpFilePath) +{ + std::vector szPath (Max (lpFilePath.length (), MAX_PATH) + 1); + if (!PathCanonicalizeW (szPath.data (), lpFilePath.c_str ())) return L""; + if (PathRemoveFileSpecW (szPath.data ()) == FALSE) return L""; + LPCWSTR pszFolder = PathFindFileNameW (szPath.data ()); + if (*pszFolder != L'\0') return std::wstring (pszFolder); + WCHAR rootName [3] = {szPath [0], L':', L'\0'}; + return std::wstring (rootName); +} +std::wstring GetSafeTimestampForFilename () +{ + ::FILETIME ft; + GetSystemTimeAsFileTime (&ft); + SYSTEMTIME st; + FileTimeToSystemTime (&ft, &st); + std::wstringstream wss; + wss << std::setfill (L'0') + << st.wYear + << std::setw (2) << st.wMonth + << std::setw (2) << st.wDay << L"_" + << std::setw (2) << st.wHour + << std::setw (2) << st.wMinute + << std::setw (2) << st.wSecond + << std::setw (3) << st.wMilliseconds; + return wss.str (); +} +size_t EnumFiles ( + const std::wstring &lpDir, + const std::wstring &lpFilter, + std::vector &aszOutput, + bool bOutputWithPath = false, + bool bSortByLetter = false, + bool bIncludeSubDir = false +) { + if (!bIncludeSubDir) aszOutput.clear (); + std::vector filters; + size_t start = 0; + while (start < lpFilter.length ()) + { + size_t pos = lpFilter.find (L'\\', start); + if (pos == std::wstring::npos) pos = lpFilter.length (); + filters.emplace_back (lpFilter.substr (start, pos - start)); + start = pos + 1; + } + + std::function enumDir; + enumDir = [&] (const std::wstring &physicalPath, std::wstring relativePath) + { + WIN32_FIND_DATAW ffd; + HANDLE hFind = FindFirstFileW ((physicalPath + L"\\*").c_str (), &ffd); + if (hFind == INVALID_HANDLE_VALUE) return; + do { + if (wcscmp (ffd.cFileName, L".") == 0 || + wcscmp (ffd.cFileName, L"..") == 0) continue; + const bool isDir = (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY); + const std::wstring newPhysical = physicalPath + L"\\" + ffd.cFileName; + std::wstring newRelative = relativePath; + if (isDir) { + if (bIncludeSubDir) { + newRelative += ffd.cFileName; + newRelative += L"\\"; + enumDir (newPhysical, newRelative); + } + } + else + { + for (const auto &filter : filters) + { + if (PathMatchSpecW (ffd.cFileName, filter.c_str ())) + { + aszOutput.push_back + ( + bOutputWithPath ? newPhysical : (relativePath + ffd.cFileName) + ); + break; + } + } + } + } while (FindNextFileW (hFind, &ffd)); + FindClose (hFind); + }; + enumDir (lpDir, L""); + if (bSortByLetter) std::sort (aszOutput.begin (), aszOutput.end ()); + return aszOutput.size (); +} +std::wstring GetRelativePath ( + const std::wstring &pszBaseDir, + const std::wstring &pszFullPath, + DWORD cchRelative +) { + std::vector szBase (Max (pszBaseDir.length (), pszFullPath.length (), MAX_PATH) + 1); + wcscpy_s (szBase.data (), MAX_PATH, pszBaseDir.c_str ()); + if (szBase [wcslen (szBase.data ()) - 1] != L'\\') + { + wcscat_s (szBase.data (), MAX_PATH, L"\\"); + } + std::vector buf (Max (MAX_PATH, szBase.size ()) + 1); + BOOL res = PathRelativePathToW ( + buf.data (), + szBase.data (), + FILE_ATTRIBUTE_DIRECTORY, + pszFullPath.c_str (), + FILE_ATTRIBUTE_NORMAL + ); + if (res) return buf.data (); + else return L""; +} +size_t EnumDirectory ( + const std::wstring &lpDir, + std::vector &aszOutput, + bool bOutputWithPath = false, + bool bSortByLetter = false, + bool bIncludeSubDir = false +) { + aszOutput.clear (); + std::function enumDir; + enumDir = [&] (const std::wstring &physicalPath, const std::wstring &relativePath) { + WIN32_FIND_DATAW ffd; + HANDLE hFind = FindFirstFileW ((physicalPath + L"\\*").c_str (), &ffd); + if (hFind == INVALID_HANDLE_VALUE) return; + do + { + const std::wstring name = ffd.cFileName; + if (name == L"." || name == L"..") continue; + const bool isDir = (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0; + std::wstring newPhysical = physicalPath + L"\\" + name; + std::wstring newRelative = relativePath + name; + if (isDir) + { + if (bIncludeSubDir) enumDir (newPhysical, newRelative + L"\\"); + if (bOutputWithPath) aszOutput.push_back (newPhysical); + else aszOutput.push_back (newRelative); + } + } while (FindNextFileW (hFind, &ffd)); + FindClose (hFind); + }; + enumDir (lpDir, L""); + if (bSortByLetter) std::sort (aszOutput.begin (), aszOutput.end ()); + return aszOutput.size (); +} + +static DWORD CALLBACK ProgressRoutine ( + LARGE_INTEGER TotalFileSize, + LARGE_INTEGER TotalBytesTransferred, + LARGE_INTEGER /*StreamSize*/, + LARGE_INTEGER /*StreamBytesTransferred*/, + DWORD /*dwStreamNumber*/, + DWORD /*dwCallbackReason*/, + HANDLE /*hSourceFile*/, + HANDLE /*hDestinationFile*/, + LPVOID lpData +) { + auto *pCallback = reinterpret_cast *> (lpData); + if (pCallback && *pCallback) + { + int progress = static_cast ( + (TotalBytesTransferred.QuadPart * 100) / TotalFileSize.QuadPart + ); + (*pCallback) (progress); + } + return PROGRESS_CONTINUE; +} +bool RenameFileW ( + const std::wstring &lpSrcPath, + const std::wstring &lpDestPath, + std::function fProgress = nullptr +) { + LPPROGRESS_ROUTINE pRoutine = nullptr; + LPVOID pData = nullptr; + if (fProgress) + { + pRoutine = ProgressRoutine; + pData = &fProgress; + } + DWORD flags = MOVEFILE_COPY_ALLOWED; + BOOL ok = MoveFileWithProgressW ( + lpSrcPath.c_str (), + lpDestPath.c_str (), + pRoutine, + pData, + flags + ); + return ok != FALSE; +} +bool RenameFileA ( + const std::string &lpSrcPath, + const std::string &lpDestPath, + std::function fProgress = nullptr +) { + LPPROGRESS_ROUTINE pRoutine = nullptr; + LPVOID pData = nullptr; + if (fProgress) + { + pRoutine = ProgressRoutine; + pData = &fProgress; + } + DWORD flags = MOVEFILE_COPY_ALLOWED; + BOOL ok = MoveFileWithProgressA ( + lpSrcPath.c_str (), + lpDestPath.c_str (), + pRoutine, + pData, + flags + ); + return ok != FALSE; +} +bool RenameFileW (const std::wstring &lpSrcDir, const std::wstring &lpSrcName, const std::wstring &lpDestName, std::function fProgress = nullptr) +{ + struct BuildTask + { + LPWSTR src = nullptr, dest = nullptr; + ~BuildTask () + { + if (src != nullptr) + { + delete [] src; + src = nullptr; + } + if (dest != nullptr) + { + delete [] dest; + dest = nullptr; + } + } + }; + BuildTask bt; + bt.src = new WCHAR [lpSrcDir.length () + lpSrcName.length () + 2]; + bt.dest = new WCHAR [lpSrcDir.length () + lpDestName.length () + 2]; + PathCombineW (bt.src, lpSrcDir.c_str (), lpSrcName.c_str ()); + PathCombineW (bt.dest, lpSrcDir.c_str (), lpDestName.c_str ()); + return RenameFileW (bt.src, bt.dest, fProgress); +} +bool RenameFileA (const std::string &lpSrcDir, const std::string &lpSrcName, const std::string &lpDestName, std::function fProgress = nullptr) +{ + struct BuildTask + { + LPSTR src = nullptr, dest = nullptr; + ~BuildTask () + { + if (src != nullptr) + { + delete [] src; + src = nullptr; + } + if (dest != nullptr) + { + delete [] dest; + dest = nullptr; + } + } + }; + BuildTask bt; + bt.src = new CHAR [lpSrcDir.length () + lpSrcName.length () + 2]; + bt.dest = new CHAR [lpSrcDir.length () + lpDestName.length () + 2]; + PathCombineA (bt.src, lpSrcDir.c_str (), lpSrcName.c_str ()); + PathCombineA (bt.dest, lpSrcDir.c_str (), lpDestName.c_str ()); + return RenameFileA (bt.src, bt.dest, fProgress); +} +bool RenameFile (const std::wstring &lpSrcPath, const std::wstring &lpDestPath, std::function fProgress = nullptr) +{ + return RenameFileW (lpSrcPath, lpDestPath, fProgress); +} +bool RenameFile (const std::string &lpSrcPath, const std::string &lpDestPath, std::function fProgress = nullptr) +{ + return RenameFileA (lpSrcPath, lpDestPath, fProgress); +} +bool RenameFile (const std::wstring &lpSrcDir, const std::wstring &lpSrcName, const std::wstring &lpDestName, std::function fProgress = nullptr) +{ + return RenameFileW (lpSrcDir, lpSrcName, lpDestName, fProgress); +} +bool RenameFile (const std::string &lpSrcDir, const std::string &lpSrcName, const std::string &lpDestName, std::function fProgress = nullptr) +{ + return RenameFileA (lpSrcDir, lpSrcName, lpDestName, fProgress); +} +bool RenameDirectoryW ( + const std::wstring &lpSrcPath, + const std::wstring &lpDestPath, + std::function fProgress = nullptr +) { + LPPROGRESS_ROUTINE pRoutine = nullptr; + LPVOID pData = nullptr; + if (fProgress) + { + pRoutine = ProgressRoutine; + pData = &fProgress; + } + DWORD flags = MOVEFILE_COPY_ALLOWED; + BOOL ok = MoveFileWithProgressW ( + lpSrcPath.c_str (), + lpDestPath.c_str (), + pRoutine, + pData, + flags + ); + return ok != FALSE; +} +bool RenameDirectoryA ( + const std::string &lpSrcPath, + const std::string &lpDestPath, + std::function fProgress = nullptr +) { + LPPROGRESS_ROUTINE pRoutine = nullptr; + LPVOID pData = nullptr; + if (fProgress) + { + pRoutine = ProgressRoutine; + pData = &fProgress; + } + DWORD flags = MOVEFILE_COPY_ALLOWED; + BOOL ok = MoveFileWithProgressA ( + lpSrcPath.c_str (), + lpDestPath.c_str (), + pRoutine, + pData, + flags + ); + return ok != FALSE; +} +bool RenameDirectoryW ( + const std::wstring &lpParentDir, + const std::wstring &lpSrcName, + const std::wstring &lpDestName, + std::function fProgress = nullptr +) { + struct PathBuilder + { + LPWSTR src = nullptr; + LPWSTR dest = nullptr; + ~PathBuilder () + { + delete [] src; + delete [] dest; + } + } pb; + pb.src = new WCHAR [lpParentDir.length () + lpSrcName.length () + 2]; + pb.dest = new WCHAR [lpParentDir.length () + lpDestName.length () + 2]; + PathCombineW (pb.src, lpParentDir.c_str (), lpSrcName.c_str ()); + PathCombineW (pb.dest, lpParentDir.c_str (), lpDestName.c_str ()); + return RenameDirectoryW (pb.src, pb.dest, fProgress); +} +bool RenameDirectoryA ( + const std::string &lpParentDir, + const std::string &lpSrcName, + const std::string &lpDestName, + std::function fProgress = nullptr +) { + struct PathBuilder + { + LPSTR src = nullptr; + LPSTR dest = nullptr; + ~PathBuilder () + { + delete [] src; + delete [] dest; + } + } pb; + pb.src = new CHAR [lpParentDir.length () + lpSrcName.length () + 2]; + pb.dest = new CHAR [lpParentDir.length () + lpDestName.length () + 2]; + PathCombineA (pb.src, lpParentDir.c_str (), lpSrcName.c_str ()); + PathCombineA (pb.dest, lpParentDir.c_str (), lpDestName.c_str ()); + return RenameDirectoryA (pb.src, pb.dest, fProgress); +} +bool RenameDirectory ( + const std::wstring &src, + const std::wstring &dst, + std::function fProgress = nullptr +) { + return RenameDirectoryW (src, dst, fProgress); +} +bool RenameDirectory ( + const std::string &src, + const std::string &dst, + std::function fProgress = nullptr +) { + return RenameDirectoryA (src, dst, fProgress); +} +bool RenameDirectory ( + const std::wstring &parentDir, + const std::wstring &srcName, + const std::wstring &dstName, + std::function fProgress = nullptr +) { + return RenameDirectoryW (parentDir, srcName, dstName, fProgress); +} +bool RenameDirectory ( + const std::string &parentDir, + const std::string &srcName, + const std::string &dstName, + std::function fProgress = nullptr +) { + return RenameDirectoryA (parentDir, srcName, dstName, fProgress); +} +std::wstring CombinePath (const std::wstring &left, const std::wstring &right) +{ + std::vector buf (left.capacity () + right.capacity () + 2); + PathCombineW (buf.data (), left.c_str (), right.c_str ()); + return buf.data (); +} +#ifdef PathCommonPrefix +#undef PathCommonPrefix +#endif +std::wstring PathCommonPrefix (const std::wstring &file1, const std::wstring &file2) +{ + std::vector buf (Max (file1.capacity (), file2.capacity (), MAX_PATH) + 2); + PathCommonPrefixW (file1.c_str (), file2.c_str (), buf.data ()); + return buf.data (); +} +#undef GetFullPathName +std::wstring GetFullPathName (const std::wstring &lpFileName) +{ + if (lpFileName.empty ()) return L""; + DWORD length = GetFullPathNameW (lpFileName.c_str (), 0, nullptr, nullptr); + if (length == 0) return L""; + std::vector buffer (length + 1, L'\0'); + DWORD result = GetFullPathNameW (lpFileName.c_str (), length, buffer.data (), nullptr); + if (result == 0) return L""; + return std::wstring (buffer.data (), result); +} +bool PathEquals (const std::wstring &path1, const std::wstring &path2) +{ + size_t maxlen = Max (path1.capacity () + 1, path2.capacity () + 1, MAX_PATH); + std::vector buf1 (maxlen), buf2 (maxlen); + PathCanonicalizeW (buf1.data (), path1.c_str ()); + PathCanonicalizeW (buf2.data (), path2.c_str ()); + return IsNormalizeStringEquals (buf1.data (), buf2.data ()); +} \ No newline at end of file diff --git a/appinstaller/ieshell.h b/appinstaller/ieshell.h new file mode 100644 index 0000000..9d059cf --- /dev/null +++ b/appinstaller/ieshell.h @@ -0,0 +1,46 @@ +#pragma once +#include +#include +#include +#include "filepath.h" +void SetWebBrowserEmulation () +{ + std::wstring instname = GetCurrentProgramNameW (); + BOOL isWow64 = FALSE; + IsWow64Process (GetCurrentProcess (), &isWow64); + HKEY hKey; + LPCWSTR keyPath = isWow64 + ? L"SOFTWARE\\WOW6432Node\\Microsoft\\Internet Explorer\\Main\\FeatureControl\\FEATURE_BROWSER_EMULATION" + : L"SOFTWARE\\Microsoft\\Internet Explorer\\Main\\FeatureControl\\FEATURE_BROWSER_EMULATION"; + LONG result = RegOpenKeyExW (HKEY_CURRENT_USER, keyPath, 0, KEY_WRITE, &hKey); + if (result == ERROR_SUCCESS) + { + DWORD value = 11001; + RegSetValueExW (hKey, instname.c_str (), 0, REG_DWORD, reinterpret_cast (&value), sizeof (value)); + RegCloseKey (hKey); + } +} +// ϵͳװ Internet Explorer 汾ţ 891011 +int GetInternetExplorerVersionMajor () +{ + HKEY hKey; + LPCWSTR IEKeyPath = L"SOFTWARE\\Microsoft\\Internet Explorer"; + if (RegOpenKeyExW (HKEY_LOCAL_MACHINE, IEKeyPath, 0, KEY_READ, &hKey) != ERROR_SUCCESS) return 0; + WCHAR buffer [128] = {0}; + DWORD bufferSize = sizeof (buffer); + DWORD type = 0; + std::wstring versionStr; + if (RegQueryValueExW (hKey, L"svcVersion", NULL, &type, (LPBYTE)buffer, &bufferSize) == ERROR_SUCCESS) versionStr = buffer; + else + { + bufferSize = sizeof (buffer); + if (RegQueryValueExW (hKey, L"Version", NULL, &type, (LPBYTE)buffer, &bufferSize) == ERROR_SUCCESS) versionStr = buffer; + } + RegCloseKey (hKey); + if (versionStr.empty ()) return 0; + int major = 0; + swscanf_s (versionStr.c_str (), L"%d", &major); + return major; +} +bool IsInternetExplorer10 () { return GetInternetExplorerVersionMajor () == 10; } +bool IsInternetExplorer11AndLater () { return GetInternetExplorerVersionMajor () >= 11; } diff --git a/appinstaller/initfile.h b/appinstaller/initfile.h new file mode 100644 index 0000000..f97ce89 --- /dev/null +++ b/appinstaller/initfile.h @@ -0,0 +1,488 @@ +#pragma once +#ifndef _CRT_NON_CONFORMING_SWPRINTFS +#define _CRT_NON_CONFORMING_SWPRINTFS +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "strcode.h" +#include "nstring.h" +#include "typestrans.h" + +template std::wstring TypeToString (T value, const std::wstring &reserve = L"") { return std::to_wstring (value); } +template std::string TypeToString (T value, const std::string &reserve = "") { return std::to_string (value); } + +std::string GetPrivateProfileStringA (const std::string &filePath, const std::string §ion, const std::string &key, LPCSTR defaultValue = "") +{ + char buf [32768] = {0}; + GetPrivateProfileStringA (section.c_str (), key.c_str (), defaultValue, buf, 32767, filePath.c_str ()); + return buf; +} +std::wstring GetPrivateProfileStringW (const std::wstring &filePath, const std::wstring §ion, const std::wstring &key, LPCWSTR defaultValue = L"") +{ + WCHAR buf [32768] = {0}; + GetPrivateProfileStringW (section.c_str (), key.c_str (), defaultValue, buf, 32767, filePath.c_str ()); + return buf; +} +UINT GetPrivateProfileIntA (const std::string &filePath, const std::string §ion, const std::string &key, INT defaultValue = 0) +{ + return GetPrivateProfileIntA (section.c_str (), key.c_str (), defaultValue, filePath.c_str ()); +} +UINT GetPrivateProfileIntW (const std::wstring &filePath, const std::wstring §ion, const std::wstring &key, INT defaultValue = 0) +{ + return GetPrivateProfileIntW (section.c_str (), key.c_str (), defaultValue, filePath.c_str ()); +} +BOOL WritePrivateProfileStringA (const std::string &filePath, const std::string §ion, const std::string &key, const std::string &value) +{ + return WritePrivateProfileStringA (section.c_str (), key.c_str (), value.c_str (), filePath.c_str ()); +} +BOOL WritePrivateProfileStringW (const std::wstring &filePath, const std::wstring §ion, const std::wstring &key, const std::wstring &value) +{ + return WritePrivateProfileStringW ( section.c_str (), key.c_str (), value.c_str (), filePath.c_str ()); +} +size_t GetPrivateProfileSectionA (const std::string &filePath, const std::string §ion, std::vector &output) +{ + char buf [32768] = {0}; + DWORD len = GetPrivateProfileSectionA (section.c_str (), buf, sizeof (buf), filePath.c_str ()); + output.clear (); + if (len == 0) return 0; + char *ptr = buf; + while (*ptr) + { + output.emplace_back (ptr); + ptr += strlen (ptr) + 1; + } + return output.size (); +} +size_t GetPrivateProfileSectionW (const std::wstring &filePath, const std::wstring §ion, std::vector &output) +{ + WCHAR buf [32768] = {0}; + DWORD len = GetPrivateProfileSectionW (section.c_str (), buf, sizeof (buf) / sizeof (WCHAR), filePath.c_str ()); + output.clear (); + if (len == 0) return 0; + WCHAR *ptr = buf; + while (*ptr) + { + output.emplace_back (ptr); + ptr += wcslen (ptr) + 1; + } + return output.size (); +} +size_t GetPrivateProfileSectionNamesA (const std::string &filePath, std::vector &output) +{ + char buf [32768] = {0}; + DWORD len = GetPrivateProfileSectionNamesA (buf, sizeof (buf), filePath.c_str ()); + output.clear (); + if (len == 0) return 0; + char *ptr = buf; + while (*ptr) + { + output.emplace_back (ptr); + ptr += strlen (ptr) + 1; + } + return output.size (); +} +size_t GetPrivateProfileSectionNamesW (const std::wstring &filePath, std::vector &output) +{ + WCHAR buf [32768] = {0}; + DWORD len = GetPrivateProfileSectionNamesW (buf, sizeof (buf) / sizeof (WCHAR), filePath.c_str ()); + output.clear (); + if (len == 0) return 0; + WCHAR *ptr = buf; + while (*ptr) + { + output.emplace_back (ptr); + ptr += wcslen (ptr) + 1; + } + return output.size (); +} +bool WritePrivateProfileSectionA (const std::string &filePath, const std::string §ion, const std::vector &lines) +{ + std::string buf; + for (const auto &line : lines) buf.append (line).push_back ('\0'); + buf.push_back ('\0'); + return WritePrivateProfileSectionA (section.c_str (), buf.c_str (), filePath.c_str ()) != 0; +} +bool WritePrivateProfileSectionW (const std::wstring &filePath, const std::wstring §ion, const std::vector &lines) +{ + std::wstring buf; + for (const auto &line : lines) buf.append (line).push_back (L'\0'); + buf.push_back (L'\0'); // ˫ \0 β + return WritePrivateProfileSectionW (section.c_str (), buf.c_str (), filePath.c_str ()) != 0; +} +bool GetPrivateProfileStructA (const std::string &filePath, const std::string §ion, const std::string &key, void *output, UINT size) +{ + return GetPrivateProfileStructA (section.c_str (), key.c_str (), output, size, filePath.c_str ()) != 0; +} +bool WritePrivateProfileStructA (const std::string &filePath, const std::string §ion, const std::string &key, void *data, UINT size) +{ + return WritePrivateProfileStructA (section.c_str (), key.c_str (), data, size, filePath.c_str ()) != 0; +} +bool GetPrivateProfileStructW (const std::wstring &filePath, const std::wstring §ion, const std::wstring &key, void *output, UINT size) +{ + return GetPrivateProfileStructW (section.c_str (), key.c_str (), output, size, filePath.c_str ()) != 0; +} +bool WritePrivateProfileStructW (const std::wstring &filePath, const std::wstring §ion, const std::wstring &key, void *data, UINT size) +{ + return WritePrivateProfileStructW (section.c_str (), key.c_str (), data, size, filePath.c_str ()) != 0; +} +size_t GetPrivateProfileKeysW (const std::wstring &filePath, const std::wstring §ion, std::vector &keys) +{ + WCHAR buf [32768] = {0}; + DWORD len = GetPrivateProfileSectionW (section.c_str (), buf, sizeof (buf) / sizeof (WCHAR), filePath.c_str ()); + keys.clear (); + if (len == 0) return 0; + WCHAR* ptr = buf; + while (*ptr) + { + std::wstring line = ptr; + size_t pos = line.find (L'='); + if (pos != std::wstring::npos) keys.push_back (line.substr (0, pos)); + ptr += wcslen (ptr) + 1; + } + return keys.size (); +} +size_t GetPrivateProfileKeysA (const std::string &filePath, const std::string §ion, std::vector &keys) +{ + char buf [32768] = {0}; + DWORD len = GetPrivateProfileSectionA (section.c_str (), buf, sizeof (buf), filePath.c_str ()); + keys.clear (); + if (len == 0) + return 0; + char *ptr = buf; + while (*ptr) + { + std::string line = ptr; + size_t pos = line.find ('='); + if (pos != std::string::npos) keys.push_back (line.substr (0, pos)); + ptr += strlen (ptr) + 1; + } + return keys.size (); +} +bool DeletePrivateProfileKeyA (const std::string &filePath, const std::string §ion, const std::string &key) +{ + return WritePrivateProfileStringA (section.c_str (), key.c_str (), NULL, filePath.c_str ()) != FALSE; +} +bool DeletePrivateProfileKeyW (const std::wstring &filePath, const std::wstring §ion, const std::wstring &key) +{ + return WritePrivateProfileStringW (section.c_str (), key.c_str (), NULL, filePath.c_str ()) != FALSE; +} +bool DeletePrivateProfileSectionA (const std::string &filePath, const std::string §ion) +{ + return WritePrivateProfileStringA (section.c_str (), NULL, NULL, filePath.c_str ()) != FALSE; +} +bool DeletePrivateProfileSectionW (const std::wstring &filePath, const std::wstring §ion) +{ + return WritePrivateProfileStringW (section.c_str (), NULL, NULL, filePath.c_str ()) != FALSE; +} + +class initkey +{ + public: + using pstring = std::string &; + using pwstring = std::wstring &; + using pcstring = const std::string &; + using pcwstring = const std::wstring &; + private: + pcwstring filepath; + pcwstring section; + template T read_t (T defaultvalue, Func process) const + { + auto res = read_wstring (std::to_wstring ((Trans)defaultvalue)); + if (IsNormalizeStringEmpty (res)) return defaultvalue; + return (T)process (res.c_str ()); + } + template bool write_t (T value) { return write_string (std::to_wstring (value)); } + public: + std::wstring key; + initkey (pcwstring path, pcwstring sect, pcwstring k): filepath (path), section (sect), key (k) {} + std::wstring read_wstring (pcwstring defaultvalue = L"") const { return GetPrivateProfileStringW (filepath, section, key, defaultvalue.c_str ()); } + std::string read_string (pcstring defaultvalue = "") const { return WStringToString (read_wstring (StringToWString (defaultvalue))); } + short read_short (short defaultvalue = 0) const { return read_t (defaultvalue, _wtoi16); } + unsigned short read_ushort (unsigned short defaultvalue = 0) const { return read_t (defaultvalue, _wtoui16); } + int read_int (int defaultvalue = 0) const { return read_t (defaultvalue, _wtoi); } + unsigned int read_uint (unsigned int defaultvalue = 0) const { return read_t (defaultvalue, _wtou); } + long read_long (long defaultvalue = 0) const { return read_t (defaultvalue, _wtol); } + unsigned long read_ulong (unsigned long defaultvalue = 0) const { return read_t (defaultvalue, _wtoul); } + long long read_llong (long long defaultvalue = 0) const { return read_t (defaultvalue, _wtoll); } + unsigned long long read_ullong (unsigned long long defaultvalue = 0) const { return read_t (defaultvalue, _wtou64); } + float read_float (float defaultvalue = 0) const { return read_t (defaultvalue, _wtof); } + double read_double (double defaultvalue = 0) const { return read_t (defaultvalue, _wtod); } + bool read_bool (bool defaultvalue = false) const + { + std::wnstring res = read_wstring (defaultvalue ? L"true" : L"false"); + if (res.empty ()) return defaultvalue; + if (res.equals (L"true") || res.equals (L"yes") || res.equals (L"zhen") || res.equals (L"") || res.equals (L"1") || _wtoi (res.c_str ()) != 0) return true; + else if (res.equals (L"false") || res.equals (L"no") || res.equals (L"jia") || res.equals (L"") || res.equals (L"0")) return false; + else return defaultvalue; + } + int8_t read_i8 (int8_t defaultvalue = 0) const { return read_t (defaultvalue, _wtoi8); } + uint8_t read_u8 (uint8_t defaultvalue = 0) const { return read_t (defaultvalue, _wtoui8); } + int16_t read_i16 (int16_t defaultvalue = 0) const { return read_t (defaultvalue, _wtoi16); } + uint16_t read_u16 (uint16_t defaultvalue = 0) const { return read_t (defaultvalue, _wtoui16); } + int32_t read_i32 (int32_t defaultvalue = 0) const { return read_t (defaultvalue, _wtoi32); } + uint32_t read_u32 (uint32_t defaultvalue = 0) const { return read_t (defaultvalue, _wtoui32); } + int64_t read_i64 (int64_t defaultvalue = 0) const { return read_t (defaultvalue, _wtoi64); } + uint64_t read_u64 (uint64_t defaultvalue = 0) const { return read_t (defaultvalue, _wtou64); } + bool read_struct (void *output, size_t size) const { return GetPrivateProfileStructW (filepath, section, key, output, size); } + template bool read_struct (T &structinst) const { return read_struct (&structinst, sizeof (structinst)); } + bool write_string (pcwstring value) { return write (value); } + bool write_string (pcstring value) { return write (value); } + bool write (pcwstring value) { return WritePrivateProfileStringW (filepath, section, key, value); } + bool write (pcstring value) { return write (StringToWString (value)); } + bool write (int value) { return write_t (value); } + bool write (unsigned int value) { return write_t (value); } + bool write (short value) { return write_t (value); } + bool write (unsigned short value) { return write_t (value); } + bool write (long value) { return write_t (value); } + bool write (unsigned long value) { return write_t (value); } + bool write (long long value) { return write_t (value); } + bool write (unsigned long long value) { return write_t (value); } + bool write (int8_t value) { return write_t ((int16_t)value); } + bool write (uint8_t value) { return write_t ((uint16_t)value); } + bool write (float value) { return write_t (value); } + bool write (double value) { return write_t (value); } + bool write (bool value) { return write (value ? L"true" : L"false"); } + bool write (void *buf, size_t bufsize) { return WritePrivateProfileStructW (filepath, section, key, buf, bufsize); } + operator std::wstring () { return read_wstring (); } + operator std::string () { return read_string (); } + template initkey &operator = (T value) { write (value); return *this; } + initkey &operator = (const initkey &) = delete; + // ɾ + bool clear () { return DeletePrivateProfileKeyW (filepath, section, key); } + bool empty () const { return read_wstring ().empty (); } +#define OPERATOR_TYPE_READ(_type_, _method_) \ +operator _type_ () { return _method_ (); } + OPERATOR_TYPE_READ (int, read_int) + OPERATOR_TYPE_READ (unsigned int, read_uint) + OPERATOR_TYPE_READ (long, read_long) + OPERATOR_TYPE_READ (unsigned long, read_ulong) + OPERATOR_TYPE_READ (long long, read_llong) + OPERATOR_TYPE_READ (unsigned long long, read_ullong) + OPERATOR_TYPE_READ (short, read_short) + OPERATOR_TYPE_READ (unsigned short, read_ushort) + OPERATOR_TYPE_READ (float, read_float) + OPERATOR_TYPE_READ (double, read_double) + OPERATOR_TYPE_READ (bool, read_bool) +#ifdef OPERATOR_TYPE_READ +#undef OPERATOR_TYPE_READ +#endif +}; +class initsection +{ + private: + const std::wstring &filepath; + template T read_t (const std::basic_string &key, T defaultvalue, Func process) const + { + std::basic_string temp; + auto res = read_string (key, TypeToString ((Trans)defaultvalue, temp)); + if (IsNormalizeStringEmpty (res)) return defaultvalue; + return (T)process (res.c_str ()); + } + template bool write_t (const std::basic_string &key, T value) + { + std::basic_string temp; + return write_string (key, TypeToString (value, temp)); + } + public: + using pcstring = const std::string &; + using pcwstring = const std::wstring &; + std::wstring section; + initsection (const std::wstring &path, const std::wstring §): filepath (path), section (sect) {} + size_t keys (std::vector &output) const { return GetPrivateProfileKeysW (filepath, section, output); } + size_t keys (std::vector &output) const { return GetPrivateProfileKeysA (WStringToString (filepath), WStringToString (section), output); } + bool key_values (const std::vector &lines) { return WritePrivateProfileSectionW (filepath, section, lines); } + bool key_values (const std::vector &lines) { return WritePrivateProfileSectionA (WStringToString (filepath), WStringToString (section), lines); } + size_t key_values (std::vector &output) const { return GetPrivateProfileSectionW (filepath, section, output); } + size_t key_values (std::vector &output) const { return GetPrivateProfileSectionA (WStringToString (filepath), WStringToString (section), output); } + std::wstring read_string (const std::wstring &key, const std::wstring &defaultvalue = L"") const { return GetPrivateProfileStringW (filepath, section, key, defaultvalue.c_str ()); } + std::string read_string (const std::string &key, const std::string &defaultvalue = "") const { return WStringToString (read_string (StringToWString (key), StringToWString (defaultvalue))); } + std::wstring read_wstring (const std::wstring &key, const std::wstring &defaultvalue = L"") const { return read_string (key, defaultvalue); } + int read_int (const std::wstring &key, int defaultvalue = 0) const { return read_t (key, defaultvalue, _wtoi); } + int read_int (const std::string &key, int defaultvalue = 0) const { return read_t (key, defaultvalue, atoi); } + unsigned int read_uint (const std::wstring &key, unsigned int defaultvalue = 0) const { return read_t (key, defaultvalue, _wtou); } + unsigned int read_uint (const std::string &key, unsigned int defaultvalue = 0) const { return read_t (key, defaultvalue, atou); } + long read_long (const std::wstring &key, long defaultvalue = 0) const { return read_t (key, defaultvalue, _wtol); } + long read_long (const std::string &key, long defaultvalue = 0) const { return read_t (key, defaultvalue, atol); } + unsigned long read_ulong (const std::wstring &key, unsigned long defaultvalue = 0) const { return read_t (key, defaultvalue, _wtoul); } + unsigned long read_ulong (const std::string &key, unsigned long defaultvalue = 0) const { return read_t (key, defaultvalue, atoul); } + long long read_llong (const std::wstring &key, long long defaultvalue = 0) const { return read_t (key, defaultvalue, _wtoll); } + long long read_llong (const std::string &key, long long defaultvalue = 0) const { return read_t (key, defaultvalue, atoll); } + unsigned long long read_ullong (const std::wstring &key, unsigned long long defaultvalue = 0) const { return read_t (key, defaultvalue, _wtou64); } + unsigned long long read_ullong (const std::string &key, unsigned long long defaultvalue = 0) const { return read_t (key, defaultvalue, atou64); } + int8_t read_i8 (const std::wstring &key, int8_t defaultvalue = 0) const { return read_t (key, defaultvalue, _wtoi8); } + int8_t read_i8 (const std::string &key, int8_t defaultvalue = 0) const { return read_t (key, defaultvalue, atoi8); } + uint8_t read_u8 (const std::wstring &key, uint8_t defaultvalue = 0) const { return read_t (key, defaultvalue, _wtoui8); } + uint8_t read_u8 (const std::string &key, uint8_t defaultvalue = 0) const { return read_t (key, defaultvalue, atoui8); } + int16_t read_i16 (const std::wstring &key, int16_t defaultvalue = 0) const { return read_t (key, defaultvalue, _wtoi16); } + int16_t read_i16 (const std::string &key, int16_t defaultvalue = 0) const { return read_t (key, defaultvalue, atoi16); } + short read_short (pcwstring key, short defaultvalue = 0) const { return read_i16 (key, defaultvalue); } + short read_short (pcstring key, short defaultvalue = 0) const { return read_i16 (key, defaultvalue); } + unsigned short read_ushort (pcwstring key, unsigned short defaultvalue = 0) const { return read_u16 (key, defaultvalue); } + unsigned short read_ushort (pcstring key, unsigned short defaultvalue = 0) const { return read_u16 (key, defaultvalue); } + uint16_t read_u16 (const std::wstring &key, uint16_t defaultvalue = 0) const { return read_t (key, defaultvalue, _wtoui16); } + uint16_t read_u16 (const std::string &key, uint16_t defaultvalue = 0) const { return read_t (key, defaultvalue, atoui16); } + int32_t read_i32 (const std::wstring &key, int32_t defaultvalue = 0) const { return read_t (key, defaultvalue, _wtoi32); } + int32_t read_i32 (const std::string &key, int32_t defaultvalue = 0) const { return read_t (key, defaultvalue, atoi32); } + uint32_t read_u32 (const std::wstring &key, uint32_t defaultvalue = 0) const { return read_t (key, defaultvalue, _wtoui32); } + uint32_t read_u32 (const std::string &key, uint32_t defaultvalue = 0) const { return read_t (key, defaultvalue, atoui32); } + int64_t read_i64 (const std::wstring &key, int64_t defaultvalue = 0) const { return read_t (key, defaultvalue, _wtoi64); } + int64_t read_i64 (const std::string &key, int64_t defaultvalue = 0) const { return read_t (key, defaultvalue, atoi64); } + uint64_t read_u64 (const std::wstring &key, uint64_t defaultvalue = 0) const { return read_ullong (key, defaultvalue); } + uint64_t read_u64 (const std::string &key, uint64_t defaultvalue = 0) const { return read_ullong (key, defaultvalue); } + float read_float (const std::wstring &key, float defaultvalue = 0) const { return read_t (key, defaultvalue, _wtof); } + double read_double (const std::wstring &key, double defaultvalue = 0) const { return read_t (key, defaultvalue, _wtod); } + bool read_bool (const std::wstring &key, bool defaultvalue = false) const + { + std::wnstring res = read_string (key, defaultvalue ? L"true" : L"false"); + if (res.empty ()) return defaultvalue; + if (res.equals (L"true") || res.equals (L"yes") || res.equals (L"zhen") || res.equals (L"") || res.equals (L"1") || _wtoi (res.c_str ()) != 0) return true; + else if (res.equals (L"false") || res.equals (L"no") || res.equals (L"jia") || res.equals (L"") || res.equals (L"0")) return false; + else return defaultvalue; + } + bool read_bool (const std::string &key, bool defaultvalue = false) const { return read_bool (StringToWString (key), defaultvalue); } + bool read_struct (const std::wstring &key, void *output, size_t size) const { return GetPrivateProfileStructW (filepath, section, key, output, size); } + template bool read_struct (const std::wstring &key, T &structinst) const { return read_struct (key, &structinst, sizeof (structinst)); } + bool write_string (const std::wstring &key, const std::wstring &value) { return WritePrivateProfileStringW (filepath, section, key, value); } + bool write_string (const std::string &key, const std::string &value) { return write_string (StringToWString (key), StringToWString (value)); } + bool write (const std::wstring &key, const std::wstring &value) { return write_string (key, value); } + bool write (const std::string &key, const std::string &value) { return write_string (key, value); } + bool write (pcwstring key, short value) { return write_t (key, value); } + bool write (pcwstring key, unsigned short value) { return write_t (key, value); } + bool write (pcwstring key, int value) { return write_t (key, value); } + bool write (pcwstring key, unsigned int value) { return write_t (key, value); } + bool write (pcwstring key, long value) { return write_t (key, value); } + bool write (pcwstring key, unsigned long value) { return write_t (key, value); } + bool write (pcwstring key, long long value) { return write_t (key, value); } + bool write (pcwstring key, unsigned long long value) { return write_t (key, value); } + bool write (pcwstring key, int8_t value) { return write_t (key, (int16_t)value); } + bool write (pcwstring key, uint8_t value) { return write_t (key, (uint16_t)value); } + bool write (pcwstring key, bool value) { return write (key, value ? L"true" : L"false"); } + bool write (pcstring key, short value) { return write_t (key, value); } + bool write (pcstring key, unsigned short value) { return write_t (key, value); } + bool write (pcstring key, int value) { return write_t (key, value); } + bool write (pcstring key, unsigned int value) { return write_t (key, value); } + bool write (pcstring key, long value) { return write_t (key, value); } + bool write (pcstring key, unsigned long value) { return write_t (key, value); } + bool write (pcstring key, long long value) { return write_t (key, value); } + bool write (pcstring key, unsigned long long value) { return write_t (key, value); } + bool write (pcstring key, int8_t value) { return write_t (key, (int16_t)value); } + bool write (pcstring key, uint8_t value) { return write_t (key, (uint16_t)value); } + bool write (pcstring key, bool value) { return write (StringToWString (key), value ? L"true" : L"false"); } + bool write (pcwstring key, void *buf, size_t bufsize) { return WritePrivateProfileStructW (filepath, section, key, buf, bufsize); } + bool write (pcstring key, void *buf, size_t bufsize) { return write (StringToWString (key), buf, bufsize); } + initkey operator [] (pcwstring key) { return initkey (filepath, section, key); } + initkey operator [] (pcstring key) { return initkey (filepath, section, StringToWString (key)); } + std::wstring operator [] (pcwstring key) const { return read_string (key); } + std::wstring operator [] (pcstring key) const { return read_string (StringToWString (key)); } + bool delete_key (pcwstring key) { return DeletePrivateProfileKeyW (filepath, section, key); } + bool delete_key (pcstring key) { return delete_key (StringToWString (key)); } + bool clear () { return DeletePrivateProfileSectionW (filepath, section); } + initkey get_key (pcwstring key) const { return initkey (filepath, section, key); } + initkey get_key (pcwstring key) { return initkey (filepath, section, key); } + initkey get_key (pcstring key) const { return initkey (filepath, section, StringToWString (key)); } + initkey get_key (pcstring key) { return initkey (filepath, section, StringToWString (key)); } + initsection &operator = (const initsection &) = delete; +}; +class initfile +{ + public: + using pstring = std::string &; + using pwstring = std::wstring &; + using pcstring = const std::string &; + using pcwstring = const std::wstring &; + std::wstring filepath; + private: + template T read_t (pcwstring section, pcwstring key, T defaultvalue, FN process) const + { + auto res = read_wstring (section, key, std::to_wstring ((TRANS)defaultvalue)); + if (IsNormalizeStringEmpty (res)) return defaultvalue; + return (T)process (res.c_str ()); + } + template bool write_t (pcwstring section, pcwstring key, T value) { return write (section, key, std::to_wstring (value)); } + public: + initfile (const std::wstring &initpath): filepath (initpath) {} + size_t sections (std::vector §) const { return GetPrivateProfileSectionNamesW (filepath, sect); } + size_t sections (std::vector §) const { return GetPrivateProfileSectionNamesA (WStringToString (filepath), sect); } + bool delete_section (pcwstring section) { return DeletePrivateProfileSectionW (filepath, section); } + bool delete_section (pcstring section) { return delete_section (StringToWString (section)); } + size_t key_values (pcwstring section, std::vector &keyvalues) const { return GetPrivateProfileSectionW (filepath, section, keyvalues); } + size_t key_values (pcstring section, std::vector &keyvalues) const { return GetPrivateProfileSectionA (WStringToString (filepath), section, keyvalues); } + size_t keys (pcwstring section, std::vector &keyvalues) const { return GetPrivateProfileKeysW (filepath, section, keyvalues); } + size_t keys (pcstring section, std::vector &keyvalues) const { return GetPrivateProfileKeysA (WStringToString (filepath), section, keyvalues); } + initsection get_section (pcwstring section) { return initsection (filepath, section); } + initsection get_section (pcwstring section) const { return initsection (filepath, section); } + std::wstring read_wstring (pcwstring section, pcwstring key, pcwstring dflt = L"") const { return GetPrivateProfileStringW (filepath, section, key, dflt.c_str ()); } + std::string read_string (pcwstring section, pcwstring key, pcstring dflt = "") const { return WStringToString (read_wstring (section, key, StringToWString (dflt))); } +#define INIT_READ_WARGS(_type_, _dfltvalue_) pcwstring section, pcwstring key, _type_ dflt = _dfltvalue_ +#define METHOD_INIT_READ(_type_, _typename_, _dfltvalue_, _process_) \ +_type_ read_##_typename_ (INIT_READ_WARGS (_type_, _dfltvalue_)) const { return read_t (section, key, dflt, _process_); } + METHOD_INIT_READ (int, int, 0, _wtoi) + METHOD_INIT_READ (unsigned int, uint, 0, _wtou) + METHOD_INIT_READ (long, long, 0, _wtol) + METHOD_INIT_READ (unsigned long, ulong, 0, _wtoul) + METHOD_INIT_READ (long long, llong, 0, _wtoll) + METHOD_INIT_READ (unsigned long, ullong, 0, _wtou64) + METHOD_INIT_READ (short, short, 0, _wtoi16) + METHOD_INIT_READ (unsigned short, ushort, 0, _wtoui16) + METHOD_INIT_READ (int16_t, i16, 0, _wtoi16) + METHOD_INIT_READ (uint16_t, u16, 0, _wtoui16) + METHOD_INIT_READ (int32_t, i32, 0, _wtoi32) + METHOD_INIT_READ (uint32_t, u32, 0, _wtoui32) + METHOD_INIT_READ (int64_t, i64, 0, _wtoi64) + METHOD_INIT_READ (uint64_t, u64, 0, _wtou64) + METHOD_INIT_READ (float, float , 0, _wtof) + METHOD_INIT_READ (double, double, 0, _wtod) + int8_t read_i8 (INIT_READ_WARGS (int8_t, 0)) const { return read_t (section, key, dflt, _wtoi8); } + uint8_t read_u8 (INIT_READ_WARGS (uint8_t, 0)) const { return read_t (section, key, dflt, _wtoui8); } + bool read_bool (INIT_READ_WARGS (bool, false)) const + { + std::wnstring res = read_wstring (section, key, dflt ? L"true" : L"false"); + if (res.empty ()) return dflt; + if (res.equals (L"true") || res.equals (L"yes") || res.equals (L"zhen") || res.equals (L"") || res.equals (L"1") || _wtoi (res.c_str ()) != 0) return true; + else if (res.equals (L"false") || res.equals (L"no") || res.equals (L"jia") || res.equals (L"") || res.equals (L"0")) return false; + else return dflt; + } + bool read_struct (pcwstring section, pcwstring key, void *output, size_t size) const { return GetPrivateProfileStructW (filepath, section, key, output, size); } + template bool read_struct (pcwstring section, pcwstring key, T &structinst) const { return read_struct (key, &structinst, sizeof (structinst)); } +#ifdef INIT_READ_WARGS +#undef INIT_READ_WARGS +#endif +#ifdef METHOD_INIT_READ +#undef METHOD_INIT_READ +#endif +#define INIT_WRITE_WARGS(_type_) pcwstring section, pcwstring key, _type_ value + bool write (INIT_WRITE_WARGS (pcwstring)) { return WritePrivateProfileStringW (filepath, section, key, value); } +#define METHOD_INIT_WRITE(_type_) \ +bool write (INIT_WRITE_WARGS (_type_)) { return write_t (section, key, value); } + METHOD_INIT_WRITE (short) + METHOD_INIT_WRITE (unsigned short) + METHOD_INIT_WRITE (int) + METHOD_INIT_WRITE (unsigned int) + METHOD_INIT_WRITE (long) + METHOD_INIT_WRITE (unsigned long) + METHOD_INIT_WRITE (long long) + METHOD_INIT_WRITE (unsigned long long) + METHOD_INIT_WRITE (float) + METHOD_INIT_WRITE (double) + bool write (INIT_WRITE_WARGS (bool)) { return write (section, key, value ? L"true" : L"false"); } + bool write (INIT_WRITE_WARGS (int8_t)) { return write_t (section, key, (int16_t)value); } + bool write (INIT_WRITE_WARGS (uint8_t)) { return write_t (section, key, (uint16_t)value); } + bool write (pcwstring section, pcwstring key, void *buf, size_t bufsize) { return WritePrivateProfileStructW (filepath, section, key, buf, bufsize); } + initsection operator [] (pcwstring section) { return initsection (filepath, section); } + initsection operator [] (pcstring section) { return initsection (filepath, StringToWString (section)); } + initsection operator [] (pcwstring section) const { return initsection (filepath, section); } + initsection operator [] (pcstring section) const { return initsection (filepath, StringToWString (section)); } +#ifdef METHOD_INIT_WRITE +#undef METHOD_INIT_WRITE +#endif +#ifdef INIT_WRITE_WARGS +#undef INIT_WRITE_WARGS +#endif +}; \ No newline at end of file diff --git a/appinstaller/main.cpp b/appinstaller/main.cpp new file mode 100644 index 0000000..42a8b40 --- /dev/null +++ b/appinstaller/main.cpp @@ -0,0 +1,595 @@ +#include +#include +#include +#include +#include +#include +#include +#include "cmdargs.h" +#include "themeinfo.h" +#include "mpstr.h" +#include "initfile.h" +#include "resource.h" +#include "vemani.h" +#include "ieshell.h" +using namespace System; +using namespace System::Runtime::InteropServices; + +#ifdef _DEBUG +#define DEBUGMODE true +#else +#define DEBUGMODE false +#endif + +LPCWSTR g_lpAppId = L"Microsoft.DesktopAppInstaller"; +auto &g_identity = g_lpAppId; +auto &m_idenName = g_lpAppId; +struct iconhandle +{ + HICON hIcon = nullptr; + iconhandle (HICON hIcon = nullptr): hIcon (hIcon) {} + ~iconhandle () { try { if (hIcon) DestroyIcon (hIcon); hIcon = nullptr; } catch (...) {} } +}; +iconhandle g_hIconMain (LoadRCIcon (IDI_ICON_MAIN)); +initfile g_initfile (CombinePath (GetProgramRootDirectoryW (), L"config.ini")); +ref class MainHtmlWnd; +msclr::gcroot g_mainwnd; +vemanifest g_vemani ( + IsFileExists (CombinePath (GetProgramRootDirectoryW (), L"VisualElementsManifest.xml")) ? + CombinePath (GetProgramRootDirectoryW (), L"VisualElementsManifest.xml") : + CombinePath (GetProgramRootDirectoryW (), L"AppInstaller.VisualElementsManifest.xml") +); +resxmldoc g_scaleres ( + IsFileExists (CombinePath (GetProgramRootDirectoryW (), L"VisualElements\\scale.xml")) ? + CombinePath (GetProgramRootDirectoryW (), L"VisualElements\\scale.xml") : + CombinePath (GetProgramRootDirectoryW (), L"VisualElementsManifest.xml") +); + +HRESULT GetWebBrowser2Interface (System::Windows::Forms::WebBrowser ^fwb, IWebBrowser2 **output) +{ + if (fwb == nullptr || output == nullptr) return E_INVALIDARG; + *output = nullptr; + Object ^activeX = fwb->ActiveXInstance; + if (activeX == nullptr) return E_FAIL; + IntPtr pUnk = Marshal::GetIUnknownForObject (activeX); + if (pUnk == IntPtr::Zero) return E_FAIL; + HRESULT hr = ((IUnknown *)pUnk.ToPointer ())->QueryInterface (IID_IWebBrowser2, (void **)output); + Marshal::Release (pUnk); + return hr; +} + +public ref class SplashForm: public System::Windows::Forms::Form +{ + public: + using PictureBox = System::Windows::Forms::PictureBox; + using Timer = System::Windows::Forms::Timer; + private: + PictureBox ^picbox; + Timer ^timer; + double opastep = 0.05; + void InitForm () + { + this->DoubleBuffered = true; + InitializeComponent (); + this->FormBorderStyle = System::Windows::Forms::FormBorderStyle::None; + this->StartPosition = System::Windows::Forms::FormStartPosition::Manual; + this->ShowInTaskbar = false; + this->AllowTransparency = true; + this->Opacity = 1.0; + } + void InitializeComponent () + { + double dDpi = GetDPI () * 0.01; + this->picbox = gcnew System::Windows::Forms::PictureBox (); + this->picbox->Size = System::Drawing::Size (620 * dDpi, 300 * dDpi); + this->picbox->BackColor = System::Drawing::Color::Transparent; + picbox->Anchor = System::Windows::Forms::AnchorStyles::None; + picbox->SizeMode = System::Windows::Forms::PictureBoxSizeMode::Zoom; + } + void OnFadeTimer (Object ^sender, EventArgs ^e) + { + auto fadeTimer = timer; + auto opacityStep = opastep; + if (this->Opacity > 0) + { + this->Opacity -= opacityStep; + } + else + { + fadeTimer->Stop (); + this->Close (); + } + } + void OnLoad (Object ^sender, EventArgs ^e) + { + this->ChangePosAndSize (); + this->Visible = true; + } + void OnResize (Object ^sender, EventArgs ^e) + { + if (IsHandleCreated && picbox->IsHandleCreated) + { + Drawing::Size sz = this->ClientSize; + this->picbox->Location = Drawing::Point ( + (sz.Width - picbox->Width) * 0.5, + (sz.Height - picbox->Height) * 0.5 + ); + } + } + void OnResizeOwner (Object ^sender, EventArgs ^e) { this->ChangePosAndSize (); } + void OnLocationChangedOwner (Object ^sender, EventArgs ^e) { this->ChangePosAndSize (); } + protected: + virtual void OnHandleCreated (EventArgs^ e) override + { + Form::OnHandleCreated (e); + if (Environment::OSVersion->Version->Major >= 6) + { + INT mr = 0; + MARGINS margins = {mr, mr, mr, mr}; + HRESULT hr = DwmExtendFrameIntoClientArea ((HWND)this->Handle.ToPointer (), &margins); + } + } + public: + SplashForm (System::String ^imgpath, System::Drawing::Color backcolor, System::Windows::Forms::Form ^owner) + { + if (owner != nullptr) this->Owner = owner; + InitForm (); + std::wstring filefullpath = MPStringToStdW (imgpath); + if (filefullpath.find (L'%') != filefullpath.npos) filefullpath = ProcessEnvVars (filefullpath); + filefullpath = GetFullPathName (imgpath ? MPStringToStdW (imgpath) : L""); + try + { + auto img = System::Drawing::Image::FromFile (gcnew System::String (filefullpath.c_str ())); + if (img != nullptr) picbox->Image = img; + } + catch (...) { } + if (backcolor != Drawing::Color::Transparent) + { + picbox->BackColor = backcolor; + this->BackColor = backcolor; + } + if (this->Owner != nullptr) + { + this->Owner->Resize += gcnew System::EventHandler (this, &SplashForm::OnResizeOwner); + this->Owner->LocationChanged += gcnew System::EventHandler (this, &SplashForm::OnLocationChangedOwner); + } + this->Controls->Add (picbox); + this->Resize += gcnew EventHandler (this, &SplashForm::OnResize); + timer = gcnew System::Windows::Forms::Timer (); + timer->Interval = 15; + timer->Tick += gcnew System::EventHandler (this, &SplashForm::OnFadeTimer); + this->Load += gcnew EventHandler (this, &SplashForm::OnLoad); + } + void ChangePosAndSize () + { + if (this->Owner && this->Owner->IsHandleCreated) + { + this->Owner->Update (); + System::Drawing::Point pt = this->Owner->PointToScreen (this->Owner->ClientRectangle.Location); + this->Location = pt; + this->Size = this->Owner->ClientSize; + } + else if (this->Parent && this->Parent->IsHandleCreated) + { + this->Parent->Update (); + System::Drawing::Point pt = this->Parent->PointToScreen (this->Parent->ClientRectangle.Location); + this->Location = pt; + this->Size = this->Parent->ClientSize; + } + if (IsHandleCreated && picbox->IsHandleCreated) + { + Drawing::Size sz = this->ClientSize; + this->picbox->Location = Drawing::Point ( + (sz.Width - picbox->Width) * 0.5, + (sz.Height - picbox->Height) * 0.5 + ); + } + } + void SetSplashImage (System::Drawing::Image ^img) { if (picbox) picbox->Image = img; } + void SetSplashImage (System::String ^imgpath) { try { SetSplashImage (System::Drawing::Image::FromFile (imgpath)); } catch (...) {} } + void SetSplashImage (const std::wstring &imgpath) { SetSplashImage (CStringToMPString (imgpath)); } + void SetSplashBackgroundColor (System::Drawing::Color color) { picbox->BackColor = color; this->BackColor = color; } + // ʧ + void FadeOut () { timer->Start (); } + // ʧ + void FadeAway () { this->Visible = false; this->Close (); } + ~SplashForm () + { + if (this->Owner != nullptr) + { + this->Owner->Resize -= gcnew System::EventHandler (this, &SplashForm::OnResizeOwner); + this->Owner->LocationChanged -= gcnew System::EventHandler (this, &SplashForm::OnLocationChangedOwner); + } + } +}; + +[ComVisible (true)] +public ref class MainHtmlWnd: public System::Windows::Forms::Form +{ + public: + using WebBrowser = System::Windows::Forms::WebBrowser; + private: + WebBrowser ^webui; + SplashForm ^splash; + public: + [ComVisible (true)] + ref class IBridge + { + private: + MainHtmlWnd ^wndinst = nullptr; + public: + IBridge (MainHtmlWnd ^wnd): wndinst (wnd) {} + ref class _I_System + { + private: + MainHtmlWnd ^wndinst = nullptr; + public: + ref class _I_UI + { + private: + MainHtmlWnd ^wndinst = nullptr; + public: + _I_UI (MainHtmlWnd ^wnd): wndinst (wnd) {} + property int DPIPercent { int get () { return GetDPI (); }} + property double DPI { double get () { return DPIPercent * 0.01; }} + void showSplash () { wndinst->SplashScreen->Show (); } + void fadeAwaySplash () { wndinst->SplashScreen->FadeAway (); } + void fadeOutSplash () { wndinst->SplashScreen->FadeOut (); } + }; + private: + _I_UI ^ui = gcnew _I_UI (wndinst); + public: + _I_System (MainHtmlWnd ^wnd): wndinst (wnd) {} + property _I_UI ^UI { _I_UI ^get () { return ui; } } + }; + ref class _I_IEFrame + { + private: + MainHtmlWnd ^wndinst = nullptr; + public: + _I_IEFrame (MainHtmlWnd ^wnd): wndinst (wnd) {} + property int Scale + { + int get () { return wndinst->PageScale; } + void set (int value) { return wndinst->PageScale = value; } + } + property int Version { int get () { return GetInternetExplorerVersionMajor (); }} + }; + ref class _I_Storage + { + private: + MainHtmlWnd ^wndinst = nullptr; + public: + _I_Storage (MainHtmlWnd ^wnd): wndinst (wnd) {} + ref class Path + { + + }; + }; + ref class _I_String + { + public: + ref class _I_NString + { + public: + bool NEquals (String ^l, String ^r) { return IsNormalizeStringEquals (MPStringToPtrW (l), MPStringToPtrW (r)); } + bool Empty (String ^l) { return IsNormalizeStringEmpty (MPStringToStdW (l)); } + int Compare (String ^l, String ^r) { return NormalizeStringCompare (MPStringToPtrW (l), MPStringToPtrW (r)); } + int Length (String ^l) { return GetNormalizeStringLength (MPStringToStdW (l)); } + }; + private: + _I_NString ^nstr = gcnew _I_NString (); + public: + property _I_NString ^NString { _I_NString ^get () { return nstr; }} + String ^Trim (String ^src) + { + std::wstring csrc = MPStringToStdW (src); + return CStringToMPString (::StringTrim (csrc)); + } + String ^ToLower (String ^src) { return CStringToMPString (StringToLower (MPStringToStdW (src))); } + String ^ToUpper (String ^src) { return CStringToMPString (StringToUpper (MPStringToStdW (src))); } + }; + private: + _I_System ^system = gcnew _I_System (wndinst); + _I_IEFrame ^ieframe = gcnew _I_IEFrame (wndinst); + _I_Storage ^storage = gcnew _I_Storage (wndinst); + _I_String ^str = gcnew _I_String (); + public: + property _I_System ^System { _I_System ^get () { return system; }} + property _I_IEFrame ^IEFrame { _I_IEFrame ^get () { return ieframe; }} + property _I_Storage ^Storage { _I_Storage ^get () { return storage; }} + property _I_String ^String { _I_String ^get () { return str; }} + }; + protected: + property WebBrowser ^WebUI { WebBrowser ^get () { return this->webui; } } + property SplashForm ^SplashScreen { SplashForm ^get () { return this->splash; } } + property int DPIPercent { int get () { return GetDPI (); }} + property double DPI { double get () { return DPIPercent * 0.01; }} + void Init () + { + this->Visible = false; + this->webui = gcnew System::Windows::Forms::WebBrowser (); + this->SuspendLayout (); + this->webui->Dock = System::Windows::Forms::DockStyle::Fill; + this->webui->IsWebBrowserContextMenuEnabled = DEBUGMODE; + this->webui->AllowWebBrowserDrop = false; + this->Controls->Add (this->webui); + if (g_hIconMain.hIcon) + { + try { this->Icon = System::Drawing::Icon::FromHandle (IntPtr (g_hIconMain.hIcon)); } + catch (...) {} + } + unsigned ww = 0, wh = 0; + auto &ini = g_initfile; + auto setsect = ini ["Settings"]; + if (setsect [L"SavePosAndSizeBeforeCancel"].read_bool ()) + { + ww = setsect [L"LastWidth"].read_uint (setsect [L"DefaultWidth"].read_uint (rcInt (IDS_DEFAULTWIDTH))); + wh = setsect [L"LastHeight"].read_uint (setsect [L"DefaultHeight"].read_uint (rcInt (IDS_DEFAULTHEIGHT))); + } + else + { + ww = setsect [L"DefaultWidth"].read_uint (rcInt (IDS_DEFAULTWIDTH)); + wh = setsect [L"DefaultHeight"].read_uint (rcInt (IDS_DEFAULTHEIGHT)); + } + this->MinimumSize = System::Drawing::Size ( + setsect [L"MinimumWidth"].read_uint (rcInt (IDS_MINWIDTH)) * DPI, + setsect [L"MinimumHeight"].read_uint (rcInt (IDS_MINHIEHGT)) * DPI + ); + this->ClientSize = System::Drawing::Size (ww * DPI, wh * DPI); + this->WindowState = (System::Windows::Forms::FormWindowState)setsect [L"LastWndState"].read_int ((int)System::Windows::Forms::FormWindowState::Normal); + this->Text = rcString (IDS_WINTITLE); + this->ResumeLayout (false); + webui->ObjectForScripting = gcnew IBridge (this); + this->webui->DocumentCompleted += gcnew System::Windows::Forms::WebBrowserDocumentCompletedEventHandler (this, &MainHtmlWnd::OnDocumentCompleted); + this->webui->PreviewKeyDown += gcnew System::Windows::Forms::PreviewKeyDownEventHandler (this, &MainHtmlWnd::OnPreviewKeyDown_WebBrowser); + this->Resize += gcnew System::EventHandler (this, &MainHtmlWnd::OnResize); + this->Load += gcnew EventHandler (this, &MainHtmlWnd::OnCreate); + this->ResizeEnd += gcnew EventHandler (this, &MainHtmlWnd::OnResizeEnd); + } + void OnDocumentCompleted (Object ^sender, System::Windows::Forms::WebBrowserDocumentCompletedEventArgs ^e) + { + if (e->Url->ToString () == webui->Url->ToString ()) + { + + ExecScript ("Windows.UI.DPI.mode = 1"); + ExecScript ("Bridge.Frame.scale = Bridge.Frame.scale * Bridge.UI.dpi"); + splash->FadeOut (); + } + } + void OnCreate (System::Object ^sender, System::EventArgs ^e) + { + splash->Owner = this; + splash->ChangePosAndSize (); + splash->Show (); + splash->Update (); + webui->Navigate (CStringToMPString (CombinePath (GetProgramRootDirectoryW (), L"html\\install.html"))); + } + void OnPreviewKeyDown_WebBrowser (System::Object ^sender, System::Windows::Forms::PreviewKeyDownEventArgs ^e) + { + if (e->KeyCode == System::Windows::Forms::Keys::F5 || (e->KeyCode == System::Windows::Forms::Keys::R && e->Control)) + e->IsInputKey = true; + } + void OnResize (Object ^sender, EventArgs ^e) + { + } + void OnResizeEnd (Object ^sender, EventArgs ^e) + { + + } + + + public: + MainHtmlWnd () + { + splash = gcnew SplashForm ( + gcnew String (g_vemani.splash_screen_image (L"App").c_str ()), + StringToColor (gcnew String (g_vemani.splash_screen_backgroundcolor (L"App").c_str ())), + this + ); + System::Windows::Forms::Application::DoEvents (); + Init (); + } + Object ^CallScriptFunction (String ^lpFuncName, ... array ^alpParams) + { + try { return this->webui->Document->InvokeScript (lpFuncName, alpParams); } + catch (Exception ^e) {} + return nullptr; + } + Object ^CallScriptFunction (String ^lpScriptName) + { + try { return this->webui->Document->InvokeScript (lpScriptName); } + catch (Exception ^e) {} + return nullptr; + } + Object ^InvokeCallScriptFunction (String ^lpFuncName, ... array ^alpParams) + { + try + { + if (this->InvokeRequired) return (Object ^)this->Invoke (gcnew Func ^, Object ^> (this, &MainHtmlWnd::CallScriptFunction), lpFuncName, alpParams); + else return CallScriptFunction (lpFuncName, alpParams); + } + catch (Exception ^e) {} + return nullptr; + } + Object ^InvokeCallScriptFunction (String ^lpScriptName) + { + try + { + if (this->InvokeRequired) return (Object ^)this->Invoke (gcnew Func (this, &MainHtmlWnd::CallScriptFunction), lpScriptName); + else return CallScriptFunction (lpScriptName); + } + catch (Exception ^e) {} + return nullptr; + } + Object ^ExecScript (... array ^alpScript) { return InvokeCallScriptFunction ("eval", alpScript); } + property int PageScale + { + int get () + { + CComPtr web2; + HRESULT hr = GetWebBrowser2Interface (webui, &web2); + if (FAILED (hr)) return 0; + VARIANT v; + VariantInit (&v); + hr = web2->ExecWB (OLECMDID_OPTICAL_ZOOM, OLECMDEXECOPT_DODEFAULT, nullptr, &v); + if (FAILED (hr) || v.vt != VT_I4) return 0; + int val = v.lVal; + VariantClear (&v); + return val; + } + void set (int value) + { + CComPtr web2; + HRESULT hr = GetWebBrowser2Interface (webui, &web2); + if (FAILED (hr)) return; + VARIANT v; + VariantInit (&v); + v.vt = VT_I4; + v.lVal = value; + web2->ExecWB (OLECMDID_OPTICAL_ZOOM, OLECMDEXECOPT_DONTPROMPTUSER, &v, nullptr); + } + } +}; +using MainWnd = MainHtmlWnd; +std::vector LoadFileListW (const std::wstring &filePath) +{ + std::vector result; + HANDLE hFile = CreateFileW ( + filePath.c_str (), + GENERIC_READ, + FILE_SHARE_READ, + nullptr, + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, + nullptr); + + if (hFile == INVALID_HANDLE_VALUE) return result; + LARGE_INTEGER fileSize {}; + if (!GetFileSizeEx (hFile, &fileSize) || fileSize.QuadPart == 0) + { + CloseHandle (hFile); + return result; + } + DWORD size = static_cast (fileSize.QuadPart); + std::vector buf; + std::wstring buffer; + buffer.resize (size / sizeof (wchar_t) + 2 + 2); + DWORD readBytes = 0; + ReadFile (hFile, buf.data (), size, &readBytes, nullptr); + buffer += buf.data (); + CloseHandle (hFile); + buffer [readBytes / sizeof (wchar_t)] = L'\0'; + size_t start = 0; + while (true) + { + size_t pos = buffer.find (L'\n', start); + std::wstring line; + if (pos == std::wstring::npos) + { + line = buffer.substr (start); + } + else + { + line = buffer.substr (start, pos - start); + start = pos + 1; + } + if (!line.empty () && line.back () == L'\r') line.pop_back (); + if (!line.empty ()) result.push_back (line); + if (pos == std::wstring::npos) break; + } + return result; +} +enum class CMDPARAM: DWORD +{ + NONE = 0b000, + SILENT = 0b001, + VERYSILENT = 0b011, + MULTIPLE = 0b100 +}; +DWORD CmdMapsToFlags (std::map cmdpairs, std::set &files, std::set &uris) +{ + DWORD dwret = 0; + for (auto &it : cmdpairs) + { + switch (it.first.type) + { + case paramtype::file: { + if (IsFileExists (it.first.key)) files.insert (it.first.key); + } break; + case paramtype::uri: { + uris.insert (it.first.key); + } break; + default: + case paramtype::string: { + auto &key = it.first; + auto &value = it.second; + if (key.key.equals (L"silent")) dwret |= (DWORD)CMDPARAM::SILENT; + else if (key.key.equals (L"verysilent")) dwret |= (DWORD)CMDPARAM::SILENT; + else if (key.key.equals (L"multiple")) + { + if (value.type == paramtype::file) + { + auto strlist = LoadFileListW (value.value); + for (auto &it_s : strlist) + { + if (std::wnstring::empty (it_s)) continue; + std::wnstring filepath = it_s; + std::wstring listdir = GetFileDirectoryW (value.value); + if (!IsFileExists (filepath)) filepath = ProcessEnvVars (filepath); + if (!IsFileExists (filepath)) filepath = CombinePath (listdir, filepath); + if (!IsFileExists (filepath)) filepath = CombinePath (listdir, it_s); + if (!IsFileExists (filepath)) continue; + else files.insert (filepath); + } + } + } + } break; + } + } + if (files.size () > 1) dwret |= (DWORD)CMDPARAM::MULTIPLE; + return dwret; +} +HRESULT SetCurrentAppUserModelID (PCWSTR appID) +{ + typedef HRESULT (WINAPI *SetAppUserModelIDFunc)(PCWSTR); + HMODULE shell32 = LoadLibraryW (L"shell32.dll"); + destruct freelib ([&] () { + if (shell32) FreeLibrary (shell32); + }); + try + { + if (!shell32) return E_FAIL; + auto SetAppUserModelID = (SetAppUserModelIDFunc)GetProcAddress (shell32, "SetCurrentProcessExplicitAppUserModelID"); + if (!SetAppUserModelID) + { + FreeLibrary (shell32); + return E_FAIL; + } + return SetAppUserModelID (appID); + } + catch (...) { return E_FAIL; } + return E_FAIL; +} +[STAThread] +int APIENTRY wWinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLine, int nCmdShow) +{ + SetCurrentProcessExplicitAppUserModelID (m_idenName); + SetProcessDPIAware (); + { + // õǰĿ¼ΪĿ¼ + std::wnstring currdir = GetCurrentProgramPathW (); + std::wnstring rootdir = GetProgramRootDirectoryW (); + if (!PathEquals (currdir, rootdir)) SetCurrentDirectoryW (rootdir.c_str ()); + } + CoInitializeEx (NULL, COINIT_MULTITHREADED); + destruct relco ([] () { + CoUninitialize (); + }); + System::Windows::Forms::Application::EnableVisualStyles (); + System::Windows::Forms::Application::SetCompatibleTextRenderingDefault (false); + auto mwnd = gcnew MainHtmlWnd (); + g_mainwnd = mwnd; + System::Windows::Forms::Application::Run (mwnd); + return 0; +} \ No newline at end of file diff --git a/appinstaller/module.h b/appinstaller/module.h new file mode 100644 index 0000000..4001903 --- /dev/null +++ b/appinstaller/module.h @@ -0,0 +1,51 @@ +#pragma once +#include +#ifdef __cplusplus +#ifndef GetCurrentModule_bRefDefault +// C++ УGetCurrentModule Ĭֵ֮ǰ궨Ĭֵʱ˵Ⱥš= +// ÷磺HMODULE GetCurrentModule (BOOL bRef GetCurrentModule_bRefDefault) +#define GetCurrentModule_bRefDefault = FALSE +#endif +#else +#define GetCurrentModule_bRefDefault +#endif +HMODULE GetCurrentModule (BOOL bRef GetCurrentModule_bRefDefault) +{ + HMODULE hModule = NULL; + if (GetModuleHandleExW (bRef ? GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS : (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS + | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT), (LPCWSTR)GetCurrentModule, &hModule)) + { + return hModule; + } + return NULL; +} +HMODULE GetSelfModuleHandle () +{ + MEMORY_BASIC_INFORMATION mbi; + return ((::VirtualQuery (GetSelfModuleHandle, &mbi, sizeof (mbi)) != 0) + ? (HMODULE)mbi.AllocationBase : NULL); +} +#ifndef GetModuleHandleW_lpModuleNameDefault +#define GetModuleHandleW_lpModuleNameDefault NULL +#endif +#ifndef DEFAULT_HMODULE +#ifdef HMODULE_MODE_EXE +#define DEFAULT_HMODULE GetModuleHandleW (NULL) +#elif defined (HMODULE_MODE_DLL1) +#define DEFAULT_HMODULE GetCurrentModule () +#elif defined (HMODULE_MODE_DLL2) +#define DEFAULT_HMODULE GetSelfModuleHandle () +#else +#define DEFAULT_HMODULE GetModuleHandleW (GetModuleHandleW_lpModuleNameDefault) +#endif +#endif +#undef GetModuleHandleW_lpModuleNameDefault +#ifdef __cplusplus +#ifndef hModule_DefaultParam +// C++ Уʹô˺ꡰhModule_DefaultParamڸһЩβζĬֵ֮ǰ궨Ĭֵʱ˵Ⱥš= +// ÷磺std::wstring GetRCStringSW (UINT resID, HMODULE hModule hModule_DefaultParam) +#define hModule_DefaultParam = DEFAULT_HMODULE +#endif +#else +#define hModule_DefaultParam +#endif \ No newline at end of file diff --git a/appinstaller/mpstr.h b/appinstaller/mpstr.h new file mode 100644 index 0000000..d0c82cc --- /dev/null +++ b/appinstaller/mpstr.h @@ -0,0 +1,42 @@ +#pragma once +#include +#include +#include + +using namespace System; +using namespace System::Text; + +String ^CStringToMPString (LPCSTR lpstr) { return (lpstr ? gcnew String (lpstr) : String::Empty); } +String ^CStringToMPString (LPCWSTR lpstr) { return (lpstr ? gcnew String (lpstr) : String::Empty); } +String ^CStringToMPString (const std::string &objstr) { return CStringToMPString (objstr.c_str ()); } +String ^CStringToMPString (const std::wstring &objstr) { return CStringToMPString (objstr.c_str ()); } +// תΪ UTF-16ָ벻Ҫͷţָת +LPCWSTR MPStringToPtrW (String ^in) +{ + if (in == nullptr) return NULL; + pin_ptr wch = PtrToStringChars (in); + return wch; +} +// תΪ std::wstringUTF-16 +std::wstring MPStringToStdW (String^ in) +{ + if (in == nullptr) return std::wstring (); + pin_ptr wch = PtrToStringChars (in); + return std::wstring (wch, in->Length); +} +// תΪ ANSI std::string +std::string MPStringToStdA (String^ in) +{ + if (in == nullptr) return std::string (); + array ^bytes = Encoding::Default->GetBytes (in); + pin_ptr pinned = &bytes [0]; + return std::string (reinterpret_cast (pinned), bytes->Length); +} +// תΪ UTF-8 std::string +std::string MPStringToStdU8 (String^ in) +{ + if (in == nullptr) return std::string (); + array ^bytes = Encoding::UTF8->GetBytes (in); + pin_ptr pinned = &bytes [0]; + return std::string (reinterpret_cast (pinned), bytes->Length); +} \ No newline at end of file diff --git a/appinstaller/nstring.h b/appinstaller/nstring.h new file mode 100644 index 0000000..15e3bf5 --- /dev/null +++ b/appinstaller/nstring.h @@ -0,0 +1,465 @@ +#pragma once +#include +#include +#include +namespace l0km +{ + template , typename AL = std::allocator > inline std::basic_string toupper (const std::basic_string &src) + { + std::basic_string dst = src; + static const std::locale loc; + const std::ctype &ctype = std::use_facet > (loc); + for (typename std::basic_string ::size_type i = 0; i < src.size (); ++ i) + { + dst [i] = ctype.toupper (src [i]); + } + return dst; + } + template , typename AL = std::allocator > inline std::basic_string tolower (const std::basic_string &src) + { + std::basic_string dst = src; + static const std::locale loc; + const std::ctype &ctype = std::use_facet > (loc); + for (typename std::basic_string ::size_type i = 0; i < src.size (); ++ i) + { + dst [i] = ctype.tolower (src [i]); + } + return dst; + } + inline char toupper (char ch) + { + if (ch < -1) return ch; + static const std::locale loc; + return std::use_facet > (loc).toupper (ch); + } + inline char tolower (char ch) + { + if (ch < -1) return ch; + static const std::locale loc; + return std::use_facet > (loc).tolower (ch); + } + inline wchar_t toupper (wchar_t ch) + { + if (ch < -1) return ch; + static const std::locale loc; + return std::use_facet > (loc).toupper (ch); + } + inline wchar_t tolower (wchar_t ch) + { + if (ch < -1) return ch; + static const std::locale loc; + return std::use_facet > (loc).tolower (ch); + } + inline int toupper (int ch) + { + if (ch < -1) return ch; + static const std::locale loc; + return std::use_facet > (loc).toupper (ch); + } + inline int tolower (int ch) + { + if (ch < -1) return ch; + static const std::locale loc; + return std::use_facet > (loc).tolower (ch); + } +} +template bool is_blank (ct &ch) +{ + return ch == ct (' ') || ch == ct ('\t') || ch == ct ('\n'); +} +template , typename AL = std::allocator > std::basic_string NormalizeString (const std::basic_string &str, bool upper = false, bool includemidblank = false) +{ + typedef std::basic_string string_type; + string_type result; + if (str.empty ()) return result; + auto begin_it = str.begin (); + auto end_it = str.end (); + while (begin_it != end_it && is_blank (*begin_it)) ++begin_it; + while (end_it != begin_it && is_blank (*(end_it - 1))) --end_it; + bool in_space = false; + for (auto it = begin_it; it != end_it; ++ it) + { + if (is_blank (*it)) + { + if (includemidblank) + { + if (!in_space) + { + result.push_back (E (' ')); + in_space = true; + } + } + else + { + result.push_back (*it); + in_space = true; + } + } + else + { + result.push_back (*it); + in_space = false; + } + } + if (upper) return l0km::toupper (result); + else return l0km::tolower (result); +} +template , typename AL = std::allocator > bool IsNormalizeStringEquals (const std::basic_string &l, const std::basic_string &r, bool includemidblank = false) +{ + auto _local_strlen = [] (const E *p) -> size_t { + size_t cnt = 0; + while (*(p + cnt)) { cnt ++; } + return cnt; + }; + const E *pl = l.c_str (); + const E *pr = r.c_str (); + while (*pl && is_blank (*pl)) ++ pl; + while (*pr && is_blank (*pr)) ++ pr; + const E *el = l.c_str () + _local_strlen (l.c_str ()); + const E *er = r.c_str () + _local_strlen (r.c_str ()); + while (el > pl && is_blank (*(el - 1))) --el; + while (er > pr && is_blank (*(er - 1))) --er; + while (pl < el && pr < er) + { + if (includemidblank) + { + if (is_blank (*pl) && is_blank (*pr)) + { + while (pl < el && is_blank (*pl)) ++pl; + while (pr < er && is_blank (*pr)) ++pr; + continue; + } + else if (is_blank (*pl)) + { + while (pl < el && is_blank (*pl)) ++pl; + continue; + } + else if (is_blank (*pr)) + { + while (pr < er && is_blank (*pr)) ++pr; + continue; + } + } + if (l0km::tolower (*pl) != l0km::tolower (*pr)) return false; + ++ pl; + ++ pr; + } + while (pl < el && is_blank (*pl)) ++ pl; + while (pr < er && is_blank (*pr)) ++ pr; + return pl == el && pr == er; +} +template , typename AL = std::allocator > int64_t NormalizeStringCompare (const std::basic_string &l, const std::basic_string &r, bool includemidblank = false) +{ + auto _local_strlen = [] (const E *p) -> size_t { + size_t cnt = 0; + while (*(p + cnt)) { cnt ++; } + return cnt; + }; + const E *pl = l.c_str (); + const E *pr = r.c_str (); + while (*pl && is_blank (*pl)) ++ pl; + while (*pr && is_blank (*pr)) ++ pr; + const E *el = l.c_str () + _local_strlen (l.c_str ()); + const E *er = r.c_str () + _local_strlen (r.c_str ()); + while (el > pl && is_blank (*(el - 1))) -- el; + while (er > pr && is_blank (*(er - 1))) -- er; + while (pl < el && pr < er) + { + if (includemidblank) + { + if (is_blank (*pl) && is_blank (*pr)) + { + while (pl < el && is_blank (*pl)) ++pl; + while (pr < er && is_blank (*pr)) ++pr; + continue; + } + else if (is_blank (*pl)) + { + while (pl < el && is_blank (*pl)) ++pl; + continue; + } + else if (is_blank (*pr)) + { + while (pr < er && is_blank (*pr)) ++pr; + continue; + } + } + E chl = l0km::tolower (*pl); + E chr = l0km::tolower (*pr); + if (chl != chr) return (int64_t)chl - (int64_t)chr; + ++ pl; + ++ pr; + } + while (pl < el && is_blank (*pl)) ++ pl; + while (pr < er && is_blank (*pr)) ++ pr; + if (pl == el && pr == er) return 0; + if (pl == el) return -1; + if (pr == er) return 1; + return (int64_t)l0km::tolower (*pl) - (int64_t)l0km::tolower (*pr); +} +template bool IsNormalizeStringEquals (const CharT *l, const CharT *r, bool includemidblank = false) +{ + if (!l || !r) return l == r; + auto skip_blank = [] (const CharT *&p) + { + while (*p && is_blank (*p)) ++ p; + }; + const CharT *p1 = l; + const CharT *p2 = r; + skip_blank (p1); + skip_blank (p2); + while (*p1 && *p2) + { + CharT ch1 = l0km::tolower (*p1); + CharT ch2 = l0km::tolower (*p2); + if (ch1 != ch2) return false; + ++ p1; + ++ p2; + if (includemidblank) + { + if (is_blank (*p1) || is_blank (*p2)) + { + skip_blank (p1); + skip_blank (p2); + } + } + } + skip_blank (p1); + skip_blank (p2); + return *p1 == 0 && *p2 == 0; +} +template int64_t NormalizeStringCompare (const CharT *l, const CharT *r, bool includemidblank = false) +{ + if (!l || !r) return l ? 1 : (r ? -1 : 0); + auto skip_blank = [] (const CharT *&p) + { + while (*p && is_blank (*p)) ++ p; + }; + const CharT *p1 = l; + const CharT *p2 = r; + skip_blank (p1); + skip_blank (p2); + while (*p1 && *p2) + { + CharT ch1 = l0km::tolower (*p1); + CharT ch2 = l0km::tolower (*p2); + if (ch1 != ch2) return (ch1 < ch2) ? -1 : 1; + ++ p1; + ++ p2; + if (includemidblank) + { + if (is_blank (*p1) || is_blank (*p2)) + { + skip_blank (p1); + skip_blank (p2); + } + } + } + skip_blank (p1); + skip_blank (p2); + if (*p1 == 0 && *p2 == 0) return 0; + if (*p1 == 0) return -1; + return 1; +} +template , typename AL = std::allocator > bool IsNormalizeStringEmpty (const std::basic_string &str) +{ + return IsNormalizeStringEquals (str, std::basic_string ()); +} +template , typename AL = std::allocator > std::basic_string StringTrim (const std::basic_string &str, bool includemidblank = false) +{ + typedef std::basic_string string_type; + typedef typename string_type::size_type size_type; + if (str.empty ()) return string_type (); + size_type first = 0; + size_type last = str.size (); + while (first < last && is_blank (str [first])) ++first; + while (last > first && is_blank (str [last - 1])) --last; + if (first == last) return string_type (); + string_type result; + result.reserve (last - first); + bool in_space = false; + for (size_type i = first; i < last; ++ i) + { + if (is_blank (str [i])) + { + if (includemidblank) + { + if (!in_space) + { + result.push_back (E (' ')); + in_space = true; + } + } + else + { + result.push_back (str [i]); + in_space = true; + } + } + else + { + result.push_back (str [i]); + in_space = false; + } + } + return result; +} +template , typename AL = std::allocator > size_t GetNormalizeStringLength (const std::basic_string &str, bool includemidblank = false) +{ + typedef typename std::basic_string ::size_type size_type; + if (str.empty ()) return 0; + size_type first = 0, last = str.size (); + while (first < last && is_blank (str [first])) ++first; + while (last > first && is_blank (str [last - 1])) --last; + if (first == last) return 0; + size_t length = 0; + bool in_space = false; + for (size_type i = first; i < last; ++i) + { + if (is_blank (str [i])) + { + if (includemidblank) + { + if (!in_space) + { + ++ length; + in_space = true; + } + } + else + { + ++ length; + in_space = true; + } + } + else + { + ++ length; + in_space = false; + } + } + return length; +} +namespace std +{ + + template , typename al = std::allocator > class basic_nstring: public std::basic_string + { + bool default_upper = false, default_include_blank_in_str = false; + public: + using base = std::basic_string ; + using derive = std::basic_nstring ; + using typename base::size_type; + using typename base::value_type; + using base::base; + using pstr = ct *; + using pcstr = const ct *; + basic_nstring (): base (), default_upper (false), default_include_blank_in_str (false) {} + basic_nstring (const ct *pStr): base (pStr), default_upper (false), default_include_blank_in_str (false) {} + basic_nstring (const base &str): base (str) {} + basic_nstring (base &&str): base (std::move (str)) {} + basic_nstring (const ct *data, size_type count): base (data, count), default_upper (false), default_include_blank_in_str (false) {} + // template basic_nstring (const ct (&arr) [N]) : base (arr, N - 1) {} + template basic_nstring (InputIt first, InputIt last): base (first, last), default_upper (false), default_include_blank_in_str (false) {} + bool upper_default () const { return this->default_upper; } + bool upper_default (bool value) { return this->default_upper = value; } + bool include_blank_in_str_middle () const { return this->default_include_blank_in_str; } + bool include_blank_in_str_middle (bool value) { return this->default_include_blank_in_str = value; } + base normalize (bool upper, bool includemidblank) const + { + return NormalizeString (*this, upper, includemidblank); + } + base normalize (bool upper) const + { + return this->normalize (upper, default_include_blank_in_str); + } + base normalize () const { return this->normalize (default_upper); } + base upper (bool includemidblank) const + { + return NormalizeString (*this, true, includemidblank); + } + base upper () const { return this->upper (default_include_blank_in_str); } + base lower (bool includemidblank) const + { + return NormalizeString (*this, false, includemidblank); + } + base lower () const { return this->lower (default_include_blank_in_str); } + base trim (bool includemidblank) const + { + return StringTrim (*this, includemidblank); + } + base trim () const { return this->trim (default_include_blank_in_str); } + size_t length (bool includemidblank) const { return GetNormalizeStringLength (*this, includemidblank); } + size_t length () const { return length (default_include_blank_in_str); } + bool empty () const + { + return IsNormalizeStringEmpty (*this); + } + bool equals (const base &another, bool includemidblank) const + { + return IsNormalizeStringEquals (*this, another, includemidblank); + } + bool equals (const base &another) const { return equals (another, default_include_blank_in_str); } + int64_t compare (const base &another, bool includemidblank) const + { + return NormalizeStringCompare (*this, another, includemidblank); + } + int64_t compare (const base &another) const { return compare (another, default_include_blank_in_str); } + base &string () { return *this; } + base to_string (bool upper, bool includemidblank) const { return this->normalize (upper, includemidblank); } + base to_string (bool upper) const { return this->normalize (upper, default_include_blank_in_str); } + base to_string () const { return this->normalize (default_upper); } + bool operator == (const base &other) const { return equals (other, false); } + bool operator != (const base &other) const { return !equals (other, false); } + bool operator < (const base &other) const { return compare (other, false) < 0; } + bool operator > (const base &other) const { return compare (other, false) > 0; } + bool operator <= (const base &other) const { return compare (other, false) <= 0; } + bool operator >= (const base &other) const { return compare (other, false) >= 0; } + int64_t operator - (const base &other) const { return compare (other, false); } + bool operator == (pcstr &other) const { return equals (other, false); } + bool operator != (pcstr &other) const { return !equals (other, false); } + bool operator < (pcstr &other) const { return compare (other, false) < 0; } + bool operator > (pcstr &other) const { return compare (other, false) > 0; } + bool operator <= (pcstr &other) const { return compare (other, false) <= 0; } + bool operator >= (pcstr &other) const { return compare (other, false) >= 0; } + template , typename AL = std::allocator > + static bool equals (const std::basic_string &l, const std::basic_string &r, bool remove_mid_blank = false) + { + return IsNormalizeStringEquals (l, r, remove_mid_blank); + } + template , typename AL = std::allocator > + static int64_t compare (const std::basic_string &l, const std::basic_string &r, bool remove_mid_blank = false) + { + return NormalizeStringCompare (l, r, remove_mid_blank); + } + template , typename AL = std::allocator > + static std::basic_string normalize (const std::basic_string &str, bool to_upper = false, bool remove_mid_blank = false) + { + return NormalizeString (str, to_upper, remove_mid_blank); + } + template , typename AL = std::allocator > + static std::basic_string trim (const std::basic_string &str, bool remove_mid_blank = false) + { + return StringTrim (str, remove_mid_blank); + } + template , typename AL = std::allocator > + static size_t length (const std::basic_string &str, bool remove_mid_blank = false) + { + return GetNormalizeStringLength (str, remove_mid_blank); + } + template , typename AL = std::allocator > + static bool empty (const std::basic_string &str) + { + return IsNormalizeStringEmpty (str); + } + template , typename AL = std::allocator > + static std::basic_nstring to_nstring (std::basic_string &str) { return std::basic_nstring (str); } + template , typename AL = std::allocator > + static std::basic_nstring toupper (const std::basic_nstring &str) { return l0km::toupper (str); } + template , typename AL = std::allocator > + static std::basic_nstring tolower (const std::basic_nstring &str) { return l0km::tolower (str); } + }; + + typedef basic_nstring nstring; + typedef basic_nstring wnstring; +} \ No newline at end of file diff --git a/appinstaller/packages.config b/appinstaller/packages.config new file mode 100644 index 0000000..c2da1c1 --- /dev/null +++ b/appinstaller/packages.config @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/appinstaller/raii.h b/appinstaller/raii.h new file mode 100644 index 0000000..2117f28 --- /dev/null +++ b/appinstaller/raii.h @@ -0,0 +1,10 @@ +#pragma once +#include +typedef struct raii +{ + std::function endtask = nullptr; + raii (std::function pFunc = nullptr): endtask (pFunc) {} + ~raii () { if (endtask) endtask (); } + raii (const raii &) = delete; + raii (raii &&) = delete; +} destruct; \ No newline at end of file diff --git a/appinstaller/rctools.h b/appinstaller/rctools.h new file mode 100644 index 0000000..8e6a12b --- /dev/null +++ b/appinstaller/rctools.h @@ -0,0 +1,121 @@ +#pragma once +#include +#include +#ifndef _CRT_SECURE_NO_WARNINGS +#define _CRT_SECURE_NO_WARNINGS +#endif +#include "typestrans.h" +#include "module.h" + +// صָǿһҪ free ͷ +LPWSTR GetRCStringW (UINT resID, HMODULE hModule hModule_DefaultParam) +{ + std::vector buf (256); + size_t cnt = 0; +CopyStringLoop_GetRCStringW: + { + size_t len = LoadStringW (hModule, resID, buf.data (), buf.size ()); + if (cnt > 1625) return _wcsdup (buf.data ()); + if (len >= buf.size () - 1) + { + buf.resize (buf.size () + 20); + cnt ++; + goto CopyStringLoop_GetRCStringW; + } + else return _wcsdup (buf.data ()); + } +} +// صָǿһҪ free ͷ +LPSTR GetRCStringA (UINT resID, HMODULE hModule hModule_DefaultParam) +{ + std::vector buf (256); + size_t cnt = 0; +CopyStringLoop_GetRCStringA: + { + size_t len = LoadStringA (hModule, resID, buf.data (), buf.size ()); + if (cnt > 1625) return _strdup (buf.data ()); + if (len >= buf.size () - 1) + { + buf.resize (buf.size () + 20); + cnt ++; + goto CopyStringLoop_GetRCStringA; + } + else return _strdup (buf.data ()); + } +} + +HICON LoadRCIcon (UINT resID, HMODULE hModule hModule_DefaultParam) +{ + return (HICON)LoadImageW (hModule, MAKEINTRESOURCEW (resID), IMAGE_ICON, 0, 0, LR_DEFAULTSIZE); +} +HRSRC FindResourceByName (LPCWSTR resourceName, LPCWSTR resourceType, HMODULE hModule hModule_DefaultParam) +{ + return FindResourceW (hModule, resourceName, resourceType); +} +#ifdef __cplusplus +#include +std::wstring GetRCStringSW (UINT resID, HMODULE hModule hModule_DefaultParam) +{ + std::vector buf (256); + size_t cnt = 0; +CopyStringLoop_GetRCStringSW: + { + size_t len = LoadStringW (hModule, resID, buf.data (), buf.size ()); + if (cnt > 1625) return buf.data (); + if (len >= buf.size () - 1) + { + buf.resize (buf.size () + 20); + cnt ++; + goto CopyStringLoop_GetRCStringSW; + } + else return buf.data (); + } +} +std::string GetRCStringSA (UINT resID, HMODULE hModule hModule_DefaultParam) +{ + std::vector buf (256); + size_t cnt = 0; +CopyStringLoop_GetRCStringSA: + { + size_t len = LoadStringA (hModule, resID, buf.data (), buf.size ()); + if (cnt > 1625) return buf.data (); + if (len >= buf.size () - 1) + { + buf.resize (buf.size () + 20); + cnt ++; + goto CopyStringLoop_GetRCStringSA; + } + else return buf.data (); + } +} +#endif +#if defined (__cplusplus) && defined (__cplusplus_cli) +using namespace System; +String ^GetRCStringCli (UINT resID, HMODULE hModule hModule_DefaultParam) +{ + std::vector buf (256); + size_t cnt = 0; +CopyStringLoop_GetRCStringCli: + { + size_t len = LoadStringW (hModule, resID, buf.data (), buf.size ()); + if (cnt > 1625) return gcnew String (buf.data ()); + if (len >= buf.size () - 1) + { + buf.resize (buf.size () + 20); + cnt ++; + goto CopyStringLoop_GetRCStringCli; + } + else return gcnew String (buf.data ()); + } +} +#define GetRCIntValue(_UINT__resID_) toInt (GetRCStringCli (_UINT__resID_)) +#define GetRCDoubleValue(_UINT__resID_) toDouble (GetRCStringCli (_UINT__resID_)) +#define GetRCBoolValue(_UINT__resID_) toBool (GetRCStringCli (_UINT__resID_)) +#define GetRCDateTimeValue(_UINT__resID_) toDateTime (GetRCStringCli (_UINT__resID_)) +#define rcString(resID) GetRCStringCli (resID) +#define rcInt(resID) GetRCIntValue (resID) +#define rcDouble(resID) GetRCDoubleValue (resID) +#define rcBool(resID) GetRCBoolValue (resID) +#define rcDTime(resID) GetRCDateTimeValue (resID) +#define rcIcon(resID) LoadRCIcon (resID) +#endif \ No newline at end of file diff --git a/appinstaller/res/icons/color.ico b/appinstaller/res/icons/color.ico new file mode 100644 index 0000000..3db5e1a Binary files /dev/null and b/appinstaller/res/icons/color.ico differ diff --git a/appinstaller/res/icons/file.ico b/appinstaller/res/icons/file.ico new file mode 100644 index 0000000..074eb28 Binary files /dev/null and b/appinstaller/res/icons/file.ico differ diff --git a/appinstaller/res/icons/main.ico b/appinstaller/res/icons/main.ico new file mode 100644 index 0000000..1ba811e Binary files /dev/null and b/appinstaller/res/icons/main.ico differ diff --git a/appinstaller/res/icons/taskbar.ico b/appinstaller/res/icons/taskbar.ico new file mode 100644 index 0000000..1ba811e Binary files /dev/null and b/appinstaller/res/icons/taskbar.ico differ diff --git a/appinstaller/res/icons/titlebar.ico b/appinstaller/res/icons/titlebar.ico new file mode 100644 index 0000000..3b0063a Binary files /dev/null and b/appinstaller/res/icons/titlebar.ico differ diff --git a/appinstaller/res/icons/white.ico b/appinstaller/res/icons/white.ico new file mode 100644 index 0000000..12dc50a Binary files /dev/null and b/appinstaller/res/icons/white.ico differ diff --git a/appinstaller/resource.h b/appinstaller/resource.h new file mode 100644 index 0000000..9a29546 Binary files /dev/null and b/appinstaller/resource.h differ diff --git a/appinstaller/strcmp.h b/appinstaller/strcmp.h new file mode 100644 index 0000000..1748724 --- /dev/null +++ b/appinstaller/strcmp.h @@ -0,0 +1,154 @@ +#pragma once +#include +#include "nstring.h" +#ifdef __cplusplus +#define ptrnull(ptr) (!(ptr)) +#else +#define ptrnull(ptr) ((ptr) == NULL) +#endif +#define ptrvalid(ptr) (!ptrnull (ptr)) +// char * WCHAR * ַβΪ NULLжǷΪǿַָЧҳȴ 0ǧҰָ룬һ +#define strvalid(strptr) (ptrvalid (strptr) && *(strptr)) +// char * WCHAR * ַβΪ NULLжǷΪַָΪ NULL 򳤶Ϊ 0ǧҰָ룬һ +#define strnull(strptr) (ptrnull (strptr) || !*(strptr)) +typedef std::wnstring strlabel, StringLabel; +std::wstring StringTrim (const std::wstring &str) { return std::wnstring::trim (str); } +std::string StringTrim (const std::string &str) { return std::nstring::trim (str); } +#define StringToUpper l0km::toupper +#define StringToLower l0km::tolower +int LabelCompare (const std::wstring &l1, const std::wstring &l2) +{ + return std::wnstring::compare (l1, l2); +} +int LabelCompare (const std::string &l1, const std::string &l2) +{ + return std::nstring::compare (l1, l2); +} +bool LabelEqual (const std::wstring &l1, const std::wstring &l2) +{ + return std::wnstring::equals (l1, l2); +} +bool LabelEqual (const std::string &l1, const std::string &l2) +{ + return std::wnstring::equals (l1, l2); +} +bool LabelEmpty (const std::wstring &str) { return std::wnstring::empty (str); } +bool LabelEmpty (const std::string &str) { return std::nstring::empty (str); } +#define LabelNoEmpty(_str_) (!LabelEmpty (_str_)) +int InStr (const std::string &text, const std::string &keyword, bool ignoreCase = false) +{ + std::string s1, s2; + if (ignoreCase) + { + s1 = StringToUpper (text); + s2 = StringToUpper (keyword); + } + else + { + s1 = text; + s2 = keyword; + } + const char *found = StrStrIA (s1.c_str (), s2.c_str ()); + if (!found) + { + return -1; + } + return found - text.c_str (); +} +int InStr (const std::wstring &text, const std::wstring &keyword, bool ignoreCase = false) +{ + std::wstring s1, s2; + if (ignoreCase) + { + s1 = StringToUpper (text); + s2 = StringToUpper (keyword); + } + else + { + s1 = text; + s2 = keyword; + } + const WCHAR *found = StrStrIW (s1.c_str (), s2.c_str ()); + if (!found) + { + return -1; + } + return found - text.c_str (); +} +bool StrInclude (const std::string &text, const std::string &keyword, bool ignoreCase = false) +{ + std::string s1, s2; + if (ignoreCase) + { + s1 = StringToUpper (text); + s2 = StringToUpper (keyword); + } + else + { + s1 = text; + s2 = keyword; + } + const char *found = StrStrIA (s1.c_str (), s2.c_str ()); + if (!found) return false; + return true; +} +bool StrInclude (const std::wstring &text, const std::wstring &keyword, bool ignoreCase = false) +{ + std::wstring s1, s2; + if (ignoreCase) + { + s1 = StringToUpper (text); + s2 = StringToUpper (keyword); + } + else + { + s1 = text; + s2 = keyword; + } + const WCHAR *found = StrStrIW (s1.c_str (), s2.c_str ()); + if (!found) return false; + return true; +} +// ú "\0\0" ַͨöԻеļ +LPCWSTR strcpynull (LPWSTR dest, LPCWSTR endwith, size_t bufsize) +{ + if (!dest || !endwith || bufsize == 0) + return dest; + if (dest [0] == L'\0' && bufsize > 1) + { + dest [1] = L'\0'; + } + size_t pos = 0; + while (pos < bufsize - 1) + { + if (dest [pos] == L'\0' && dest [pos + 1] == L'\0') + { + if (dest [0]) pos ++; + break; + } + pos ++; + } + size_t i = 0; + while (pos < bufsize - 1 && endwith [i] != L'\0') + { + dest [pos ++] = endwith [i ++]; + } + if (pos < bufsize) + { + dest [pos] = L'\0'; + } + return dest; +} +// ȡıߣע⣺ָıַ硰chijΪ2 +std::wstring GetStringLeft (const std::wstring &str, size_t length) +{ + std::vector buf (length + 1); + lstrcpynW (buf.data (), str.c_str (), length + 1); + return buf.data (); +} +// ȡıұ +std::wstring GetStringRight (const std::wstring &str, size_t length) +{ + if (length >= str.length ()) return str; + return str.substr (str.length () - length, length).c_str (); +} diff --git a/appinstaller/strcode.h b/appinstaller/strcode.h new file mode 100644 index 0000000..f6f2f62 --- /dev/null +++ b/appinstaller/strcode.h @@ -0,0 +1,21 @@ +#pragma once +#include +#include +std::wstring StringToWString (const std::string &str, UINT codePage = CP_ACP) +{ + if (str.empty ()) return std::wstring (); + int len = MultiByteToWideChar (codePage, 0, str.c_str (), -1, nullptr, 0); + if (len == 0) return std::wstring (); + std::wstring wstr (len - 1, L'\0'); + MultiByteToWideChar (codePage, 0, str.c_str (), -1, &wstr [0], len); + return wstr; +} +std::string WStringToString (const std::wstring &wstr, UINT codePage = CP_ACP) +{ + if (wstr.empty ()) return std::string (); + int len = WideCharToMultiByte (codePage, 0, wstr.c_str (), -1, nullptr, 0, nullptr, nullptr); + if (len == 0) return std::string (); + std::string str (len - 1, '\0'); + WideCharToMultiByte (codePage, 0, wstr.c_str (), -1, &str [0], len, nullptr, nullptr); + return str; +} diff --git a/appinstaller/themeinfo.h b/appinstaller/themeinfo.h new file mode 100644 index 0000000..d1c4fe3 --- /dev/null +++ b/appinstaller/themeinfo.h @@ -0,0 +1,160 @@ +#pragma once +#include +#include + +bool IsHighContrastEnabled () +{ + HIGHCONTRAST hc = {sizeof (HIGHCONTRAST)}; + if (SystemParametersInfo (SPI_GETHIGHCONTRAST, sizeof (hc), &hc, 0)) return (hc.dwFlags & HCF_HIGHCONTRASTON) != 0; + return false; +} +enum class HighContrastTheme +{ + None, + Black, + White, + Other +}; +HighContrastTheme GetHighContrastTheme () +{ + HIGHCONTRAST hc = {sizeof (HIGHCONTRAST)}; + if (!SystemParametersInfo (SPI_GETHIGHCONTRAST, sizeof (hc), &hc, 0)) return HighContrastTheme::None; + if (!(hc.dwFlags & HCF_HIGHCONTRASTON)) return HighContrastTheme::None; + COLORREF bgColor = GetSysColor (COLOR_WINDOW); + COLORREF textColor = GetSysColor (COLOR_WINDOWTEXT); + int brightnessBg = (GetRValue (bgColor) + GetGValue (bgColor) + GetBValue (bgColor)) / 3; + int brightnessText = (GetRValue (textColor) + GetGValue (textColor) + GetBValue (textColor)) / 3; + if (brightnessBg < brightnessText) return HighContrastTheme::Black; + else if (brightnessBg > brightnessText) return HighContrastTheme::White; + else return HighContrastTheme::Other; +} +int GetDPI () +{ + HDC hDC = GetDC (NULL); + int DPI_A = (int)(((double)GetDeviceCaps (hDC, 118) / (double)GetDeviceCaps (hDC, 8)) * 100); + int DPI_B = (int)(((double)GetDeviceCaps (hDC, 88) / 96) * 100); + ReleaseDC (NULL, hDC); + if (DPI_A == 100) return DPI_B; + else if (DPI_B == 100) return DPI_A; + else if (DPI_A == DPI_B) return DPI_A; + else return 0; +} +int GetScreenWidth () { return GetSystemMetrics (SM_CXSCREEN); } +int GetScreenHeight () { return GetSystemMetrics (SM_CYSCREEN); } +System::Drawing::Color GetDwmThemeColor () +{ + DWORD color = 0; + BOOL opaqueBlend = FALSE; + // DwmGetColorizationColor ȡ Aero ɫ + HRESULT hr = DwmGetColorizationColor (&color, &opaqueBlend); + if (SUCCEEDED (hr)) { + BYTE r = (BYTE)((color & 0x00FF0000) >> 16); + BYTE g = (BYTE)((color & 0x0000FF00) >> 8); + BYTE b = (BYTE)(color & 0x000000FF); + return System::Drawing::Color::FromArgb (r, g, b); + } + else { + // ȡʧܣĬɫ + return System::Drawing::Color::FromArgb (0, 120, 215); + } +} +String ^ColorToHtml (System::Drawing::Color color) +{ + return String::Format ("#{0:X2}{1:X2}{2:X2}", color.R, color.G, color.B); +} +System::Drawing::Color StringToColor (String ^colorStr) +{ + using Color = System::Drawing::Color; + using Regex = System::Text::RegularExpressions::Regex; + auto Clamp = [] (int value, int min, int max) + { + return (value < min) ? min : (value > max) ? max : value; + }; + String ^normalized = colorStr->Trim ()->ToLower (); + if (normalized == "transparent") return Color::Transparent; + if (Color::FromName (normalized).IsKnownColor) + { + return Color::FromName (normalized); + } + if (normalized->StartsWith ("#")) + { + String^ hex = normalized->Substring (1); + if (hex->Length == 3 || hex->Length == 4) + { + hex = String::Concat ( + hex [0].ToString () + hex [0], + hex [1].ToString () + hex [1], + hex [2].ToString () + hex [2], + (hex->Length == 4) ? (hex [3].ToString () + hex [3]) : "" + ); + } + uint32_t argb = Convert::ToUInt32 (hex, 16); + switch (hex->Length) + { + case 6: return Color::FromArgb ( + 0xFF, + (argb >> 16) & 0xFF, + (argb >> 8) & 0xFF, + argb & 0xFF + ); + case 8: return Color::FromArgb ( + (argb >> 24) & 0xFF, + (argb >> 16) & 0xFF, + (argb >> 8) & 0xFF, + argb & 0xFF + ); + default: throw gcnew ArgumentException ("Invalid hex color format"); + } + } + System::Text::RegularExpressions::Match ^match = Regex::Match (normalized, + "^(rgba?)\\s*\\(\\s*(\\d+%?)\\s*,\\s*(\\d+%?)\\s*,\\s*(\\d+%?)\\s*,?\\s*([\\d.]+%?)?\\s*\\)$"); + if (match->Success) + { + auto GetComponent = [&] (String^ val) -> int + { + if (val->EndsWith ("%")) + { + float percent = float::Parse (val->TrimEnd ('%')) / 100.0f; + return Clamp ((int)Math::Round (percent * 255), 0, 255); + } + return Clamp (int::Parse (val), 0, 255); + }; + int r = GetComponent (match->Groups [2]->Value); + int g = GetComponent (match->Groups [3]->Value); + int b = GetComponent (match->Groups [4]->Value); + if (match->Groups [1]->Value == "rgba") + { + String^ alphaVal = match->Groups [5]->Value; + int a = 255; + if (alphaVal->EndsWith ("%")) + { + float percent = float::Parse (alphaVal->TrimEnd ('%')) / 100.0f; + a = Clamp ((int)Math::Round (percent * 255), 0, 255); + } + else if (!String::IsNullOrEmpty (alphaVal)) + { + a = Clamp ((int)Math::Round (float::Parse (alphaVal) * 255), 0, 255); + } + return Color::FromArgb (a, r, g, b); + } + return Color::FromArgb (r, g, b); + } + //throw gcnew ArgumentException ("Unsupported color format: " + colorStr); + return Color::Transparent; +} +bool IsAppInDarkMode () +{ + HKEY hKey; + DWORD dwValue; + if (RegOpenKeyEx (HKEY_CURRENT_USER, L"Software\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize", 0, KEY_READ, &hKey) == ERROR_SUCCESS) + { + DWORD dwSize = sizeof (dwValue); + if (RegQueryValueEx (hKey, L"AppsUseLightTheme", NULL, NULL, (LPBYTE)&dwValue, &dwSize) == ERROR_SUCCESS) + { + RegCloseKey (hKey); + return dwValue == 0; + } + RegCloseKey (hKey); + } + return false; +} \ No newline at end of file diff --git a/appinstaller/typestrans.h b/appinstaller/typestrans.h new file mode 100644 index 0000000..7abde9b --- /dev/null +++ b/appinstaller/typestrans.h @@ -0,0 +1,265 @@ +#pragma once +#ifndef _CRT_SECURE_NO_WARNINGS +#define _CRT_SECURE_NO_WARNINGS +#endif +#include +#ifdef __cplusplus +#include +#include +#include +#include +#else +#include +#include +#include +#include +#endif +unsigned _wtou (const wchar_t *str) +{ + unsigned value = 0; + if (str) + { + swscanf (str, L"%u", &value); + } + return value; +} +unsigned long _wtoul (const wchar_t *str) +{ + unsigned value = 0; + if (str) + { + swscanf (str, L"%lu", &value); + } + return value; +} +unsigned long long _wtou64 (const wchar_t *str) +{ + unsigned long long value = 0; + if (str) + { + swscanf (str, L"%llu", &value); + } + return value; +} +double _wtod (const wchar_t *str) +{ + if (!str || !*str) return 0.0; // ַָ + double value = 0.0; + if (swscanf (str, L"%lg", &value) == 1) + { + return value; + } + return 0.0; // ʧʱ 0.0 +} +unsigned atou (const char *str) +{ + unsigned value = 0; + if (str) + { + sscanf (str, "%u", &value); + } + return value; +} +unsigned long atoul (const char *str) +{ + unsigned value = 0; + if (str) + { + sscanf (str, "%lu", &value); + } + return value; +} +unsigned long long atou64 (const char *str) +{ + unsigned long long value = 0; + if (str) + { + sscanf (str, "%llu", &value); + } + return value; +} +double atod (const char *str) +{ + if (!str || !*str) return 0.0; // ַָ + double value = 0.0; + if (sscanf (str, "%lg", &value) == 1) + { + return value; + } + return 0.0; // ʧʱ 0.0 +} +int8_t atoi8 (const char *str) +{ + int value = 0; + if (str) sscanf (str, "%d", &value); + return (int8_t)value; +} +int16_t atoi16 (const char *str) +{ + int value = 0; + if (str) sscanf (str, "%d", &value); + return (int16_t)value; +} +int32_t atoi32 (const char *str) +{ + int32_t value = 0; + if (str) sscanf (str, "%d", &value); + return value; +} +uint8_t atoui8 (const char *str) +{ + unsigned value = 0; + if (str) sscanf (str, "%u", &value); + return (uint8_t)value; +} +uint16_t atoui16 (const char *str) +{ + unsigned value = 0; + if (str) sscanf (str, "%u", &value); + return (uint16_t)value; +} +uint32_t atoui32 (const char *str) +{ + uint32_t value = 0; + if (str) sscanf (str, "%u", &value); + return value; +} +int8_t _wtoi8 (const wchar_t *str) +{ + int value = 0; + if (str) swscanf (str, L"%d", &value); + return (int8_t)value; +} +int16_t _wtoi16 (const wchar_t *str) +{ + int value = 0; + if (str) swscanf (str, L"%d", &value); + return (int16_t)value; +} +int32_t _wtoi32 (const wchar_t *str) +{ + int32_t value = 0; + if (str) swscanf (str, L"%d", &value); + return value; +} +uint8_t _wtoui8 (const wchar_t *str) +{ + unsigned value = 0; + if (str) swscanf (str, L"%u", &value); + return (uint8_t)value; +} +uint16_t _wtoui16 (const wchar_t *str) +{ + unsigned value = 0; + if (str) swscanf (str, L"%u", &value); + return (uint16_t)value; +} +uint32_t _wtoui32 (const wchar_t *str) +{ + uint32_t value = 0; + if (str) swscanf (str, L"%u", &value); + return value; +} +int64_t atoi64 (const char *str) +{ + int64_t value = 0; + if (str) sscanf (str, "%lld", &value); + return value; +} + +EXTERN_C int StringToIntA (const char *str) { return atoi (str); } +EXTERN_C int StringToIntW (const WCHAR *str) { return _wtoi (str); } +EXTERN_C unsigned StringToUnsignedA (const char *str) { return atou (str); } +EXTERN_C unsigned StringToUnsignedW (const WCHAR *str) { return _wtou (str); } +EXTERN_C bool StringToBoolA (const char *str) +{ + char buf [32] = {0}; + strcpy (buf, str); + for (int cnt = 0; buf [cnt]; cnt ++) buf [cnt] = tolower (buf [cnt]); + return !strcmp (buf, "true") || + !strcmp (buf, "yes") || + !strcmp (buf, "ok") || + !strcmp (buf, "sure") || + !strcmp (buf, "okay") || + !strcmp (buf, "zhen") || + !strcmp (buf, ""); +} +EXTERN_C bool StringToBoolW (const WCHAR *str) +{ + WCHAR buf [32] = {0}; + lstrcpyW (buf, str); + for (int cnt = 0; buf [cnt]; cnt ++) buf [cnt] = tolower (buf [cnt]); + return !lstrcmpW (buf, L"true") || + !lstrcmpW (buf, L"yes") || + !lstrcmpW (buf, L"ok") || + !lstrcmpW (buf, L"sure") || + !lstrcmpW (buf, L"okay") || + !lstrcmpW (buf, L"zhen") || + !lstrcmpW (buf, L""); +} +EXTERN_C long StringToLongA (const char *str) { return atol (str); } +EXTERN_C long StringToLongW (const WCHAR *str) { return _wtol (str); } +EXTERN_C unsigned long StringToULongA (const char *str) { return atoul (str); } +EXTERN_C unsigned long StringToULongW (const WCHAR *str) { return _wtoul (str); } +EXTERN_C long long StringToLongLongA (const char *str) { return atoll (str); } +EXTERN_C long long StringToLongLongW (const WCHAR *str) { return _wtoll (str); } +EXTERN_C unsigned long long StringToULongLongA (const char *str) { return atou64 (str); } +EXTERN_C unsigned long long StringToULongLongW (const WCHAR *str) { return _wtou64 (str); } +EXTERN_C float StringToFloatA (const char *str) { return atof (str); } +EXTERN_C float StringToFloatW (const WCHAR *str) { return _wtof (str); } +EXTERN_C double StringToDoubleA (const char *str) { return atod (str); } +EXTERN_C double StringToDoubleW (const WCHAR *str) { return _wtod (str); } + +#ifdef __cplusplus +int StringToInt (LPCSTR str) { return StringToIntA (str); } +int StringToInt (LPCWSTR str) { return StringToIntW (str); } +unsigned StringToUnsigned (LPCSTR str) { return StringToUnsignedA (str); } +unsigned StringToUnsigned (LPCWSTR str) { return StringToUnsignedW (str); } +bool StringToBool (LPCSTR str) { return StringToBoolA (str); } +bool StringToBool (LPCWSTR str) { return StringToBoolW (str); } +long StringToLong (LPCSTR str) { return StringToLongA (str); } +long StringToLong (LPCWSTR str) { return StringToLongW (str); } +unsigned long StringToULong (LPCSTR str) { return StringToULongA (str); } +unsigned long StringToULong (LPCWSTR str) { return StringToULongW (str); } +long long StringToLongLong (LPCSTR str) { return StringToLongLongA (str); } +long long StringToLongLong (LPCWSTR str) { return StringToLongLongW (str); } +unsigned long long StringToULongLong (LPCSTR str) { return StringToULongLongA (str); } +unsigned long long StringToULongLong (LPCWSTR str) { return StringToULongLongW (str); } +float StringToFloat (LPCSTR str) { return StringToFloatA (str); } +float StringToFloat (LPCWSTR str) { return StringToFloatW (str); } +double StringToDouble (LPCSTR str) { return StringToDoubleA (str); } +double StringToDouble (LPCWSTR str) { return StringToDoubleW (str); } +#endif + +#if defined (__cplusplus) && defined (__cplusplus_cli) +using namespace System; +#define toInt(_String_Managed_Object_) Int32::Parse (_String_Managed_Object_) +#define objToInt(_Object_Managed_) Convert::ToInt32 (_Object_Managed_) +#define toDouble(_String_Managed_Object_) Double::Parse (_String_Managed_Object_) +#define objToDouble(_Object_Managed_) Convert::ToDouble (_Object_Managed_) +#define toBool(_String_Managed_Object_) Boolean::Parse (_String_Managed_Object_) +bool objToBool (Object ^result) +{ + if (!result) return false; + try + { + String ^strValue = safe_cast (result); + return (strValue->ToLower () == "on"); + } + catch (InvalidCastException ^) + { + try + { + return Convert::ToBoolean (result); + } + catch (InvalidCastException ^) + { + return false; + } + } + return false; +} +#define toDateTime(_String_Managed_Object_) DateTime::Parse (_String_Managed_Object_) +#define toDateTimeObj(_Object_Managed_) Convert::ToDateTime (_Object_Managed_) +#define objectToType(_Object_Managed_, _Type_Name_) Convert::To##_Type_Name_ (_Object_Managed_) +#endif \ No newline at end of file diff --git a/appinstaller/vemani.h b/appinstaller/vemani.h new file mode 100644 index 0000000..17cd96b --- /dev/null +++ b/appinstaller/vemani.h @@ -0,0 +1,322 @@ +#pragma once +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "strcmp.h" +#include "dynarr.h" +#include "themeinfo.h" +#include "nstring.h" + +class vemanifest +{ + public: + enum class TextColor { dark = 0x000000, light = 0xFFFFFF }; + vemanifest (): available (false) {} + vemanifest (LPCSTR filename): available (false) { create (filename); } + vemanifest (const std::string &filename): available (false) { create (filename); } + vemanifest (LPCWSTR filename): available (false) { create (filename); } + vemanifest (const std::wstring &filename): available (false) { create (filename); } + vemanifest (std::ifstream &stream): available (false) { create (stream); } + vemanifest (std::fstream &stream): available (false) { create (stream); } + vemanifest (FILE *file): available (false) { create (file); } + ~vemanifest () { destroy (); } + bool create (LPCSTR filename) + { + destroy (); + pugi::xml_parse_result result = doc.load_file (filename); + available = result; return available; + } + bool create (const std::string &filename) { return create (filename.c_str ()); } + bool create (LPCWSTR filename) + { + destroy (); + std::wstring ws (filename); + std::string s = to_utf8 (ws); + pugi::xml_parse_result result = doc.load_file (s.c_str ()); + available = result; return available; + } + bool create (const std::wstring &filename) { return create (filename.c_str ()); } + bool create (std::ifstream &stream) + { + destroy (); + std::stringstream buffer; buffer << stream.rdbuf (); + std::string content = buffer.str (); + pugi::xml_parse_result result = doc.load_string (content.c_str ()); + available = result; return available; + } + bool create (std::fstream &stream) + { + destroy (); + std::stringstream buffer; buffer << stream.rdbuf (); + std::string content = buffer.str (); + pugi::xml_parse_result result = doc.load_string (content.c_str ()); + available = result; return available; + } + bool create (FILE *file) + { + destroy (); + if (!file) { return false; } + fseek (file, 0, SEEK_END); + long length = ftell (file); + fseek (file, 0, SEEK_SET); + std::string content (length, '\0'); + fread (&content [0], 1, length, file); + pugi::xml_parse_result result = doc.load_string (content.c_str ()); + available = result; return available; + } + void destroy () { if (!available) return; doc.reset (); available = false; } + bool valid () const { return available; } + std::string display_name (const std::string &id = "App") const + { + pugi::xml_node visual = visual_element_node (id); + return visual ? visual.attribute ("DisplayName").as_string () : ""; + } + std::wstring display_name (const std::wstring &id = L"App") const { return pugi::as_wide (this->display_name (pugi::as_utf8 (id))); } + std::string logo (const std::string &id = "App") const + { + pugi::xml_node visual = visual_element_node (id); + if (!visual) return ""; + std::string logo = visual.attribute ("Logo").as_string (); + return !logo.empty () ? logo : visual.attribute ("Square150x150Logo").as_string (); + } + std::wstring logo (const std::wstring &id = L"App") const { return pugi::as_wide (this->logo (pugi::as_utf8 (id))); } + std::string small_logo (const std::string &id = "App") const + { + pugi::xml_node visual = visual_element_node (id); + if (!visual) return ""; + std::string smallLogo = visual.attribute ("SmallLogo").as_string (); + return !smallLogo.empty () ? smallLogo : visual.attribute ("Square70x70Logo").as_string (); + } + std::wstring small_logo (const std::wstring &id = L"App") const { return pugi::as_wide (this->small_logo (pugi::as_utf8 (id))); } + TextColor foreground_text (const std::string &id = "App") const + { + pugi::xml_node visual = visual_element_node (id); + if (!visual) return TextColor::dark; + std::string fg = visual.attribute ("ForegroundText").as_string (); + return (fg == "light") ? TextColor::light : TextColor::dark; + } + TextColor foreground_text (const std::wstring &id = L"App") const { return (this->foreground_text (pugi::as_utf8 (id))); } + std::string lnk_32x32_logo (const std::string &id = "App") const + { + pugi::xml_node visual = visual_element_node (id); + return visual ? visual.attribute ("Lnk32x32Logo").as_string () : ""; + } + std::wstring lnk_32x32_logo (const std::wstring &id = L"App") const { return pugi::as_wide (this->lnk_32x32_logo (pugi::as_utf8 (id))); } + std::string item_display_logo (const std::string &id = "App") const + { + pugi::xml_node visual = visual_element_node (id); + if (!visual) return ""; + std::string itemLogo = visual.attribute ("ItemDisplayLogo").as_string (); + if (!itemLogo.empty ()) return itemLogo; + itemLogo = visual.attribute ("Lnk32x32Logo").as_string (); + return !itemLogo.empty () ? itemLogo : visual.attribute ("Square44x44Logo").as_string (); + } + std::wstring item_display_logo (const std::wstring &id = L"App") const { return pugi::as_wide (this->item_display_logo (pugi::as_utf8 (id))); } + bool show_name_on_tile (const std::string &id = "App") const + { + pugi::xml_node visual = visual_element_node (id); + return visual ? (std::string (visual.attribute ("ShowNameOnSquare150x150Logo").as_string ()) == "on") : false; + } + bool show_name_on_tile (const std::wstring &id = L"App") const { return (this->show_name_on_tile (pugi::as_utf8 (id))); } + std::string background_color (const std::string &id = "App") const + { + pugi::xml_node visual = visual_element_node (id); + return visual ? visual.attribute ("BackgroundColor").as_string () : ""; + } + std::wstring background_color (const std::wstring &id = L"App") const { return pugi::as_wide (this->background_color (pugi::as_utf8 (id))); } + std::string splash_screen_image (const std::string &id = "App") const + { + pugi::xml_node visual = visual_element_node (id); + if (!visual) return ""; + pugi::xml_node splash = visual.child ("SplashScreen"); + return splash ? splash.attribute ("Image").as_string () : ""; + } + std::wstring splash_screen_image (const std::wstring &id = L"App") const { return pugi::as_wide (this->splash_screen_image (pugi::as_utf8 (id))); } + std::string splash_screen_backgroundcolor (const std::string &id = "App") const + { + pugi::xml_node visual = visual_element_node (id); + if (!visual) return ""; + pugi::xml_node splash = visual.child ("SplashScreen"); + std::string bg = splash ? splash.attribute ("BackgroundColor").as_string () : ""; + return !bg.empty () ? bg : visual.attribute ("BackgroundColor").as_string (); + } + std::wstring splash_screen_backgroundcolor (const std::wstring &id = L"App") const { return pugi::as_wide (this->splash_screen_backgroundcolor (pugi::as_utf8 (id))); } + bool is_appid_exists (const std::string &id) const + { + pugi::xml_node root = doc.document_element (); + std::string rootName = root.name (); + if (rootName == "Applications") + { + for (pugi::xml_node app : root.children ("Application")) + { + pugi::xml_attribute attr = app.attribute ("Id"); + if (attr && LabelEqual (std::string (attr.value ()), id)) return true; + } + return pugi::xml_node (); + } + else if (rootName == "Application") + { + pugi::xml_attribute attr = root.attribute ("Id"); + if (attr && LabelEqual (std::string (attr.value ()), id)) return true; + } + return false; + } + bool is_appid_exists (const std::wstring &id) const { return this->is_appid_exists (pugi::as_utf8 (id)); } + size_t app_ids (std::vector &output) const + { + if (!&output) return 0; + output.clear (); + pugi::xml_node root = doc.document_element (); + std::string rootName = root.name (); + if (rootName == "Applications") + { + for (pugi::xml_node app : root.children ("Application")) + { + pugi::xml_attribute attr = app.attribute ("Id"); + if (attr) + { + LPCSTR lp = attr.value (); + if (lp) push_unique (output, std::string (lp)); + } + } + } + else if (rootName == "Application") + { + pugi::xml_attribute attr = root.attribute ("Id"); + if (attr) + { + if (attr) + { + LPCSTR lp = attr.value (); + if (lp) push_unique (output, std::string (lp)); + } + } + } + if (!output.size ()) push_unique (output, std::string ("App")); + return output.size (); + } + size_t app_ids (std::vector &output) const + { + if (!&output) return 0; + output.clear (); + pugi::xml_node root = doc.document_element (); + std::string rootName = root.name (); + if (rootName == "Applications") + { + for (pugi::xml_node app : root.children ("Application")) + { + pugi::xml_attribute attr = app.attribute ("Id"); + if (attr) + { + LPCSTR lp = attr.value (); + if (lp) push_unique (output, pugi::as_wide (lp)); + } + } + } + else if (rootName == "Application") + { + pugi::xml_attribute attr = root.attribute ("Id"); + if (attr) + { + if (attr) + { + LPCSTR lp = attr.value (); + if (lp) push_unique (output, pugi::as_wide (lp)); + } + } + } + if (!output.size ()) push_unique (output, std::wstring (L"App")); + return output.size (); + } + private: + pugi::xml_document doc; + bool available; + // id VisualElements ڵ㣺ڵΪ ڵ Id idڵΪ ֱӷ + pugi::xml_node visual_element_node (const std::string &id) const + { + pugi::xml_node root = doc.document_element (); + std::string rootName = root.name (); + if (rootName == "Applications") + { + for (pugi::xml_node app : root.children ("Application")) + { + pugi::xml_attribute attr = app.attribute ("Id"); + if (attr && LabelEqual (std::string (attr.value ()), id)) + { + pugi::xml_node visual = app.child ("VisualElements"); + if (visual) return visual; + } + } + return pugi::xml_node (); + } + else if (rootName == "Application") return root.child ("VisualElements"); + return pugi::xml_node (); + } + std::string to_utf8 (const std::wstring &wstr) const + { + std::wstring_convert > conv; + return conv.to_bytes (wstr); + } +}; + +class resxmldoc +{ + private: + std::wstring filepath; + pugi::xml_document doc; + bool available = false; + std::string to_utf8 (const std::wstring &wstr) const + { + std::wstring_convert > conv; + return conv.to_bytes (wstr); + } + public: + resxmldoc (const std::wstring &xmlpath) { create (xmlpath); } + void destroy () { if (!available) return; doc.reset (); available = false; } + ~resxmldoc () { destroy (); } + bool create (const std::wstring &xmlpath) + { + destroy (); + std::wstring ws (filepath = xmlpath); + std::string s = to_utf8 (ws); + pugi::xml_parse_result result = doc.load_file (s.c_str ()); + available = result; return available; + } + std::string get (const std::string &id) const + { + auto root = doc.first_child (); + auto nodes = root.children (); + for (auto it : nodes) + { + if (IsNormalizeStringEquals (std::string (it.attribute ("id").as_string ()), id)) + { + auto scales = it.children (); + std::map s_v; + for (auto it_s : scales) + { + std::string dpi = it_s.attribute ("dpi").as_string (); + if (IsNormalizeStringEquals (dpi.c_str (), "default")) { s_v [0] = it_s.text ().get (); } + else + { + try { s_v [it_s.attribute ("dpi").as_int ()] = it_s.text ().get (); } + catch (...) {} + } + } + + break; + } + } + return ""; + } + std::wstring get (const std::wstring &id) const { return pugi::as_wide (get (WStringToString (id)).c_str ()); } + std::wstring operator [] (const std::wstring &id) const { return get (id); } + std::wstring operator [] (const std::wstring &id) { return get (id); } + std::string operator [] (const std::string &id) const { return get (id); } + std::string operator [] (const std::string &id) { return get (id); } +}; \ No newline at end of file diff --git a/appinstaller/version.h b/appinstaller/version.h new file mode 100644 index 0000000..2253e1c --- /dev/null +++ b/appinstaller/version.h @@ -0,0 +1,124 @@ +#pragma once +#include +#include +#include +typedef uint64_t UINT64; +typedef int64_t INT64; +typedef uint16_t UINT16; +typedef struct version +{ + UINT16 major = 0, minor = 0, build = 0, revision = 0; + version (UINT64 value): + major ((value >> 0x30) & 0xFFFF), minor ((value >> 0x20) & 0xFFFF), + build ((value >> 0x10) & 0xFFFF), revision ((value) & 0xFFFF) {} + version (UINT16 major, UINT16 minor, UINT16 build, UINT16 revision): + major (major), minor (minor), build (build), revision (revision) {} + version (const std::wstring &verstr) { this->interpret (verstr); } + version (const std::string &verstr) { this->interpret (verstr); } + version () {} + version (const version &other): major (other.major), minor (other.minor), build (other.build), revision (other.revision) {} + version (version &&other) noexcept: major (other.major), minor (other.minor), build (other.build), revision (other.revision) {} + version &operator = (const version &other) + { + if (this != &other) + { + major = other.major; + minor = other.minor; + build = other.build; + revision = other.revision; + } + return *this; + } + version &operator = (version &&other) noexcept + { + if (this != &other) + { + major = other.major; + minor = other.minor; + build = other.build; + revision = other.revision; + } + return *this; + } + version &operator = (UINT64 value) { this->data (value); return *this; } + UINT64 data () const { return (((UINT64)major) << 48) | (((UINT64)minor) << 32) | (((UINT64)build) << 16) | ((UINT64)revision); } + UINT64 data (UINT64 value) + { + major = (value >> 48) & 0xFFFF; + minor = (value >> 32) & 0xFFFF; + build = (value >> 16) & 0xFFFF; + revision = value & 0xFFFF; + return value; + } + std::wstring stringifyw () const + { + std::wstringstream ss; + ss << major << L'.' << minor << L'.' << build << L'.' << revision; + return ss.str (); + } + std::string stringify () const + { + std::stringstream ss; + ss << major << '.' << minor << '.' << build << '.' << revision; + return ss.str (); + } + version &interpret (const std::wstring &verstr) + { + auto result = split (verstr); + if (result.size () > 0) this->major = _wtoi (result [0].c_str ()); + if (result.size () > 1) this->minor = _wtoi (result [1].c_str ()); + if (result.size () > 2) this->build = _wtoi (result [2].c_str ()); + if (result.size () > 3) this->revision = _wtoi (result [3].c_str ()); + return *this; + } + version &interpret (const std::string &verstr) + { + auto result = split (verstr); + if (result.size () > 0) this->major = atoi (result [0].c_str ()); + if (result.size () > 1) this->minor = atoi (result [1].c_str ()); + if (result.size () > 2) this->build = atoi (result [2].c_str ()); + if (result.size () > 3) this->revision = atoi (result [3].c_str ()); + return *this; + } + bool empty () const { return *(UINT64 *)this == 0; } + friend bool operator == (const version &l, const version &r) { return *(UINT64 *)&l == *(UINT64 *)&r; } + friend bool operator == (const version &l, const UINT64 &r) { return l.data () == r; } + friend bool operator == (const UINT64 &r, const version &l) { return l.data () == r; } + friend bool operator < (const version &l, const version &r) { return l.data () < r.data (); } + friend bool operator > (const version &l, const version &r) { return l.data () > r.data (); } + friend bool operator <= (const version &l, const version &r) { return l.data () <= r.data (); } + friend bool operator >= (const version &l, const version &r) { return l.data () >= r.data (); } + friend bool operator != (const version &l, const version &r) { return *(UINT64 *)&l != *(UINT64 *)&r; } + explicit operator bool () const { return !this->empty (); } + bool operator ! () { return this->empty (); } + friend std::ostream &operator << (std::ostream &o, const version &v) { return o << v.major << '.' << v.minor << '.' << v.build << '.' << v.revision; } + friend std::wostream &operator << (std::wostream &o, const version &v) { return o << v.major << '.' << v.minor << '.' << v.build << '.' << v.revision; } + bool equals (const version &r) const { return *this == r; } + INT64 compare (const version &r) const { return this->data () - r.data (); } + static version parse (const std::wstring &value) { return version (value); } + static version parse (const std::string &value) { return version (value); } + static std::wstring stringifyw (const version &v) { return v.stringifyw (); } + static std::string stringify (const version &v) { return v.stringify (); } + static bool equals (const version &l, const version &r) { return l == r; } + static INT64 compare (const version &l, const version &r) { return l.data () - r.data (); } + static version decode (UINT64 value) { return version (value); } + static UINT64 encode (const version &v) { return v.data (); } + protected: + template std::vector split (const StringType &str, typename StringType::value_type delimiter1 = '.', typename StringType::value_type delimiter2 = ',') + { + std::vector result; + std::basic_stringstream ss (str); + StringType segment; + while (std::getline (ss, segment, delimiter1)) + { + size_t pos = 0; + while ((pos = segment.find (delimiter2)) != StringType::npos) + { + result.push_back (segment.substr (0, pos)); + segment.erase (0, pos + 1); + } + if (!segment.empty ()) result.push_back (segment); + } + return result; + } +} Version; \ No newline at end of file diff --git a/certmgr/certmgr.rc b/certmgr/certmgr.rc new file mode 100644 index 0000000..b98aca9 Binary files /dev/null and b/certmgr/certmgr.rc differ diff --git a/certmgr/certmgr.vcxproj b/certmgr/certmgr.vcxproj index 01b2894..1d2d2d8 100644 --- a/certmgr/certmgr.vcxproj +++ b/certmgr/certmgr.vcxproj @@ -148,6 +148,7 @@ + @@ -174,6 +175,9 @@ Create + + + diff --git a/certmgr/certmgr.vcxproj.filters b/certmgr/certmgr.vcxproj.filters index e923a7e..f17ca1d 100644 --- a/certmgr/certmgr.vcxproj.filters +++ b/certmgr/certmgr.vcxproj.filters @@ -27,6 +27,9 @@ 头文件 + + 头文件 + @@ -39,4 +42,9 @@ 源文件 + + + 资源文件 + + \ No newline at end of file diff --git a/certmgr/resource.h b/certmgr/resource.h new file mode 100644 index 0000000..5312ac3 --- /dev/null +++ b/certmgr/resource.h @@ -0,0 +1,14 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by certmgr.rc + +// ¶һĬֵ +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 101 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1001 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/dlltest/main.cpp b/dlltest/main.cpp index 18a83bd..a61271b 100644 --- a/dlltest/main.cpp +++ b/dlltest/main.cpp @@ -195,24 +195,14 @@ int main (int argc, char *argv []) std::wcout << L"\\> "; std::wstring pkgPathStr = L"E:\\Profiles\\Bruce\\Desktop\\Discourse.appx"; pkgPathStr = L"F:\\BaiduNetdiskDownload\\Collection4\\Microsoft.BingFinance_2015.709.2014.2069_neutral_~_8wekyb3d8bbwe\\FinanceApp_3.0.4.336_x86.appx"; - pkgPathStr = L"F:\\BaiduNetdiskDownload\\Collection4\\Microsoft.BingFinance_2015.709.2014.2069_neutral_~_8wekyb3d8bbwe.appxbundle"; - pkgPathStr = L""; + //pkgPathStr = L"F:\\BaiduNetdiskDownload\\Collection4\\Microsoft.BingFinance_2015.709.2014.2069_neutral_~_8wekyb3d8bbwe.appxbundle"; + //pkgPathStr = L""; if (pkgPathStr.empty ()) std::getline (std::wcin, pkgPathStr); pkgPathStr.erase ( std::remove (pkgPathStr.begin (), pkgPathStr.end (), L'\"'), pkgPathStr.end () ); - std::wcout << L"Installing ..." << std::endl; - std::wstring ec, dm; - HRESULT hr = AddAppxPackage (pkgPathStr, [] (int progress) { - std::wcout << L"\r " << progress << L"%"; - }, ec, dm); - if (SUCCEEDED (hr)) std::wcout << std::endl << L"Installed Successfully." << std::endl; - else - { - std::wcout << std::endl << L"Exception: " << ec << L"(0x" << std::hex << std::setw (8) << std::setfill (L'0') << hr << L")" << std::endl; - std::wcout << L"Detail Message: " << dm << std::endl; - } + read_package (pkgPathStr); system ("pause"); return 0; } \ No newline at end of file diff --git a/notice/notice.rc b/notice/notice.rc index 40bf078..6cf56bb 100644 Binary files a/notice/notice.rc and b/notice/notice.rc differ diff --git a/pkgmgr/pkgmgr.rc b/pkgmgr/pkgmgr.rc index 1428052..11fadfa 100644 Binary files a/pkgmgr/pkgmgr.rc and b/pkgmgr/pkgmgr.rc differ diff --git a/pkgread/pkgread.cpp b/pkgread/pkgread.cpp index bfe1b99..99ddf38 100644 --- a/pkgread/pkgread.cpp +++ b/pkgread/pkgread.cpp @@ -1068,7 +1068,8 @@ ULONG DestroyAppxFileStream (_In_ HANDLE hFileStream) if (!hFileStream) return 0; IStream *ptr = reinterpret_cast (hFileStream); if (!ptr) return 0; - return ptr->Release (); + ULONG ret = ptr->Release (); + return ret; } HANDLE GetAppxBundleApplicationPackageFile (_In_ HPKGREAD hReader) { diff --git a/pkgread/pkgread.rc b/pkgread/pkgread.rc index cc870e1..0856007 100644 Binary files a/pkgread/pkgread.rc and b/pkgread/pkgread.rc differ diff --git a/shared/AppInstaller.VisualElementsManifest.xml b/shared/AppInstaller.VisualElementsManifest.xml new file mode 100644 index 0000000..ecf202d --- /dev/null +++ b/shared/AppInstaller.VisualElementsManifest.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/shared/VisualElements/150x150.png b/shared/VisualElements/150x150.png new file mode 100644 index 0000000..88f58b4 Binary files /dev/null and b/shared/VisualElements/150x150.png differ diff --git a/shared/VisualElements/150x150_Metadata.xml b/shared/VisualElements/150x150_Metadata.xml new file mode 100644 index 0000000..3809fcb --- /dev/null +++ b/shared/VisualElements/150x150_Metadata.xml @@ -0,0 +1,9 @@ + + + iVBORw0KGgoAAAANSUhEUgAAAQAAAAEACAYAAABccqhmAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAAEnQAABJ0Ad5mH3gAAA3kSURBVHhe7d2NkR3HkUVhmkSTaJJMkCkyQSbQElCVqjOIB2AG815O/9TtPl8EYyUIyOnMrLxL7m7E/qH7+Oeff/797du3v8d//YtfknQHdfzjr+8MAukmxqH/cPyPDALpwsZxf3j8jwwCXUo9Zh71v/ml26nex18vMQgUrR4vj/jR7UKgep6t9xgEilKP9Z3Df3SbEKheZ8tfZxBoafU4Pzn8R5cPgepxtrotg0BLqcf4wuE/umwIVG+zxf0YBDpVPb7m4T+6XAhUT7O1YxgEOlQ9tg0O/9FlQqB6mS0dzyDQrupxbXz4j+JDoHqYrZzLINCm6jHtePiPYkOgvn22sA6DQF9Sj+egw38UFwL1zfPT12QQ6CX1WE44/EcxIVDfOj95fQaBfqsex8mH/2j5EKhvnJ+axSDQD+oxLHT4j5YNgfq2+Ym5DIKbq+UveviPlguB+qb5addgENxMLTvg8B8tEwL1LfOTrscguLhabtjhPzo9BOob5qdcm0FwMbXM4MN/dFoI1M+en3AfBkG4Wt5FDv/R4SFQP3P+6HsyCMLUsi54+I8OC4H6WfNHyiBYXC3n4of/aPcQqJ8xf5QeGQSLqWXc6PAf7RYCVXv+CH3EIDhZDf+mh/9o8xComrO0nmEQHKyG7eH/YLMQqFqzpF5lEOyshuvhf+jLIVA1Zil9hUGwsRqmh/+UdgjUn50ltBWD4ItqeB7+y14Ogfoz849qDwbBi2pYHv6XPB0C9XvnH9HeDIJP1HA8/M18GgL1e+Zv1ZEMgg+Mofggt/VhCNR/Nn+LzmIQvGMMxIe5rV9CoH5t/kdagUHwkzEMH+i2vodA/ev5S1qNQfBgDMKHuq2apzMNYBBgDMEHu4G3B1V/8a8VwCAYxgAMgaaPHlD9mkGQ4/ZBMJo3BF7w7IOp32MQ5Lh1EIzGDYFPdB9I/RmDIMdtg2A0bQi8Y6sHUTUMghy3DILRsCGAvR5A1TQIctwuCEaztw6BoxZeP8MgyHGrIBiN3i4Ezlpw/UyDIMdtgmA0eYsQWGWh9Q0GQY5bBMFo8LIhsOoC65sMghyXD4LR3KVCIGVh9Y0GQY5LB8FoLD4EUhdU32wQ5LhsEIymIkPgKgupHgyCHJcMgtFQTAhcNYmrJ4Mgx+Xe4Whm6RC46uH/rHo0CHJc6l2ORpYLgbsc/s+qZ4Mgx2Xe6WhiiRC46+H/rGZgEOS4xLsdDZwWAh7++2omBkGO+Hc8Pv7QEPDwn1MzMghyRL/r8eG7h4CH31MzMwhyxL7z8dG7hICHv42aoUGQI/Ldjw/eLAQ8/H3UTA2CHHF3MD72SyHg4R+jZmwQ5Ii6i/GhL4eAh3+OmrlBkCPmTsZHPhUCHv4aagcGQYyn/z9Un6o+dH7vrzz8NdVODIKlZRz/m/rg+d2Th5+hdmQQLCfr+N/Uh3v4mWpnBsESMo//jQGQrXZnEJwm+/gLjfiPAOFqdwbBofKPv9DMdwZBttqdQbC7axx/oaFfGATZancGwS6uc/yFpj5kEGSr3RkEm7nW8Rca+5RBkK12ZxB8yfWOv9Dc0wyCbLU7g+Bl1zz+QoMvMwiy1e4Mgqdc9/gLTbYZBNlqdwbBh659/IVGv8wgyFa7Mwh+cP3jLzS7GYMgW+3OILjJ8Rca3pxBkK12d9MguM/xF5rejUGQrXZ3oyC41/EXGt+dQZCtdnfxILjf8ReaP4xBkK12d8EguOfxFwZwOIMgW+3uIkFw3+MvDOE0BkG22l1wENz7+AuDOJ1BkK12FxYEHn9hGMswCLLV7gKCwON/w0CWYxBkq90tGgQe/yOGsiyDIFvtbqEg8Ph/xmCWZxBkq92dHAQe/3sYTgyDIFvt7oQg8Pg/woDiGATZancHBYHH/zsMKZZBkK12t2MQePyfYVDxDIJstbuNg8DjfwbDugyDIFvtboMg8PifxcAuxyDIVrtrBoHH/wqGdlkGQbba3QtB4PG/isFdnkGQrXb3SRB4/B0M7zYMgmy1u3eCwOPvYoC3YxBkq92xQ4//K+oY7swgyFQ7e+fvBGIs8+7m58ggyFA78vA3ND9LbwyCNdVOPPwdzM/TzwyCNdQOPPwdzc/URwyCc9TMPfwDzM/VZwyCY9SMPfwDzc/WswyCfdRMPfwTzM/XqwyCbdQMPfwTzTbUZRD01Mw8/AXMdvRVBsFzakYe/kJmW9qKQfC+momHv6DZnrZmEEw1Aw9/YbNN7eWuQVA9e/gBZrva210eVPXo4QeZbesoV31g1ZOHH2i2r6Nd5cFVDx5+sDkGnSX1AdY3e/gXMMehs6U8yPpGD/9C5li0ilUfaH2Th39BczxazSoPtr7Bw7+wOSat6qwHXD/Tw7+BOS6t7qgHXT/Dw7+ROTal2OuBV00P/4bm+JRmqwdfNTz8G5tjVCrW2EaZOB7+RuY4lYo1tlEmhoe/sTlWpWKNbZRZnoe/kzlepWKNbZRZloe/szlmpWKNbZRZjod/kDlupWKNbZRZhod/sDl2pWKNbZQ5nYd/kjl+pWKNbZQ5jYd/srkGpWKNUg/vSKFYo9TDO1Io1ij18I4UijVKPbwjhWKNUg/vSKFYo9TDO1Io1ij18I4UijVKPbwjhWKNbf4f4tzcfEZKxRrbKOP/Rd5dzfUrFWtso8x3BsHNzLUrFWtso8wvDIKbmOtWKtbYRpkPGQQXN9esVKyxjTKfMgguaq5XqVhjG2WeZhBczFyrUrHGNsq8zCC4iLlOpWKNbZRpOysI6mfWz+bfquv/W1Qs1thGmS87KgjqZ/Cz/o9fVhdzVCjW2EaZzewVBFXz8fDf8B+rizkqFGtso8zmtgqCqvHe4b/ht6mLOSoUa2yjzG66QVB/5neH/4bfri7mqFCssY0yu3s2COr3PHP4b/hj6mKOCsUa2yhzmI+CoH7tlcN/wx9XF3NUKNbYRpnDvQVB/dU5/De0oS7mqFCssY0ysWhDXcxRoVhjG2Vi0Ya6mKNCscY2ysSiDXUxR4VijW2UiUUb6mKOCsUa2ygTizbUxRwVijW2USYWbaiLOSoUa2yjTCzaUBdzVCjW2EaZWLShLuaoUKyxjTKxaENdzFGhWGMbZWLRhrqYo0KxxjbKxKINdTFHhWKNbZSJRRvqYo4KxRrbKBOLNtTFHBWKNbZRJhZtqIs5KhRrbKNMLNpQF3NUKNbYRplYtKEu5qhQrLGNMrFoQ13MUaFYYxtlYtGGupijQrHGNsrEog11MUeFYo1tlIlFG+pijgrFGtsoE4s21MUcFYo1tlEmFm2oizkqFGtso0ws2lAXc1Qo1thGmVi0oS7mqFCssY0ysWhDXcxRoVhjG2Vi0Ya6mKNCscY2ysSiDXUxR4VijW2UiUUb6mKOCsUa2ygTizbUxRwVijW2USYWbaiLOSoUa2yjTCzaUBdzVCjW2EaZWLShLuaoUKyxjTKxaENdzFGhWGMbZWLRhrqYo0KxxjbKxKINdTFHhWKNbZSJRRvqYo4KxRrbKBOLNtTFHBWKNbZRJhZtqIs5KhRrbKNMLNpQF3NUKNbYRplYtKEu5qhQrLGNMrFoQ11jhv+Zo1Qi1thGmVi0oa5v3779i1kqEGtso0ws2lDXmOGfc5RKxBrbKBOLNvQV4+8C/maeCsMK2ygTizb0FWOOf81xKg0rbKNMLNrQV42/C/gvM1UQ1tdGmVi0oa8as/R/FhCI9bVRJhZtaAtjnv6jQBhW10aZWLShrYyZGgJBWFsbZWLRhrY05moIhGBlbZSJRRva2pjtn/6vB9fHutooE4s2tJcx478MgnWxpjbKxKIN7W3M2iBYEOtpo0ws2tBRxswNgoWwljbKxKINHW3M3iBYAOtoo0ws2tBZxg4MghOxhjbKxKINnW3swiA4AeNvo0ws2tAqxk4MggMx9jbKxKINrWbsxiA4AONuo0ws2tCqxo4Mgh0x5jbKxKINrW7syiDYAeNto0ws2lCKsTODYEOMtY0ysWhDacbuDIINMM42ysSiDaUaOzQIvoAxtlEmFm0o3dilQdDA+NooE4s2dBVjpwbBCxhbG2Vi0YauZuzWIHgC42qjTCza0FWNHRsEv8GY2igTizZ0dWPXBsE7GE8bZWLRhu5i7NwgeMBY2igTizZ0N2P3BsHAONooE4s2dFfjDdw6CBhDG2Vi0YbubryFWwYB7bdRJtV/aEOaxqO4VRDQdhtlIo09/4s2pB+N93GLIKDdNsqk+pM2pPeNR3LpIKDNNsrEqZ3SgvS58WYuGQS010aZRH/RgvS8ejhXCgLaaqNMlLG///L5Us94R5cIAtppo0wa/9lf2xiPKToIaKONMkn8W39trx5WYhDw+W2USeHxa1/1yJKCgM9uo0wCj1/HqQeXEAR8bhtllsUO/Gd+nWM8vqWDgM9so8xymLn/XV9rqMe4YhDweW2UWYaHr6XV41wpCPisNsqczsNXlHqsKwQBn9NGmdN4+IpWj/fMIOAz2ihzOA9fl1KP+Ywg4Me3UeYwHr4urR73kUHAj22jzO48fN1KPfYjgoAf10aZ3Xj4urV6/HsGAT+mjTKb8/ClB3UMewQB5dsosxkPX/qNOo4tg4CybZT5Mg9fekEdyxZBQLk2yrR5+NIX1PF8JQgo00aZl3n40obqmDpBwB9vo8zTPHxpR3VcrwQBf6yNMp/y8KUD1bE9EwT89jbKfMjDl05Ux/e7IOC3tVHmFx6+tJA6xveCgP+4jTLfefjSwuo4H4OAX26jjIcvJaljraPl37Z5+Gf644//AZoB5wgfxM0FAAAAAElFTkSuQmCC + E:\Profiles\Bruce\Desktop\新建文件夹\Microsoft.DesktopAppInstaller_1.22.11261.0_x64__8wekyb3d8bbwe\Assets\AppPackageAppList.targetsize-256_altform-unplated.png + 48 + 48 + 26 + 20 + \ No newline at end of file diff --git a/shared/VisualElements/44x44.png b/shared/VisualElements/44x44.png new file mode 100644 index 0000000..b42b62f Binary files /dev/null and b/shared/VisualElements/44x44.png differ diff --git a/shared/VisualElements/70x70.png b/shared/VisualElements/70x70.png new file mode 100644 index 0000000..5b46fc9 Binary files /dev/null and b/shared/VisualElements/70x70.png differ diff --git a/shared/VisualElements/70x70_Metadata.xml b/shared/VisualElements/70x70_Metadata.xml new file mode 100644 index 0000000..e3bc534 --- /dev/null +++ b/shared/VisualElements/70x70_Metadata.xml @@ -0,0 +1,9 @@ + + + iVBORw0KGgoAAAANSUhEUgAAAEIAAABCCAYAAADjVADoAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAAEnQAABJ0Ad5mH3gAAAJ8SURBVHhe7drhceJADIZhSkgpKSklXAn3L38pgdKuhFQAJ2lfM8bYgrWFd5fsM8MEhNDKX2Amk+HQdV3XdV3XVeRyuXxy9ynS/yW3H7n9ldsH5XbpBZ3P53/yU31Rdmlfar9qNxBZehzAmBuGPp/aZrUTiCy5FMDYbBhaT08/VG8gstQzAYzdhKGPUzlLPYHIErkBjFkY+jM9XK18IHL4STdZS0L85m6EsoHIwdlh8C66viM2vKvmlAtEDn0qjHEAU1p/i0DkwMUwvACmtK/5QOSwmzByApjS1zUdiBx02hLAlM6JDERmHRn9epFBDHReQCAnxu2DQzd9NJbovJWB7BuC4uCrCgLZPwTF4XcKBVImBMUCi3YMpFwIiiUeenEgZUNQepE5IgPROTPvjBuv+AXMSsfl27Kgvq6aAAbp2PVyFta+6gIYpOO38y5A69UGMEhrxNELYrSpPoBBWicWow2lO9UEMEhrxWJ0W9g9FKONPKznt+5Jq8ditNHH1X0M5uii0RhtKJlpIHpfazwsyzYMxmhD6YZePKEYWstil1CMNpRctJbFLqEYbSi5aC2LXUIx2lBy0VoWu4RitKHkorUsdgnFaEPJRWtZ7BKK0YaSi9ay2CUUow0lF61lsUsoRhtKLlrLkj9sjuwThtGGkovW8mSXh/84ycFYQ8lFaz1kp5BAGGcouWitj+y2KRDGGEouWuslO64KhJcbSi5a6ye7ZgXCywwlF63tkJ2fCoR2Q2mRzNvvOxDRZH83ENoMpTu8vo1/4z2iFzIXCE8bSldvFcCUXNwfuTj93pOhbCi9dwBjcpEfctMvgP1QMr8mgK7ruq7ruvYdDv8B85b251lfl2cAAAAASUVORK5CYII= + E:\Profiles\Bruce\Desktop\新建文件夹\Microsoft.DesktopAppInstaller_1.22.11261.0_x64__8wekyb3d8bbwe\Assets\AppPackageAppList.scale-150.png + 44 + 44 + 3 + 3 + \ No newline at end of file diff --git a/shared/VisualElements/SmallLogo.png b/shared/VisualElements/SmallLogo.png new file mode 100644 index 0000000..612edfd Binary files /dev/null and b/shared/VisualElements/SmallLogo.png differ diff --git a/shared/VisualElements/SplashLarge.png b/shared/VisualElements/SplashLarge.png new file mode 100644 index 0000000..d230c50 Binary files /dev/null and b/shared/VisualElements/SplashLarge.png differ diff --git a/shared/VisualElements/SplashScreen.png b/shared/VisualElements/SplashScreen.png new file mode 100644 index 0000000..cf2338e Binary files /dev/null and b/shared/VisualElements/SplashScreen.png differ diff --git a/shared/VisualElements/scale.xml b/shared/VisualElements/scale.xml new file mode 100644 index 0000000..14abe86 --- /dev/null +++ b/shared/VisualElements/scale.xml @@ -0,0 +1,14 @@ + + + + splash\SplashScreen.scale-100.png + splash\SplashScreen.scale-125.png + splash\SplashScreen.scale-150.png + splash\SplashScreen.scale-200.png + SplashScreen.png + + + SplashLarge.png + SplashLarge.png + + \ No newline at end of file diff --git a/shared/VisualElements/splash/SplashScreen.scale-100.png b/shared/VisualElements/splash/SplashScreen.scale-100.png new file mode 100644 index 0000000..788ff59 Binary files /dev/null and b/shared/VisualElements/splash/SplashScreen.scale-100.png differ diff --git a/shared/VisualElements/splash/SplashScreen.scale-125.png b/shared/VisualElements/splash/SplashScreen.scale-125.png new file mode 100644 index 0000000..9254e35 Binary files /dev/null and b/shared/VisualElements/splash/SplashScreen.scale-125.png differ diff --git a/shared/VisualElements/splash/SplashScreen.scale-150.png b/shared/VisualElements/splash/SplashScreen.scale-150.png new file mode 100644 index 0000000..31e8318 Binary files /dev/null and b/shared/VisualElements/splash/SplashScreen.scale-150.png differ diff --git a/shared/VisualElements/splash/SplashScreen.scale-200.png b/shared/VisualElements/splash/SplashScreen.scale-200.png new file mode 100644 index 0000000..9003659 Binary files /dev/null and b/shared/VisualElements/splash/SplashScreen.scale-200.png differ diff --git a/shared/VisualElementsManifest.xml b/shared/VisualElementsManifest.xml new file mode 100644 index 0000000..bad6ca1 --- /dev/null +++ b/shared/VisualElementsManifest.xml @@ -0,0 +1,12 @@ + + + + + + \ No newline at end of file diff --git a/shared/html/applist.html b/shared/html/applist.html new file mode 100644 index 0000000..dd4c97c --- /dev/null +++ b/shared/html/applist.html @@ -0,0 +1,357 @@ + + + + + App Launch List + + + + + + + + + +
+
+
+ +
+
+
+
+ My Application0 +
+
+
+
+ 你要如何打开这个文件? +
+
+
+
+ +
+
+
+ + + + \ No newline at end of file diff --git a/shared/html/css/pages.css b/shared/html/css/pages.css new file mode 100644 index 0000000..4b0bfe5 --- /dev/null +++ b/shared/html/css/pages.css @@ -0,0 +1,447 @@ +body { + margin: 0; + width: 100%; + height: 100%; + -ms-overflow-style: -ms-autohiding-scrollbar; + user-select: none; + -ms-user-select: none; +} + +body * { + -ms-overflow-style: -ms-autohiding-scrollbar; +} + +.pagecontainer { + padding: 0px; + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + overflow-x: hidden; + overflow-y: hidden; + box-sizing: border-box; + margin: 0px; + padding: 0px; + border: 0px; +} + +.pagecontainer.full { + padding: 0px; + top: 0; + left: 0; + width: 100%; + height: 100%; + max-width: 100%; + max-height: 100%; +} + +.pagecontainer>.page { + position: relative; + overflow-x: hidden; + overflow-y: hidden; + box-sizing: border-box; + top: 0px; + left: 0px; + width: 100%; + height: 100%; +} + +.page.padding { + padding: 24px; +} + +.page.splash { + padding: 0px; + background-color: #333; +} + +.page.splash>img { + width: 620px; + height: 300px; + position: absolute; + left: 50%; + top: 50%; + transform: translate(-50%, -50%); +} + +.page.splash progress.win-ring { + width: 30px; + height: 30px; + margin-top: 32px; + border-style: none; + background-color: transparent; + color: white; +} + +.page.splash>.content { + position: absolute; + text-align: center; + box-sizing: border-box; + top: calc(50% + 300px / 2); + left: 50%; + width: 100%; + transform: translateX(-50%); + color: white; +} + +progress.win-ring:indeterminate::-ms-fill { + animation-delay: 1.5s; +} + +.page.preinstall .content.preinstall, +.page.installsuccess .content.installsuccess, +.page.installfailed .content.installfailed { + position: absolute; + top: 0px; + left: 0px; + width: 100%; + height: calc(100% - 100px); + bottom: auto; + box-sizing: border-box; + padding: 24px 24px 0px 24px; + overflow-x: hidden; + overflow-y: auto; + word-break: break-all; + max-width: 100%; + max-height: calc(100% - 100px); +} + +.page>.controls { + position: absolute; + top: auto; + bottom: 0px; + left: 0px; + width: 100%; + max-width: 100%; + height: 100px; + max-height: 100px; + padding: 10px 24px 24px 24px; + box-sizing: border-box; + overflow-y: hidden; + overflow-x: auto; + -ms-overflow-style: -ms-autohiding-scrollbar; + /* display: flex; */ + flex-direction: row-reverse; + flex-wrap: nowrap; + align-content: center; + justify-content: space-between; + align-items: center; +} + +.page>.controls .command { + float: right; + display: flex; + flex-direction: row; + flex-wrap: nowrap; + align-content: center; + justify-content: flex-end; + align-items: center; +} + +.page>.controls .command button { + margin-left: 20px; +} + +.page>.controls .checkbox { + display: flex; + flex-direction: row; + flex-wrap: nowrap; + align-content: center; + justify-content: flex-start; + align-items: center; + -ms-flex-direction: row; + -ms-flex-wrap: nowrap; + -ms-flex-line-pack: center; + -ms-flex-pack: start; + -ms-flex-align: center; +} + +.page>.controls .checkbox input[type="checkbox"] { + min-width: 21px; + min-height: 21px; +} + +.page>.controls .checkbox label { + text-overflow: ellipsis; +} + +.page>.content p { + margin: 0; +} + +.page>.content ul { + margin: 0; + padding-left: 16px; +} + +.page>.content ul li { + margin: 0; +} + +.storelogo { + width: 90px; + height: 90px; + position: relative; + background-color: #464646; + float: right; + display: flex; + flex-direction: row; + -ms-flex-direction: row; + flex-wrap: nowrap; + -ms-flex-wrap: nowrap; + align-content: center; + -ms-flex-line-pack: center; + justify-content: center; + -ms-flex-pack: center; + align-items: center; + -ms-flex-align: center; + min-width: 90px; + min-height: 90px; + max-width: 90px; + max-height: 90px; +} + +.storelogo .filter img { + max-width: 90px; + max-height: 90px; + aspect-ratio: auto; +} + +.storelogo .filter { + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + background-color: rgba(254, 0.1); + display: flex; + flex-direction: row; + -ms-flex-direction: row; + flex-wrap: nowrap; + -ms-flex-wrap: nowrap; + align-content: center; + -ms-flex-line-pack: center; + justify-content: center; + -ms-flex-pack: center; + align-items: center; + -ms-flex-align: center; +} + +.storelogo::after { + border: 1px solid rgba(254, 254, 254, 0.1); + content: ""; + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; +} + +.page>.content .functions { + position: relative; + width: 100%; + max-width: 100%; + min-height: 10%; + overflow-x: hidden; + overflow-y: auto; + max-height: calc(100% - 10.5em); + user-select: all; + -ms-user-select: element; + box-sizing: border-box; + transition: width 0.5s cubic-bezier(0.1, 0.9, 0.2, 1), height 0.5s cubic-bezier(0.1, 0.9, 0.2, 1), top 0.5s cubic-bezier(0.1, 0.9, 0.2, 1), left 0.5s cubic-bezier(0.1, 0.9, 0.2, 1), right 0.5s cubic-bezier(0.1, 0.9, 0.2, 1), bottom 0.5s cubic-bezier(0.1, 0.9, 0.2, 1), margin 0.5s cubic-bezier(0.1, 0.9, 0.2, 1), padding 0.5s cubic-bezier(0.1, 0.9, 0.2, 1); + list-style-position: outside; + /*test*/ + border: 1px solid black; + padding: 5px; +} + +.page>.progress { + position: absolute; + top: auto; + left: 0; + right: 0; + bottom: 100px; + height: auto; + padding: 0 24px; + box-sizing: border-box; +} + +.progress * { + width: 100%; +} + +.page>.reason { + position: absolute; + top: auto; + left: 0; + bottom: 100px; + height: auto; + min-height: 10%; + padding: 0 24px; + box-sizing: border-box; + max-height: calc(100% - 10.5em); + width: 100%; + transition: all 0.5s cubic-bezier(0.1, 0.9, 0.2, 1); +} + +.page>.reason p { + margin: 0; +} + +.page>.reason textarea { + width: 100%; + height: calc(100% - 1.3em); + box-sizing: border-box; +} + +.moreinfo-content p { + margin: 0px; +} + +.moreinfo-content table tbody tr td { + border-bottom: 1px solid #ccc; + border-left: 1px solid #ddd; +} + +.page>* { + display: none; +} + +.page.splash>.splash { + display: block; +} + +.page.loading, +.page.select, +.page.preinstall, +.page.installing, +.page.installsuccess, +.page.installfailed {} + +.page.loading>.content.loading { + width: 100%; + height: 100%; + padding: 0px; + display: flex; + flex-direction: row; + flex-wrap: nowrap; + align-content: center; + justify-content: center; + align-items: center; + font-size: 16pt; + max-height: 100%; +} + +.ring-loading { + font-family: Setup, "Segoe Boot Semilight", "Segoe Boot Semilight"; +} + +.page.select>.select { + display: block; +} + +.page.select>.controls.select, +.page.installsuccess>.controls.installsuccess, +.page.installfailed>.controls.installfailed { + display: flex; +} + +.page.preinstall>.controls.preinstall, +.page.installing>.controls.installing { + display: flex; + flex-direction: row; +} + +.page.select>.controls.select>.checkbox { + display: none; +} + +.page.select>.controls.select>.command button:nth-of-type(2) { + display: none; +} + +.page.select>.content.select, +.page.preinstall>.content.preinstall, +.page.installing>.content.installing, +.page.installsuccess>.content.installsuccess, +.page.installfailed>.content.installfailed { + padding: 24px 24px 0 24px; +} + +.page:not(.multiple) .multiple { + display: none; +} + +.page.select>.content.select .multiple { + display: none; +} + +.page.select>.content.select .storelogo { + display: none; +} + +.page.select>.content.select p, +.page.select>.content.select br, +.page.select>.content.select .functions, +.page.select>.content.select .moreinfo { + display: none; +} + +.page.preinstall>.content.preinstall, +.page.installing>.content.installing, +.page.installsuccess>.content.installsuccess, +.page.installfailed>.content.installfailed { + display: block; +} + +.page.installing>.progress.installing { + display: block; +} + +.page.installing>.content.installing { + height: calc(100% - 200px); + max-height: calc(100% - 24px); +} + +.page.installing>.content.installing .pkgfunctions-label, +.page.installing>.content.installing .functions, +.page.installing>.content.installing .moreinfo, +.page.installsuccess>.content.installsuccess .pkgfunctions-label, +.page.installsuccess>.content.installsuccess .functions, +.page.installsuccess>.content.installsuccess .moreinfo, +.page.installfailed>.content.installfailed .pkgfunctions-label, +.page.installfailed>.content.installfailed .functions, +.page.installfailed>.content.installfailed .moreinfo { + display: none; +} + +.page.installing>.controls.installing .command { + display: none; +} + +.page.installsuccess>.content.installsuccess { + display: block; +} + +.page.installsuccess>.controls.installsuccess .checkbox, +.page.installfailed>.controls.installfailed .checkbox { + display: none; +} + +.page.installfailed>.reason { + display: block; +} + +.page.preinstall.multiple>.content.preinstall h1.pkgtitle, +.page.installing.multiple>.content.installing h1.pkgtitle, +.page.installsuccess.multiple>.content.installsuccess h1.pkgtitle, +.page.installfailed.multiple>.content.installfailed h1.pkgtitle { + display: none; +} + +.page.installing.multiple>.content .currentfile label, +.page.installing.multiple>.content .currentfile select, +.page.installing.multiple>.content .currentfile br { + display: none; +} \ No newline at end of file diff --git a/shared/html/css/popup.css b/shared/html/css/popup.css new file mode 100644 index 0000000..f3fb587 --- /dev/null +++ b/shared/html/css/popup.css @@ -0,0 +1,26 @@ +.popup { + width: 288px; + height: 252px; + position: absolute; + box-sizing: border-box; + max-width: 50%; + max-height: 50%; + padding: 20px; + margin: 5px; + background-color: white; + overflow-x: auto; + overflow-y: auto; + -ms-overflow-style: -ms-autohiding-scrollbar; + border: 2px solid black; +} + +.popup .content { + width: 100%; + max-height: 100%; + overflow-y: auto; + overflow-x: hidden; +} + +.popup .content.with-ctrls { + max-height: calc(100% - 40px); +} \ No newline at end of file diff --git a/shared/html/fonts/SETUP.TTF b/shared/html/fonts/SETUP.TTF new file mode 100644 index 0000000..bc89e9c Binary files /dev/null and b/shared/html/fonts/SETUP.TTF differ diff --git a/shared/html/fonts/SETUP.eot b/shared/html/fonts/SETUP.eot new file mode 100644 index 0000000..c1d2803 Binary files /dev/null and b/shared/html/fonts/SETUP.eot differ diff --git a/shared/html/fonts/SegoeIcons.eot b/shared/html/fonts/SegoeIcons.eot new file mode 100644 index 0000000..96d789a Binary files /dev/null and b/shared/html/fonts/SegoeIcons.eot differ diff --git a/shared/html/fonts/SegoeIcons.ttf b/shared/html/fonts/SegoeIcons.ttf new file mode 100644 index 0000000..67b608d Binary files /dev/null and b/shared/html/fonts/SegoeIcons.ttf differ diff --git a/shared/html/fonts/fonts.css b/shared/html/fonts/fonts.css new file mode 100644 index 0000000..c3ed66c --- /dev/null +++ b/shared/html/fonts/fonts.css @@ -0,0 +1,34 @@ +@font-face +{ + font-family: "Segoe UI Symbol"; + src: url('seguisym.eot') format('embedded-opentype'), + url('seguisym.ttf') format('truetype'); + font-weight: normal; + font-style: normal; +} + +@font-face +{ + font-family: "Segoe MDL2 Assets"; + src: url('segmdl2.eot') format('embedded-opentype'), + url('segmdl2.ttf') format('truetype'); + font-weight: normal; + font-style: normal; +} + +@font-face +{ + font-family: "Segoe Fluent Icons"; + src: url('SegoeIcons.eot') format('embedded-opentype'), + url('SegoeIcons.ttf') format('truetype'); + font-weight: normal; + font-style: normal; +} + +@font-face { + font-family: "Setup"; + src: url('SETUP.EOT') format('embedded-opentype'), + url('SETUP.TTF') format('truetype'); + font-weight: normal; + font-style: normal; +} \ No newline at end of file diff --git a/shared/html/fonts/segmdl2.eot b/shared/html/fonts/segmdl2.eot new file mode 100644 index 0000000..ef3f40d Binary files /dev/null and b/shared/html/fonts/segmdl2.eot differ diff --git a/shared/html/fonts/segmdl2.ttf b/shared/html/fonts/segmdl2.ttf new file mode 100644 index 0000000..65cddd7 Binary files /dev/null and b/shared/html/fonts/segmdl2.ttf differ diff --git a/shared/html/fonts/seguisym.eot b/shared/html/fonts/seguisym.eot new file mode 100644 index 0000000..17327f9 Binary files /dev/null and b/shared/html/fonts/seguisym.eot differ diff --git a/shared/html/fonts/seguisym.ttf b/shared/html/fonts/seguisym.ttf new file mode 100644 index 0000000..5aa78d7 Binary files /dev/null and b/shared/html/fonts/seguisym.ttf differ diff --git a/shared/html/images/SplashLarge.png b/shared/html/images/SplashLarge.png new file mode 100644 index 0000000..d230c50 Binary files /dev/null and b/shared/html/images/SplashLarge.png differ diff --git a/shared/html/images/SplashScreen.png b/shared/html/images/SplashScreen.png new file mode 100644 index 0000000..cf2338e Binary files /dev/null and b/shared/html/images/SplashScreen.png differ diff --git a/shared/html/images/splash.default.png b/shared/html/images/splash.default.png new file mode 100644 index 0000000..c951e03 Binary files /dev/null and b/shared/html/images/splash.default.png differ diff --git a/shared/html/images/storelogo.default.png b/shared/html/images/storelogo.default.png new file mode 100644 index 0000000..dcb6727 Binary files /dev/null and b/shared/html/images/storelogo.default.png differ diff --git a/shared/html/install.html b/shared/html/install.html new file mode 100644 index 0000000..ee4ecb7 --- /dev/null +++ b/shared/html/install.html @@ -0,0 +1,112 @@ + + + + + App Installer + + + + + + + + + + + + + + + + + + + + +
+
+ + Splash Screen +
+ +
+ +
+   + 请稍候... +
+ +
+
+

有多个包将要安装


+
+
+
+ +

Install {Package Title}?

+

{Package Title}

+

Microsoft Store Application

+

Publisher: {Package Publisher}

+

Version: {Package Version}


+

Functions:

+
+
    +
  • 使用全部的系统资源
  • +
+
+ 更多信息 + +
+
+
+  Installing... 0% +
+ +
+
+

Reason:

+ +
+
+
+ + +
+
+ + +
+
+
+
+ + + \ No newline at end of file diff --git a/shared/html/js/animation.js b/shared/html/js/animation.js new file mode 100644 index 0000000..5b44fc5 --- /dev/null +++ b/shared/html/js/animation.js @@ -0,0 +1,188 @@ +(function() { + "use strict"; + + var AnimationKeyFrames = { + // 弹出(从边缘) + Flyout: new(function Flyout() { + if (this._instance) { return this._instance; } + this._instance = this; + // 向顶端 + this.toTop = "WinJS-showFlyoutTop"; + // 向底端 + this.toBottom = "WinJS-showFlyoutBottom"; + // 向左 + this.toLeft = "WinJS-showFlyoutLeft"; + // 向右 + this.toRight = "WinJS-showFlyoutRight"; + // 从底端(别名,向顶端) + this.fromBottom = this.toTop; + // 从顶端(别名,向底端) + this.fromTop = this.toBottom; + // 从左侧 + this.fromLeft = this.toRight; + // 从右侧 + this.fromRight = this.toLeft; + })(), + // WinJS 内部使用,建议不用这个 + Progress: { + fadeOut: "win-progress-fade-out" + }, + // WinJS 内部使用,对于搜索框弹出搜索推荐 + SearchBox: { + // 显示搜索推荐 + showPopup: { + flyoutBelow: "WinJS-flyoutBelowSearchBox-showPopup", + flyoutAbove: "WinJS-flyoutAboveSearchBox-showPopup" + } + }, + // 渐变 + Opacity: { + // 显示 + visible: "WinJS-opacity-in", + // 消失 + hidden: "WinJS-opacity-out" + }, + // 缩放 + Scale: { + // 放大一点 + up: "WinJS-scale-up", + // 缩小一点 + down: "WinJS-scale-down" + }, + // 默认分类 + Default: { + // 从右返回 + remove: "WinJS-default-remove", + // 从左返回 + removertl: "WinJS-default-remove-rtl", + // 向右移动 + apply: "WinJS-default-apply", + // 向左移动 + applyrtl: "WinJS-default-apply-rtl" + }, + // 从边缘 + Edge: { + // 从顶部 + show: "WinJS-showEdgeUI", + // 到顶部 + hide: "WinJS-hideEdgeUI" + }, + Panel: { + // 从右侧 + show: "WinJS-showPanel", + // 从左侧 + showrtl: "WinJS-showPanel-rtl", + // 到右侧 + hide: "WinJS-hidePanel", + // 到左侧 + hidertl: "WinJS-hidePanel-rtl" + }, + Popup: { + show: "WinJS-showPopup" + }, + // 对元素的拖放 + Drag: { + // 从右复位 + sourceEnd: "WinJS-dragSourceEnd", + // 从左复位 + sourceEndRtl: "WinJS-dragSourceEnd-rtl" + }, + // 进入内容 + Content: { + // 从右进入 + enter: "WinJS-enterContent", + // 从左进入 + enterrtl: "WinJS-enterContent-rtl" + }, + Page: { + // 从右进入 + enter: "WinJS-enterPage", + // 从左进入 + enterrtl: "WinJS-enterPage-rtl" + }, + Exit: "WinJS-exit", + UpdateBadge: "WinJS-updateBadge" + }; + Object.freeze(AnimationKeyFrames); + /** + * 生成用于 element.style.animation 的字符串 + * @param {string} swKeyFrames - 动画关键帧名称 + * @param {number} uMillisecond - 动画持续时间(毫秒) + * @param {string} [swTimingFunc] - 缓动函数,默认 cubic-bezier(0.1, 0.9, 0.2, 1) + * @param {number} [uDelayMs] - 延迟时间(毫秒),默认 0 + * @param {string} [swIteration] - 播放次数,默认 "1" + * @param {string} [swDirection] - 播放方向,默认 "normal" + * @param {string} [swFillMode] - 填充模式,默认 "forwards" + * @param {string} [swPlayState] - 播放状态,默认 "" + * @returns {string} 可直接赋给 element.style.animation + */ + function generateAnimeString(swKeyFrames, uMillisecond, swTimingFunc, uDelayMs, swIteration, swDirection, swFillMode, swPlayState) { + // 默认参数 + if (!swTimingFunc) { swTimingFunc = "cubic-bezier(0.1, 0.9, 0.2, 1)"; } + if (!uDelayMs) { uDelayMs = 0; } + if (!swIteration) { swIteration = "1"; } + if (!swDirection) { swDirection = "normal"; } + if (!swFillMode) { swFillMode = "forwards"; } + if (!swPlayState) { swPlayState = ""; } + if (!uMillisecond) { uMillisecond = 500; } + if (!swKeyFrames) { swKeyFrames = AnimationKeyFrames.Flyout.toLeft; } + // 毫秒转秒 + var swDuration = (uMillisecond * 0.001) + "s"; + var swDelay = (uDelayMs * 0.001) + "s"; + // 拼接函数 + function buildOne(name) { + return name + " " + + swDuration + " " + + swTimingFunc + " " + + swDelay + " " + + swIteration + " " + + swDirection + " " + + swFillMode + + (swPlayState ? (" " + swPlayState) : ""); + } + var swResult = ""; + if (typeof swKeyFrames === "string") { + swResult = buildOne(swKeyFrames); + } else if (swKeyFrames instanceof Array) { + var parts = []; + for (var i = 0; i < swKeyFrames.length; i++) { + parts.push(buildOne(swKeyFrames[i])); + } + swResult = parts.join(", "); + } + return swResult; + } + + module.exports = { + Windows: { + UI: { + Animation: { + Keyframes: AnimationKeyFrames, + Animation: generateAnimeString, + /** + * 异步设置动画,返回一个 Promise 对象,动画结束后会执行 + * @param {HTMLElement} element 元素节点 + * @param {string|Array } swKeyFrames 动画关键帧名称 + * @param {number} uMillisecond 动画持续时间(毫秒) + * @param {string} [swTimingFunc] 缓动函数,默认 cubic-bezier(0.1, 0.9, 0.2, 1) + * @param {number} [uDelayMs] 延迟时间(毫秒),默认 0 + * @param {string} [swIteration] 播放次数,默认 "1" + * @param {string} [swDirection] 播放方向,默认 "normal" + * @param {string} [swFillMode] 填充模式,默认 "forwards" + * @param {string} [swPlayState] 播放状态,默认 "" + * @returns {Promise} + */ + RunAsync: function(element, swKeyFrames, uMillisecond, swTimingFunc, uDelayMs, swIteration, swDirection, swFillMode, swPlayState) { + return new WinJS.Promise(function(complete) { + element.style.animation = generateAnimeString(swKeyFrames, uMillisecond, swTimingFunc, uDelayMs, swIteration, swDirection, swFillMode, swPlayState); + element.addEventListener("animationend", function() { + element.style.animation = ""; + complete(); + }); + }); + } + } + } + } + }; +})(); \ No newline at end of file diff --git a/shared/html/js/bridge.js b/shared/html/js/bridge.js new file mode 100644 index 0000000..f41c198 --- /dev/null +++ b/shared/html/js/bridge.js @@ -0,0 +1,41 @@ +(function(global) { + "use strict"; + var ext = global.external; + + function blankFunc(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10) { + console.log("blankFunc called with arguments: " + arg1 + ", " + arg2 + ", " + arg3 + ", " + arg4 + ", " + arg5 + ", " + arg6 + ", " + arg7 + ", " + arg8 + ", " + arg9 + ", " + arg10); + } + + global.Bridge = { + External: ext, + Frame: { + isIe10: function() { return ext.IEFrame.Version === 10; }, + isIe11: function() { return ext.IEFrame.Version === 11; } + }, + UI: {}, + String: { + trim: function(str) { return ext.String.Trim(str); }, + tolower: function(str) { return ext.String.ToLower(str); }, + toupper: function(str) { return ext.String.ToUpper(str); }, + }, + NString: { + equals: function(str1, str2) { return ext.String.NString.NEquals(str1, str2); }, + compare: function(str1, str2) { return ext.String.NString.Compare(str1, str2); }, + empty: function(str) { return ext.String.NString.Empty(str); }, + length: function(str) { return ext.String.NString.Length(str); }, + } + } + Object.defineProperty(global.Bridge.Frame, "scale", { + get: function() { return ext.IEFrame.Scale; }, + set: function(value) { ext.IEFrame.Scale = value; return ext.IEFrame.Scale; } + }); + Object.defineProperty(global.Bridge.Frame, "version", { + get: function() { return ext.IEFrame.Version; }, + }); + Object.defineProperty(global.Bridge.UI, "dpiPercent", { + get: function() { return ext.System.UI.DPIPercent; } + }); + Object.defineProperty(global.Bridge.UI, "dpi", { + get: function() { return ext.System.UI.DPI; } + }); +})(this); \ No newline at end of file diff --git a/shared/html/js/color.js b/shared/html/js/color.js new file mode 100644 index 0000000..f39f9f2 --- /dev/null +++ b/shared/html/js/color.js @@ -0,0 +1,463 @@ +(function(global) { + "use strict"; + + function RGB(parent) { + Object.defineProperty(this, "red", { + get: function() { return parent.red; }, + set: function(value) { parent.red = value; } + }); + Object.defineProperty(this, "green", { + get: function() { return parent.green; }, + set: function(value) { parent.green = value; } + }); + Object.defineProperty(this, "blue", { + get: function() { return parent.blue; }, + set: function(value) { parent.blue = value; } + }); + this.toString = function() { return "rgb(" + parent.red + "," + parent.green + "," + parent.blue + ")"; }; + this.valueOf = function() { return parent.valueOf(); }; + this.convert = function(type) { return new type(parent); }; + Object.defineProperty(this, "hex", { + get: function() { return parent.hex; }, + set: function(value) { parent.hex = value; } + }); + Object.defineProperty(this, "color", { + get: function() { return parent; } + }); + } + + function RGBA(parent) { + parent.RGB.constructor.call(this, parent); + Object.defineProperty(this, "alpha", { + get: function() { return parent.alpha; }, + set: function(value) { parent.alpha = value; } + }); + this.toString = function() { return "rgba(" + parent.red + "," + parent.green + "," + parent.blue + "," + (parent.alpha / 255).toFixed(2) + ")"; }; + this.valueOf = function() { return parent.valueOf(); }; + } + + function HSL(parent) { + parent.RGB.constructor.call(this, parent); + /** + * @type {number} 色调 0 - 360 + */ + Object.defineProperty(this, "hue", { + get: function() { + var r = parent.red / 255, + g = parent.green / 255, + b = parent.blue / 255; + var max = Math.max(r, g, b), + min = Math.min(r, g, b); + var h, s, l = (max + min) / 2; + if (max == min) { + h = s = 0; // achromatic + } else { + var d = max - min; + s = l > 0.5 ? d / (2 - max - min) : d / (max + min); + switch (max) { + case r: + h = (g - b) / d + (g < b ? 6 : 0); + break; + case g: + h = (b - r) / d + 2; + break; + case b: + h = (r - g) / d + 4; + break; + } + h /= 6; + } + return h * 360; + }, + set: function(value) { + var r = parent.red / 255, + g = parent.green / 255, + b = parent.blue / 255; + var max = Math.max(r, g, b), + min = Math.min(r, g, b); + var h = value / 360, + s = (max == 0 ? 0 : (max - min) / (max + min)), + l = (max + min) / 2; + if (s == 0) { + r = g = b = l; // achromatic + } else { + var hue2rgb = function hue2rgb(p, q, t) { + if (t < 0) t += 1; + if (t > 1) t -= 1; + if (t < 1 / 6) return p + (q - p) * 6 * t; + if (t < 1 / 2) return q; + if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6; + return p; + }; + var q = l < 0.5 ? l * (1 + s) : l + s - l * s; + var p = 2 * l - q; + r = hue2rgb(p, q, h + 1 / 3); + g = hue2rgb(p, q, h); + b = hue2rgb(p, q, h - 1 / 3); + } + parent.red = Math.round(r * 255); + parent.green = Math.round(g * 255); + parent.blue = Math.round(b * 255); + } + }); + /** + * @type {number} 饱和度 0 - 1 + */ + Object.defineProperty(this, "saturation", { + get: function() { + var r = parent.red / 255, + g = parent.green / 255, + b = parent.blue / 255; + var max = Math.max(r, g, b), + min = Math.min(r, g, b); + var h, s, l = (max + min) / 2; + if (max == min) { + s = 0; // achromatic + } else { + var d = max - min; + s = l > 0.5 ? d / (2 - max - min) : d / (max + min); + } + return s; + }, + set: function(value) { + var r = parent.red / 255, + g = parent.green / 255, + b = parent.blue / 255; + var max = Math.max(r, g, b), + min = Math.min(r, g, b); + var h = parent.hue / 360, + s = value, + l = (max + min) / 2; + if (s == 0) { + r = g = b = l; // achromatic + } else { + var hue2rgb = function hue2rgb(p, q, t) { + if (t < 0) t += 1; + if (t > 1) t -= 1; + if (t < 1 / 6) return p + (q - p) * 6 * t; + if (t < 1 / 2) return q; + if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6; + return p; + }; + var q = l < 0.5 ? l * (1 + s) : l + s - l * s; + var p = 2 * l - q; + r = hue2rgb(p, q, h + 1 / 3); + g = hue2rgb(p, q, h); + b = hue2rgb(p, q, h - 1 / 3); + } + parent.red = Math.round(r * 255); + parent.green = Math.round(g * 255); + parent.blue = Math.round(b * 255); + } + }); + /** + * @type {number} 亮度 0 - 1 + */ + Object.defineProperty(this, "lightness", { + get: function() { + var r = parent.red / 255, + g = parent.green / 255, + b = parent.blue / 255; + var max = Math.max(r, g, b), + min = Math.min(r, g, b); + var h, s, l = (max + min) / 2; + return l; + }, + set: function(value) { + var r = parent.red / 255, + g = parent.green / 255, + b = parent.blue / 255; + var max = Math.max(r, g, b), + min = Math.min(r, g, b); + var h = parent.hue / 360, + s = parent.saturation, + l = value; + if (s == 0) { + r = g = b = l; // achromatic + } else { + var hue2rgb = function hue2rgb(p, q, t) { + if (t < 0) t += 1; + if (t > 1) t -= 1; + if (t < 1 / 6) return p + (q - p) * 6 * t; + if (t < 1 / 2) return q; + if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6; + return p; + }; + var q = l < 0.5 ? l * (1 + s) : l + s - l * s; + var p = 2 * l - q; + r = hue2rgb(p, q, h + 1 / 3); + g = hue2rgb(p, q, h); + b = hue2rgb(p, q, h - 1 / 3); + } + parent.red = Math.round(r * 255); + parent.green = Math.round(g * 255); + parent.blue = Math.round(b * 255); + } + }); + this.toString = function() { return "hsl(" + this.hue + "," + (this.saturation * 100).toFixed(2) + "%," + (this.lightness * 100).toFixed(2) + "%)"; }; + this.valueOf = function() { return parent.valueOf(); }; + } + + function HSLA(parent) { + parent.HSL.constructor.call(this, parent); + Object.defineProperty(this, "alpha", { + get: function() { return parent.alpha; }, + set: function(value) { parent.alpha = value; } + }); + this.toString = function() { return "hsla(" + this.hue + "," + (this.saturation * 100).toFixed(2) + "%," + (this.lightness * 100).toFixed(2) + "%," + (parent.alpha / 255).toFixed(2) + ")"; }; + this.valueOf = function() { return parent.valueOf(); }; + } + + function HWB(parent) { + parent.RGB.constructor.call(this, parent); + /** + * @type {number} 色调 0 - 360 + */ + Object.defineProperty(this, "hue", { + get: function() { + var r = parent.red / 255, + g = parent.green / 255, + b = parent.blue / 255; + var max = Math.max(r, g, b), + min = Math.min(r, g, b); + var h, w, b = (max + min) / 2; + if (max == min) { + h = w = 0; // achromatic + } else { + var d = max - min; + w = (max == 0 ? 0 : d / max); + switch (max) { + case r: + h = (g - b) / d + (g < b ? 6 : 0); + break; + case g: + h = (b - r) / d + 2; + break; + case b: + h = (r - g) / d + 4; + break; + } + h /= 6; + } + return h * 360; + }, + set: function(value) { + var r = parent.red / 255, + g = parent.green / 255, + b = parent.blue / 255; + var max = Math.max(r, g, b), + min = Math.min(r, g, b); + var h = value / 360, + w = parent.white, + b = (1 - w) * max; + if (max == min) { + r = g = b = max; // achromatic + } else { + var hue2rgb = function hue2rgb(p, q, t) { + if (t < 0) t += 1; + if (t > 1) t -= 1; + if (t < 1 / 6) return p + (q - p) * 6 * t; + if (t < 1 / 2) return q; + if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6; + return p; + }; + var q = b < 0.5 ? b * (1 + w) : b + w - b * w; + var p = 2 * b - q; + r = hue2rgb(p, q, h + 1 / 3); + g = hue2rgb(p, q, h); + b = hue2rgb(p, q, h - 1 / 3); + } + parent.red = Math.round(r * 255); + parent.green = Math.round(g * 255); + parent.blue = Math.round(b * 255); + } + }); + /** + * @type {number} 白色分量 0 - 1 + */ + Object.defineProperty(this, "white", { + get: function() { + var r = parent.red / 255, + g = parent.green / 255, + b = parent.blue / 255; + var max = Math.max(r, g, b), + min = Math.min(r, g, b); + var h, w, b = (max + min) / 2; + if (max == min) { + w = 0; // achromatic + } else { + var d = max - min; + w = (max == 0 ? 0 : d / max); + } + return w; + }, + set: function(value) { + var r = parent.red / 255, + g = parent.green / 255, + b = parent.blue / 255; + var max = Math.max(r, g, b), + min = Math.min(r, g, b); + var h = parent.hue / 360, + w = value, + b = (1 - w) * max; + if (max == min) { + r = g = b = max; // achromatic + } else { + var hue2rgb = function hue2rgb(p, q, t) { + if (t < 0) t += 1; + if (t > 1) t -= 1; + if (t < 1 / 6) return p + (q - p) * 6 * t; + if (t < 1 / 2) return q; + if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6; + return p; + }; + var q = b < 0.5 ? b * (1 + w) : b + w - b * w; + var p = 2 * b - q; + r = hue2rgb(p, q, h + 1 / 3); + g = hue2rgb(p, q, h); + b = hue2rgb(p, q, h - 1 / 3); + } + parent.red = Math.round(r * 255); + parent.green = Math.round(g * 255); + parent.blue = Math.round(b * 255); + } + }); + Object.defineProperty(this, "black", { + get: function() { + var r = parent.red / 255, + g = parent.green / 255, + b = parent.blue / 255; + var max = Math.max(r, g, b), + min = Math.min(r, g, b); + var h, w, b = (max + min) / 2; + if (max == min) { + w = 0; // achromatic + } else { + var d = max - min; + w = (max == 0 ? 0 : d / max); + } + return 1 - w; + }, + set: function(value) { + var r = parent.red / 255, + g = parent.green / 255, + b = parent.blue / 255; + var max = Math.max(r, g, b), + min = Math.min(r, g, b); + var h = parent.hue / 360, + w = 1 - value, + b = (1 - w) * max; + if (max == min) { + r = g = b = max; // achromatic + } else { + var hue2rgb = function hue2rgb(p, q, t) { + if (t < 0) t += 1; + if (t > 1) t -= 1; + if (t < 1 / 6) return p + (q - p) * 6 * t; + if (t < 1 / 2) return q; + if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6; + return p; + }; + var q = b < 0.5 ? b * (1 + w) : b + w - b * w; + var p = 2 * b - q; + r = hue2rgb(p, q, h + 1 / 3); + g = hue2rgb(p, q, h); + b = hue2rgb(p, q, h - 1 / 3); + } + parent.red = Math.round(r * 255); + parent.green = Math.round(g * 255); + parent.blue = Math.round(b * 255); + } + }); + Object.defineProperty(this, "alpha", { + get: function() { return parent.alpha; }, + set: function(value) { parent.alpha = value; } + }); + this.toString = function() { + if (parent.alpha == 255) { + return "hwb(" + this.hue + "," + (this.white * 100).toFixed(2) + "%," + (this.black * 100).toFixed(2) + "%)"; + } else { + return "hwb(" + this.hue + "," + (this.white * 100).toFixed(2) + "%," + (this.black * 100).toFixed(2) + "% / " + (parent.alpha / 255).toFixed(2) + ")"; + } + }; + this.valueOf = function() { return parent.valueOf(); }; + } + /** + * + * @param {number} red 红色通道值 0-255 + * @param {number} green 绿色通道值 0-255 + * @param {number} blue 蓝色通道值 0-255 + * @param {number} alpha 透明度通道值 0-255 + */ + function Color(red, green, blue, alpha) { + red = red & 0xFF; + green = green & 0xFF; + blue = blue & 0xFF; + alpha = (typeof alpha === "undefined") ? 255 : (alpha & 0xFF); + this.rgbData = (red << 16) | (green << 8) | blue; + this.alpha = alpha; + // 红色通道 + Object.defineProperty(this, "red", { + get: function() { return (this.rgbData >>> 16) & 0xFF; }, + set: function(value) { this.rgbData = ((value & 0xFF) << 16) | (this.rgbData & 0x00FFFF); } + }); + // 绿色通道 + Object.defineProperty(this, "green", { + get: function() { return (this.rgbData >>> 8) & 0xFF; }, + set: function(value) { this.rgbData = (this.rgbData & 0xFF00FF) | ((value & 0xFF) << 8); } + }); + // 蓝色通道 + Object.defineProperty(this, "blue", { + get: function() { return this.rgbData & 0xFF; }, + set: function(value) { this.rgbData = (this.rgbData & 0xFFFF00) | (value & 0xFF); } + }); + // Alpha 通道单独存储 + Object.defineProperty(this, "alpha", { + get: function() { return this._alpha; }, + set: function(value) { this._alpha = value & 0xFF; } + }); + // hex 属性 + Object.defineProperty(this, "hex", { + get: function() { + function padZero(str, length) { + while (str.length < length) str = "0" + str; + return str; + } + var r = padZero(this.red.toString(16), 2), + g = padZero(this.green.toString(16), 2), + b = padZero(this.blue.toString(16), 2), + a = padZero(this.alpha.toString(16), 2); + return this.alpha === 255 ? "#" + r + g + b : "#" + r + g + b + a; + }, + set: function(value) { + var hex = value.replace(/^#/, ""); + if (hex.length === 3) hex = hex[0] + hex[0] + hex[1] + hex[1] + hex[2] + hex[2]; + if (hex.length === 6) hex = hex + "ff"; + if (hex.length === 8) { + this.red = parseInt(hex.substr(0, 2), 16); + this.green = parseInt(hex.substr(2, 2), 16); + this.blue = parseInt(hex.substr(4, 2), 16); + this.alpha = parseInt(hex.substr(6, 2), 16); + } + } + }); + this.toString = function() { return this.hex; }; + this.valueOf = function() { + function padZero(str, length) { + while (str.length < length) str = "0" + str; + return str; + } + var r = padZero(this.red.toString(16), 2), + g = padZero(this.green.toString(16), 2), + b = padZero(this.blue.toString(16), 2), + a = padZero(this.alpha.toString(16), 2); + return "#" + r + g + b + a; + }; + this.RGB = new RGB(this); + this.RGBA = new RGBA(this); + this.HSL = new HSL(this); + this.HSLA = new HSLA(this); + this.HWB = new HWB(this); + } + module.exports = { Color: Color }; +})(this); \ No newline at end of file diff --git a/shared/html/js/dpimodes.js b/shared/html/js/dpimodes.js new file mode 100644 index 0000000..58c2ce3 --- /dev/null +++ b/shared/html/js/dpimodes.js @@ -0,0 +1,178 @@ +(function(global) { + "use strict"; + var highDpiMode = 0; // 默认不进行转换 + var dpiValue = 1.0; // 默认 100% + + function IsIeVersionElder() { + return Bridge.Frame.version < 11; + } + // 0 - 不使用 + // 1 - 转换为 px + // 2 - 转换为 pt + function setHighDpiMode(modeType) { + highDpiMode = modeType; + refreshModeDisplay(); + } + + function refreshModeDisplay() { + switch (highDpiMode) { + case 1: + convertAllPtToPx(); + break; + case 2: + convertAllPxToPt(); + break; + } + var images = document.getElementsByTagName('img'); + for (var i = 0; i < images.length; i++) { + var img = images[i]; + var parent = img.parentElement; + if (!parent) continue; + var parentWidth = parent.offsetWidth; + var parentHeight = parent.offsetHeight; + var scaledWidth = img.naturalWidth * dpiValue; + var scaledHeight = img.naturalHeight * dpiValue; + if (scaledWidth > parentWidth || scaledHeight > parentHeight) { + img.style.transform = 'none'; + } else { + img.style.transform = 'scale(' + dpiValue + ')'; + } + // console.log('Image:', img, 'Parent Size:', parentWidth, parentHeight, 'Scaled:', scaledWidth, scaledHeight, 'Transform:', img.style.transform); + } + if (IsIeVersionElder && IsIeVersionElder()) { + var contentElements = document.getElementsByClassName('content'); + var controlElements = document.getElementsByClassName('control-column-bottom'); + if (controlElements.length === 0 || contentElements.length === 0) { + console.error('未找到相应元素'); + } else { + var controlElement = controlElements[0]; + var vheight = controlElement.currentStyle ? + controlElement.currentStyle.height : + window.getComputedStyle(controlElement).height; + for (var i = 0; i < contentElements.length; i++) { + var element = contentElements[i]; + element.style.height = 'calc(100% - ' + vheight + ')'; + // console.log('Set height of', element, 'to', 'calc(100% - ' + vheight + ')'); + } + } + } + } + + function convertAllPtToPx() { + var conversionFactor = 96 / 72; + var allElements = document.getElementsByTagName("*"); + for (var i = 0; i < allElements.length; i++) { + var el = allElements[i]; + if (el.style && el.style.cssText) { + el.style.cssText = el.style.cssText.replace(/(\d+(\.\d+)?)pt/g, function(match, p1) { + var pxValue = parseFloat(p1) * conversionFactor; + return pxValue + "px"; + }); + } + } + for (var i = 0; i < document.styleSheets.length; i++) { + var styleSheet = document.styleSheets[i]; + try { + var rules = styleSheet.cssRules || styleSheet.rules; + if (rules) { + for (var j = 0; j < rules.length; j++) { + var rule = rules[j]; + if (rule.style) { + for (var k = 0; k < rule.style.length; k++) { + var propertyName = rule.style[k]; + var value = rule.style.getPropertyValue(propertyName); + if (value.indexOf("pt") !== -1) { + var newValue = value.replace(/(\d+(\.\d+)?)pt/g, function(match, p1) { + var pxValue = parseFloat(p1) * conversionFactor; + return pxValue + "px"; + }); + var priority = (rule.style.getPropertyPriority) ? rule.style.getPropertyPriority(propertyName) : ""; + rule.style.setProperty(propertyName, newValue, priority); + } + } + } + } + } + } catch (e) { + console.warn("Could not access stylesheet", styleSheet.href, e); + } + } + } + + function convertAllPxToPt() { + var conversionFactor = 72 / 96; + var allElements = document.getElementsByTagName("*"); + for (var i = 0; i < allElements.length; i++) { + var el = allElements[i]; + if (el.style && el.style.cssText) { + el.style.cssText = el.style.cssText.replace(/(\d+(\.\d+)?)px/g, function(match, p1) { + var pxValue = parseFloat(p1) * conversionFactor; + return pxValue + "pt"; + }); + } + } + for (var i = 0; i < document.styleSheets.length; i++) { + var styleSheet = document.styleSheets[i]; + try { + var rules = styleSheet.cssRules || styleSheet.rules; + if (rules) { + for (var j = 0; j < rules.length; j++) { + var rule = rules[j]; + if (rule.style) { + for (var k = 0; k < rule.style.length; k++) { + var propertyName = rule.style[k]; + var value = rule.style.getPropertyValue(propertyName); + if (value.indexOf("px") !== -1) { + var newValue = value.replace(/(\d+(\.\d+)?)px/g, function(match, p1) { + var pxValue = parseFloat(p1) * conversionFactor; + return pxValue + "pt"; + }); + var priority = (rule.style.getPropertyPriority) ? rule.style.getPropertyPriority(propertyName) : ""; + rule.style.setProperty(propertyName, newValue, priority); + } + } + } + } + } + } catch (e) { + console.warn("Could not access stylesheet", styleSheet.href, e); + } + } + } + + function setPageZoom(zoomLevel) { + // document.body.style.zoom = zoomLevel; + dpiValue = zoomLevel; + refreshModeDisplay(); + } + + module.exports = { + Windows: { + UI: { + DPI: new function() { + Object.defineProperty(this, "mode", { + get: function() { + return highDpiMode; + }, + set: function(modeType) { + setHighDpiMode(modeType); + return highDpiMode; + } + }); + this.refresh = refreshModeDisplay; + Object.defineProperty(this, "zoom", { + get: function() { + return dpiValue; + }, + set: function(zoomLevel) { + setPageZoom(zoomLevel); + return dpiValue; + } + }); + this.ptToPx = convertAllPtToPx; + this.pxToPt = convertAllPxToPt; + }() + } + } + }; +})(this); \ No newline at end of file diff --git a/shared/html/js/event.js b/shared/html/js/event.js new file mode 100644 index 0000000..033998e --- /dev/null +++ b/shared/html/js/event.js @@ -0,0 +1,338 @@ +(function(global) { + "use strict"; + + if (!global.Windows) global.Windows = {}; + if (!global.Windows.UI) global.Windows.UI = {}; + if (!global.Windows.UI.Event) global.Windows.UI.Event = {}; + + var Monitor = (function() { + var _sIdAttr = "data-monitor-id"; + var _idCounter = 1; + var _aRegistry = {}; + var _typeRegistry = {}; // 按事件类型分类缓存 + var _polling = false; + var _loopHandle = null; + var _cleanupThreshold = 30000; // 30秒清理一次 + var _lastCleanup = Date.now(); + var _checkInterval = 200; // 节流时间 + var _eventTypes = [ + "resize", + "position", + "attribute", + "child" + ]; + + // 缓存 DOM 元素引用 + var _elementCache = {}; + + function _ensureId(el) { + if (!el.getAttribute(_sIdAttr)) { + el.setAttribute(_sIdAttr, "monitor_" + (_idCounter++)); + } + return el.getAttribute(_sIdAttr); + } + + function _getElementById(id) { + if (_elementCache[id] && _elementCache[id].parentNode) { + return _elementCache[id]; + } + var el = document.querySelector("[" + _sIdAttr + "=\"" + id + "\"]"); + if (el) _elementCache[id] = el; + return el; + } + + function _getAttrSnapshot(el) { + var attrs = {}; + for (var i = 0; i < el.attributes.length; i++) { + var attr = el.attributes[i]; + attrs[attr.name] = attr.value; + } + attrs["_rect"] = el.getBoundingClientRect(); + return attrs; + } + + function _hasChanged(snapshotA, snapshotB) { + for (var key in snapshotA) { + if (snapshotA.hasOwnProperty(key)) { + if (key === "_rect") { + var a = snapshotA[key], + b = snapshotB[key]; + if (!b || a.top !== b.top || a.left !== b.left || a.width !== b.width || a.height !== b.height) { + return true; + } + } else { + if (snapshotA[key] !== snapshotB[key]) { + return true; + } + } + } + } + return false; + } + + function _pollOnce() { + var now = Date.now(); + + // 按事件类型遍历,减少不必要检查 + for (var type in _typeRegistry) { + if (!_typeRegistry.hasOwnProperty(type)) continue; + + var list = _typeRegistry[type]; + for (var i = 0; i < list.length; i++) { + var item = list[i]; + var el = _getElementById(item.id); + if (!el) { + list.splice(i--, 1); + delete _elementCache[item.id]; + continue; + } + + var newSnapshot = _getAttrSnapshot(el); + if (_hasChanged(item.snapshot, newSnapshot)) { + item.snapshot = newSnapshot; + try { + item.callback.call(el, { type: type }); + } catch (ex) { + console.error("Monitor callback error:", ex); + } + } + } + } + + // 清理过期节点 + if (now - _lastCleanup > _cleanupThreshold) { + _cleanup(); + _lastCleanup = now; + } + } + + function _startLoop() { + if (_polling) return; + _polling = true; + + function loop() { + _pollOnce(); + _loopHandle = global.requestAnimationFrame ? requestAnimationFrame(loop) : setTimeout(loop, _checkInterval); + } + loop(); + } + + function _stopLoop() { + _polling = false; + if (_loopHandle) { + if (global.cancelAnimationFrame) cancelAnimationFrame(_loopHandle); + else clearTimeout(_loopHandle); + _loopHandle = null; + } + } + + function _cleanup() { + for (var type in _typeRegistry) { + if (!_typeRegistry.hasOwnProperty(type)) continue; + var list = _typeRegistry[type]; + for (var i = 0; i < list.length; i++) { + if (!_getElementById(list[i].id)) { + list.splice(i--, 1); + delete _elementCache[list[i].id]; + } + } + } + } + + function observe(el, type, callback) { + if (_eventTypes.indexOf(type) < 0) throw new Error("Unsupported event type: " + type); + var id = _ensureId(el); + if (!_typeRegistry[type]) _typeRegistry[type] = []; + _typeRegistry[type].push({ + id: id, + callback: callback, + snapshot: _getAttrSnapshot(el) + }); + _startLoop(); + } + + function detach(el, type, callback) { + if (!_typeRegistry[type]) return; + var id = el.getAttribute(_sIdAttr); + if (!id) return; + var list = _typeRegistry[type]; + for (var i = 0; i < list.length; i++) { + if (list[i].id === id && (!callback || list[i].callback === callback)) { + list.splice(i--, 1); + delete _elementCache[id]; + } + } + } + + function clearAll() { + _typeRegistry = {}; + _elementCache = {}; + _stopLoop(); + } + + return { + observe: observe, + detach: detach, + clearAll: clearAll, + EventType: { + resize: "resize", + position: "position", + attribute: "attribute", + child: "child" + } + }; + })(); + + global.Windows.UI.Event.Monitor = Monitor; + +})(window); + +/* + +// 1) 监听元素尺寸变化 +var el = document.getElementById("box"); +Windows.UI.Event.Monitor.observe(el, "resize", function (e) { + console.log("resized", e.oldValue, e.newValue, e.rect); +}); + +// 2) 监听属性变化 +Windows.UI.Event.Monitor.observe(el, "attributeChange", function (e) { + console.log("attrs changed", e.detail); // detail.added / removed / changed +}); + +// 3) 监听附着/分离 +Windows.UI.Event.Monitor.observe(el, "attach", function (e) { + console.log("attached to doc"); +}); +Windows.UI.Event.Monitor.observe(el, "detach", function (e) { + console.log("detached from doc"); +}); + +// 4) 取消监听 +Windows.UI.Event.Monitor.unobserve(el, "resize", handler); + +*/ + +(function(global) { + "use strict"; + + var EventUtil = {}; + + /** + * 添加事件,兼容 IE10/IE11 + * @param {Element|Window|Document} el 目标元素 + * @param {string} sType 事件类型,如 "click", "resize", "scroll" + * @param {function} pfHandler 回调函数 + * @param {boolean} [bUseCapture] 是否捕获阶段,默认 false + */ + EventUtil.addEvent = function(el, sType, pfHandler, bUseCapture) { + if (!el || typeof sType !== "string" || typeof pfHandler !== "function") return; + + bUseCapture = !!bUseCapture; + + if (el.addEventListener) { + // 标准方式 + el.addEventListener(sType, pfHandler, bUseCapture); + } else if (el.attachEvent) { + // IE8-9 fallback + el.attachEvent("on" + sType, pfHandler); + } else { + // 最原始方式 + var oldHandler = el["on" + sType]; + el["on" + sType] = function(e) { + if (oldHandler) oldHandler(e || window.event); + pfHandler(e || window.event); + }; + } + }; + + /** + * 移除事件,兼容 IE10/IE11 + * @param {Element|Window|Document} el 目标元素 + * @param {string} sType 事件类型,如 "click", "resize", "scroll" + * @param {function} pfHandler 回调函数 + * @param {boolean} [bUseCapture] 是否捕获阶段,默认 false + */ + EventUtil.removeEvent = function(el, sType, pfHandler, bUseCapture) { + if (!el || typeof sType !== "string" || typeof pfHandler !== "function") return; + + bUseCapture = !!bUseCapture; + + if (el.removeEventListener) { + el.removeEventListener(sType, pfHandler, bUseCapture); + } else if (el.detachEvent) { + el.detachEvent("on" + sType, pfHandler); + } else { + var oldHandler = el["on" + sType]; + if (oldHandler === pfHandler) { + el["on" + sType] = null; + } + } + }; + + // 暴露到全局命名空间 + if (typeof module !== "undefined" && module.exports) { + module.exports = { + Windows: { + UI: { + Event: { + Util: EventUtil + } + } + } + }; + } else { + global.Windows = global.Windows || {}; + global.Windows.UI = global.Windows.UI || {}; + global.Windows.UI.Event = global.Windows.UI.Event || {}; + global.Windows.UI.Event.Util = EventUtil; + } + +})(this); +/* +使用示例: +var handler = function (e) { + console.log("事件触发", e.type); +}; + +// 添加事件 +Windows.UI.Event.Util.addEvent(window, "resize", handler); + +// 删除事件 +Windows.UI.Event.Util.removeEvent(window, "resize", handler); + +*/ +(function(global) { + "use strict"; + /** + * + * @param {function} fn + * @param {number} delay + * @param {boolean} immediate 是否在第一次立即执行(可选,默认 false) + * @returns {function} 返回一个新的函数,该函数在 delay 时间后执行 fn 函数,如果在 delay 时间内再次调用该函数,则会重新计时。 + */ + function debounce(fn, delay, immediate) { + var timer = null; + var lastCall = 0; + return function() { + var context = this; + var args = arguments; + var now = +new Date(); + var callNow = immediate && !timer; + if (now - lastCall >= delay) { + lastCall = now; + if (callNow) { + fn.apply(context, args); + } + } + clearTimeout(timer); + timer = setTimeout(function() { + lastCall = +new Date(); + if (!immediate) { + fn.apply(context, args); + } + }, delay); + }; + } + module.exports = { debounce: debounce }; +})(this); \ No newline at end of file diff --git a/shared/html/js/module.js b/shared/html/js/module.js new file mode 100644 index 0000000..6502ef3 --- /dev/null +++ b/shared/html/js/module.js @@ -0,0 +1,187 @@ +/** + * @module module + * @description 模块化相关的函数。 + + * 将模块化相关的函数封装在一个对象中,方便使用。 + * 1. directexports(cObject, cDirection):直接将对象或函数公开到指定方向。 + * 2. exports:公开的对象或函数。 + * 3. imports:已加载的脚本。 + * 4. unload:卸载已加载的脚本。 + * 5. namespace(swNameSpace, cDirection):创建命名空间。 + * 6. export(cObject, cDirection):兼容旧版本的函数。 + * 7. import(aswSrc):兼容旧版本的函数。 + * 兼容的旧版本函数需要引用预处理模块,否则不会兼容。 + */ +(function(global) { + "use strict"; + // 是否为通过构造函数创建的对象实例 + function isNewFunctionInstance(obj) { + return typeof obj === "object" && Object.getPrototypeOf(obj) !== Object.prototype; + } + var module = new function() { + if (this._instance) return this._instance; + else this._instance = this; + /** + * 便于从代码块中公开。 + * @param {Object} cObject 需要公开的内容,以对象的形式分配好命名空间和别名。如: + * { + * myFunc: function() { }, + * myObj: function () { + * // object constructor + * } + * } + * 然后默认公开到全局。则公开后可以直接通过 myFunc() 或 myObj() 调用。 + * @param {global | Object} cDirection 公开方向 + * @returns {void} + */ + this.directexports = function(cObject, cDirection) { + if (!cDirection) cDirection = global; + if (typeof cObject === "function") { + cDirection[cObject.name] = cObject; + return; + } else if (typeof cObject === "object") { + var keys = Object.keys(cObject); + for (var i = 0; i < keys.length; i++) { + if (typeof cDirection[keys[i]] === "undefined") { + cDirection[keys[i]] = {}; + } + if (isNewFunctionInstance(cObject[keys[i]])) { + cDirection[keys[i]] = cObject[keys[i]]; + } else if (typeof cObject[keys[i]] === "object") { + this.directexports(cObject[keys[i]], cDirection[keys[i]]); + } else { + cDirection[keys[i]] = cObject[keys[i]]; + } + } + } else { + cDirection = cObject; + } + }; + /** + * @property {global | Object} exports 欲公开的内容以对象的形式分配好命名空间和别名。如: + */ + Object.defineProperty(this, "exports", { + get: function() { + return global; + }, + set: function(cObject, cDirection) { + this.directexports(cObject, cDirection); + } + }); + if (typeof Array.isArray === "undefined") { + /** + * 判断是否为数组。这是函数补充。 + * @param {*} arg 需要判断的对象 + * @returns {boolean} + */ + Array.isArray = function(arg) { + return Object.prototype.toString.call(arg) === "[object Array]" || arg instanceof Array || arg instanceof HTMLCollection || arg instanceof NodeList; + }; + } + /** + * @property {HTMLCollection | NodeList | null} imports 当前页面已加载的脚本。会以 NodeList 形式返回。如果为设置,则是追加脚本,而不是覆盖。传入脚本路径或其数组。 + */ + Object.defineProperty(this, "imports", { + get: function() { + if (typeof window !== "undefined" && typeof document !== "undefined") { + var scripts = document.querySelectorAll("script[src]"); + return scripts; + } else return null; + }, + set: function(aswSrc) { + if (typeof aswSrc === "string") { + var scripts = this.imports; + if (scripts && scripts.length > 0) { + for (var i = 0; i < scripts.length; i++) { + if (scripts[i].src === aswSrc) { + return; + } + } + var script = document.createElement("script"); + script.src = aswSrc; + document.head.appendChild(script); + } + } else if (Array.isArray(aswSrc) || aswSrc instanceof Array || aswSrc instanceof HTMLCollection || aswSrc instanceof NodeList) { + for (var i = 0; i < aswSrc.length; i++) { + this.imports = aswSrc[i]; + } + } + } + }); + /** + * @property {HTMLCollection | NodeList | null} unload 卸载当前页面已加载的脚本。传入脚本路径或其数组。无法只读 + */ + Object.defineProperty(this, "unload", { + set: function(aswSrc) { + if (typeof aswSrc === "string") { + var scripts = this.imports; + if (scripts && scripts.length > 0) { + for (var i = 0; i < scripts.length; i++) { + if (scripts[i].src === aswSrc) { + scripts[i].remove(); + return; + } + } + } + } else if (Array.isArray(aswSrc) || aswSrc instanceof Array || aswSrc instanceof HTMLCollection || aswSrc instanceof NodeList) { + for (var i = 0; i < aswSrc.length; i++) { + this.unload = aswSrc[i]; + } + } + } + }); + Object.defineProperty(this, "global", { + get: function() { + return global; + } + }); + /** + * 命名空间,其实为一种字典。 + * @param {string} swNameSpace 命名空间路径。如:"WinJS.Namespace"。不能为空。命名空间之间用"."分隔。 + * @param {*} cDirection 创建的路径,默认创建到全局 + * @returns + */ + this.namespace = function(swNameSpace, cDirection) { + if (!cDirection) cDirection = global; + var strarr = (swNameSpace || "").split("."); + if (!strarr.length) strarr.push(swns); + + function setNs(strarr, direct) { + var newdirect = null; + if (!strarr.length) return direct; + else { + if (typeof direct[strarr[0]] === "undefined") { + direct[strarr[0]] = {}; + } + newdirect = direct[strarr[0]]; + } + strarr.shift(); + return setNs((strarr || []), newdirect); + } + return setNs(strarr, cDirection); + }; + if (typeof preprocess !== "undefined" && preprocess.ifdef("COMPATIBLE")) { + /** + * 兼容旧版本的函数。 + * @param {Object | Function} cObject 欲公开的对象或函数 + * @param {global | Object} cDirection 公开方向 + */ + this.export = function(cObject, cDirection) { + this.directexports(cObject, cDirection); + }; + /** + * + * @param {Array | string} aswSrc 脚本路径或其数组 + * @returns {boolean} 是否成功添加脚本。在兼容模式下只会返回真。 + */ + this.import = function(aswSrc) { + this.imports = aswSrc; + return true; + }; + } + }; + module.exports = { + module: module, + namespace: module.namespace, + }; +})(this); \ No newline at end of file diff --git a/shared/html/js/pages.js b/shared/html/js/pages.js new file mode 100644 index 0000000..a3733ae --- /dev/null +++ b/shared/html/js/pages.js @@ -0,0 +1,74 @@ +(function(global) { + "use strict"; + Object.defineProperty(global, "pagecontainer", { + get: function() { return document.querySelector(".pagecontainer"); } + }); + + function getPage() { + return pagecontainer.querySelector(".page"); + } + var supportPageList = [ + "splash", + "select", + "preinstall", + "installing", + "installsuccess", + "installfailed" + ]; + var supportMulPageList = [ + "preinstall", + "installing", + "installsuccess", + "installfailed" + ]; + + function setPage(swPageLabel, bIsMulti) { + var page = getPage(); + swPageLabel = ("" + (swPageLabel || "")); + for (var i = 0; i < supportPageList.length; i++) { + if (Bridge.NString.equals(swPageLabel, supportPageList[i])) { + page.classList.add(supportPageList[i]); + } else { + if (page.classList.contains(supportPageList[i])) page.classList.remove(supportPageList[i]); + } + } + if (page.classList.contains("multiple")) page.classList.remove("multiple"); + for (var j = 0; j < supportMulPageList.length; j++) { + if (Bridge.NString.equals(swPageLabel, supportMulPageList[j]) && bIsMulti) { + page.classList.add("multiple"); + break; + } + } + } + + function getPageLabel() { + var page = getPage(); + for (var i = 0; i < supportPageList.length; i++) { + if (page.classList.contains(supportPageList[i])) { + return supportPageList[i]; + } + } + return ""; + } + + function isMultiPage() { + var page = getPage(); + return page.classList.contains("multiple"); + } + + function setPageMultiple(bIsMulti) { + setPage(getPageLabel(), bIsMulti); + } + + module.exports = { + Page: {} + }; + Object.defineProperty(Page, "current", { + get: function() { return getPageLabel(); }, + set: function(swPageLabel) { setPage(swPageLabel, isMultiPage()); } + }); + Object.defineProperty(Page, "multiple", { + get: function() { return isMultiPage(); }, + set: function(bIsMulti) { setPage(getPageLabel(), bIsMulti); } + }); +})(this); \ No newline at end of file diff --git a/shared/html/js/polyfill-ie.js b/shared/html/js/polyfill-ie.js new file mode 100644 index 0000000..6f5df17 --- /dev/null +++ b/shared/html/js/polyfill-ie.js @@ -0,0 +1,130 @@ +(function(global) { + if (typeof msWriteProfilerMark === "undefined") { + function msWriteProfilerMark(swMark) { + if (typeof performance !== "undefined" && typeof performance.mark === "function") { + return performance.mark(swMark); + } else if (typeof console !== "undefined" && typeof console.log === "function") { + return console.log(swMark); + } + } + module.exports = { + msWriteProfilerMark: msWriteProfilerMark + }; + } +})(this); +(function(global) { + + if (typeof global.Debug === "undefined") { + var fakeDebug = {}; + + // 基本属性 + fakeDebug.debuggerEnabled = true; + fakeDebug.setNonUserCodeExceptions = false; + + // 常量 + fakeDebug.MS_ASYNC_CALLBACK_STATUS_ASSIGN_DELEGATE = 0; + fakeDebug.MS_ASYNC_CALLBACK_STATUS_JOIN = 1; + fakeDebug.MS_ASYNC_CALLBACK_STATUS_CHOOSEANY = 2; + fakeDebug.MS_ASYNC_CALLBACK_STATUS_CANCEL = 3; + fakeDebug.MS_ASYNC_CALLBACK_STATUS_ERROR = 4; + + fakeDebug.MS_ASYNC_OP_STATUS_SUCCESS = 1; + fakeDebug.MS_ASYNC_OP_STATUS_CANCELED = 2; + fakeDebug.MS_ASYNC_OP_STATUS_ERROR = 3; + + // 方法:输出 + fakeDebug.write = function(msg) { + if (console && console.log) console.log("[Debug.write] " + msg); + }; + fakeDebug.writeln = function(msg) { + if (console && console.log) console.log("[Debug.writeln] " + msg); + }; + + // 方法:断言 / 中断 + fakeDebug.assert = function(cond, msg) { + if (!cond) { + if (console && console.error) { + console.error("[Debug.assert] Assertion failed: " + (msg || "")); + } + // 可选触发断点 + // debugger; + } + }; + fakeDebug.break = function() { + debugger; + }; + + // 方法:异步跟踪(空实现) + fakeDebug.msTraceAsyncCallbackCompleted = function() {}; + fakeDebug.msTraceAsyncCallbackStarting = function() {}; + fakeDebug.msTraceAsyncOperationCompleted = function() {}; + fakeDebug.msTraceAsyncOperationStarting = function() {}; + fakeDebug.msUpdateAsyncCallbackRelation = function() {}; + + global.Debug = fakeDebug; + } + +})(this); +(function(global) { + + if (typeof global.setImmediate === "undefined") { + var nextHandle = 1; // 唯一任务 id + var tasksByHandle = {}; + var currentlyRunning = false; + + function addTask(fn, args) { + tasksByHandle[nextHandle] = function() { + fn.apply(undefined, args); + }; + return nextHandle++; + } + + function run(handle) { + if (currentlyRunning) { + // 如果已经在运行,延迟一下 + setTimeout(run, 0, handle); + } else { + var task = tasksByHandle[handle]; + if (task) { + currentlyRunning = true; + try { + task(); + } finally { + delete tasksByHandle[handle]; + currentlyRunning = false; + } + } + } + } + + function installSetImmediate() { + if (typeof MessageChannel !== "undefined") { + var channel = new MessageChannel(); + channel.port1.onmessage = function(event) { + run(event.data); + }; + return function() { + var handle = addTask(arguments[0], Array.prototype.slice.call(arguments, 1)); + channel.port2.postMessage(handle); + return handle; + }; + } else { + // fallback: setTimeout + return function() { + var handle = addTask(arguments[0], Array.prototype.slice.call(arguments, 1)); + setTimeout(run, 0, handle); + return handle; + }; + } + } + + global.setImmediate = installSetImmediate(); + + // 对应 clearImmediate + if (typeof global.clearImmediate === "undefined") { + global.clearImmediate = function(handle) { + delete tasksByHandle[handle]; + }; + } + } +})(this); \ No newline at end of file diff --git a/shared/html/js/popup.js b/shared/html/js/popup.js new file mode 100644 index 0000000..64493a8 --- /dev/null +++ b/shared/html/js/popup.js @@ -0,0 +1,60 @@ +(function(global) { + "use strict"; + var flags = { + HIDE_WHEN_CLICK_OUTSIDE: 0x00000001, + SHOW_CONTROL_BUTTONS: 0x00000002, + }; + + function Popup(content, options) { + var _content = content; + var _flags = options["flags"] || 0; + var _element = null; + _element = document.createElement("div"); + _element.className = "popup"; + _element.style.position = "absolute"; + _element.style.display = "none"; + document.body.appendChild(_element); + if (_content instanceof HTMLElement) { + _element.appendChild(_content); + } else if (typeof _content === "string") { + _element.textContent = _content; + } + Object.defineProperty(this, "content", { + get: function() { return _content; }, + set: function(value) { + _element.innerHTML = ""; + if (value instanceof HTMLElement) { + _element.appendChild(value); + } else if (typeof value === "string") { + _element.textContent = value; + } + _content = value; + } + }); + Object.defineProperty(this, "", { + + }); + } + + Popup.Options = function() { + this.flags = 0; + this.position = { + left: null, + top: null + }; + this.size = { + width: null, + height: null + }; + }; + + module.exports = { + Windows: { + UI: { + Popups: { + DisplayBlock: Popup + } + } + } + }; +})(this); \ No newline at end of file diff --git a/shared/html/js/promise.js b/shared/html/js/promise.js new file mode 100644 index 0000000..51255d5 --- /dev/null +++ b/shared/html/js/promise.js @@ -0,0 +1,241 @@ +(function(global) { + "use strict"; + + function PromisePolyfill(pfExecutor) { + var swState = "pending"; // "fulfilled" | "rejected" + var vValue = undefined; + var aHandlers = []; + var pfOnCancel = null; + + function invokeHandlers() { + if (swState === "pending") return; + for (var i = 0; i < aHandlers.length; i++) { + handle(aHandlers[i]); + } + aHandlers = []; + } + + function handle(hHandler) { + if (swState === "pending") { + aHandlers.push(hHandler); + return; + } + var pfCallback = swState === "fulfilled" ? hHandler.onFulfilled : hHandler.onRejected; + if (!pfCallback) { + if (swState === "fulfilled") { + hHandler.resolve(vValue); + } else { + hHandler.reject(vValue); + } + return; + } + try { + var vResult = pfCallback(vValue); + hHandler.resolve(vResult); + } catch (ex) { + hHandler.reject(ex); + } + } + + function resolve(vResult) { + try { + if (vResult === self) throw new TypeError("A promise cannot be resolved with itself."); + if (vResult && (typeof vResult === "object" || typeof vResult === "function")) { + var pfThen = vResult.then; + if (typeof pfThen === "function") { + pfThen.call(vResult, resolve, reject); + return; + } + } + swState = "fulfilled"; + vValue = vResult; + invokeHandlers(); + } catch (ex) { + reject(ex); + } + } + + function reject(vReason) { + swState = "rejected"; + vValue = vReason; + if (typeof PromisePolyfill.onerror === "function") { + PromisePolyfill.onerror(vReason); + } + invokeHandlers(); + } + var self = this; + try { + pfExecutor(resolve, reject, function(pfCancel) { + pfOnCancel = pfCancel; + }); + } catch (ex) { + reject(ex); + } + this.then = function(pfOnFulfilled, pfOnRejected) { + return new PromisePolyfill(function(resolve, reject) { + handle({ + onFulfilled: pfOnFulfilled, + onRejected: pfOnRejected, + resolve: resolve, + reject: reject + }); + }); + }; + this["catch"] = function(pfOnRejected) { + return this.then(null, pfOnRejected); + }; + this.done = function(pfOnFulfilled, pfOnRejected) { + this.then(pfOnFulfilled, pfOnRejected)["catch"](function(ex) { + setTimeout(function() { throw ex; }, 0); + }); + }; + this.cancel = function() { + if (pfOnCancel) { + try { pfOnCancel(); } catch (ex) {} + } + reject(new Error("Promise was canceled")); + }; + this._oncancel = pfOnCancel; + this._state = swState; + this._value = vValue; + } + PromisePolyfill.is = function(vObj) { + return vObj instanceof PromisePolyfill; + }; + PromisePolyfill.resolve = function(vValue) { + return new PromisePolyfill(function(resolve) { resolve(vValue); }); + }; + PromisePolyfill.reject = function(vReason) { + return new PromisePolyfill(function(resolve, reject) { reject(vReason); }); + }; + PromisePolyfill.all = function(aPromises) { + return new PromisePolyfill(function(resolve, reject) { + var nRemaining = aPromises.length; + var aResults = new Array(nRemaining); + if (nRemaining === 0) resolve([]); + + function resolver(iIndex) { + return function(vValue) { + aResults[iIndex] = vValue; + nRemaining--; + if (nRemaining === 0) resolve(aResults); + }; + } + for (var i = 0; i < aPromises.length; i++) { + PromisePolyfill.resolve(aPromises[i]).then(resolver(i), reject); + } + }); + }; + PromisePolyfill.race = function(aPromises) { + return new PromisePolyfill(function(resolve, reject) { + for (var i = 0; i < aPromises.length; i++) { + PromisePolyfill.resolve(aPromises[i]).then(resolve, reject); + } + }); + }; + PromisePolyfill.join = function(aPromises) { + return PromisePolyfill.all(aPromises); + }; + PromisePolyfill.any = function(aPromises) { + return new PromisePolyfill(function(resolve, reject) { + var nRemaining = aPromises.length; + var aErrors = new Array(nRemaining); + if (nRemaining === 0) reject(new Error("No promises provided.")); + + function resolver(vValue) { resolve(vValue); } + + function rejecter(iIndex) { + return function(ex) { + aErrors[iIndex] = ex; + nRemaining--; + if (nRemaining === 0) reject(aErrors); + }; + } + for (var i = 0; i < aPromises.length; i++) { + PromisePolyfill.resolve(aPromises[i]).then(resolver, rejecter(i)); + } + }); + }; + PromisePolyfill.timeout = function(pPromise, nMilliseconds) { + return new PromisePolyfill(function(resolve, reject) { + var hTimer = setTimeout(function() { + reject(new Error("Promise timed out after " + nMilliseconds + "ms")); + }, nMilliseconds); + PromisePolyfill.resolve(pPromise).then(function(vValue) { + clearTimeout(hTimer); + resolve(vValue); + }, function(ex) { + clearTimeout(hTimer); + reject(ex); + }); + }); + }; + PromisePolyfill.as = function(vValue) { + return PromisePolyfill.resolve(vValue); + }; + PromisePolyfill.wrap = function(vValue) { + return PromisePolyfill.resolve(vValue); + }; + PromisePolyfill.wrapError = function(vError) { + return PromisePolyfill.reject(vError); + }; + PromisePolyfill.thenEach = function(aValues, pfCallback) { + var aPromises = []; + for (var i = 0; i < aValues.length; i++) { + aPromises.push(PromisePolyfill.resolve(aValues[i]).then(pfCallback)); + } + return PromisePolyfill.all(aPromises); + }; + var hListeners = {}; + PromisePolyfill.addEventListener = function(sType, pfHandler) { + if (!hListeners[sType]) hListeners[sType] = []; + hListeners[sType].push(pfHandler); + }; + PromisePolyfill.removeEventListener = function(sType, pfHandler) { + if (!hListeners[sType]) return; + var aList = hListeners[sType]; + for (var i = 0; i < aList.length; i++) { + if (aList[i] === pfHandler) { + aList.splice(i, 1); + break; + } + } + }; + PromisePolyfill.dispatchEvent = function(sType, vDetail) { + if (!hListeners[sType]) return; + var aList = hListeners[sType].slice(); + for (var i = 0; i < aList.length; i++) { + try { aList[i](vDetail); } catch (ex) {} + } + }; + PromisePolyfill.supportedForProcessing = true; + PromisePolyfill.onerror = null; + if (typeof global.Promise !== "undefined") { + var p = global.Promise; + if (!p.join) p.join = p.all; + if (!p.any) p.any = PromisePolyfill.any; + if (!p.timeout) p.timeout = PromisePolyfill.timeout; + if (!p.as) p.as = p.resolve; + if (!p.wrap) p.wrap = p.resolve; + if (!p.wrapError) p.wrapError = p.reject; + if (!p.thenEach) p.thenEach = PromisePolyfill.thenEach; + if (!p.is) p.is = function(vObj) { return vObj instanceof p; }; + if (!p.supportedForProcessing) p.supportedForProcessing = true; + if (!p.addEventListener) p.addEventListener = PromisePolyfill.addEventListener; + if (!p.removeEventListener) p.removeEventListener = PromisePolyfill.removeEventListener; + if (!p.dispatchEvent) p.dispatchEvent = PromisePolyfill.dispatchEvent; + if (!p.onerror) p.onerror = null; + } + if (typeof global.WinJS !== "undefined" && typeof global.WinJS.Promise !== "undefined") { + var wp = global.WinJS.Promise; + if (!wp.resolve) wp.resolve = function(vValue) { return new wp(function(c) { c(vValue); }); }; + if (!wp.reject) wp.reject = function(vReason) { return new wp(function(c, e) { e(vReason); }); }; + if (!wp.all) wp.all = function(aPromises) { return wp.join(aPromises); }; + if (!wp.race) wp.race = PromisePolyfill.race; + global.Promise = wp; + if (typeof global.Promise === "undefined") global.Promise = wp; + } + if (typeof global.Promise === "undefined" && typeof global.WinJS === "undefined") { + global.Promise = PromisePolyfill; + } +})(this); \ No newline at end of file diff --git a/shared/html/libs/winjs/2.0/css/ui-dark.css b/shared/html/libs/winjs/2.0/css/ui-dark.css new file mode 100644 index 0000000..604fe51 --- /dev/null +++ b/shared/html/libs/winjs/2.0/css/ui-dark.css @@ -0,0 +1,5423 @@ +/*! + © Microsoft. All rights reserved. + + This library is supported for use in Windows Store apps only. + + Build: 1.0.9600.17018.winblue_gdr.140204-1946 + + Version: Microsoft.WinJS.2.0 +*/ + +/* +Modifications for top-level elements html, body and iframe. +*/ +html, body { + height: 100%; + width: 100%; + margin: 0; + -ms-user-select: none; + cursor: default; + -ms-scroll-translation: vertical-to-horizontal; +} +html { + overflow: hidden; +} +body { + -ms-content-zooming: none; +} +iframe { + border: 0; +} +html:-ms-lang(ar, dv, fa, he, ku-Arab, pa-Arab, prs, ps, sd-Arab, syr, ug, ur, qps-plocm) { + direction: rtl; +} + +/* +Explicitly define a Segoe UI font-family so that we can assign Segoe UI +Semilight to an appropriate font-weight. +*/ +@font-face { + font-family: "Segoe UI"; + font-weight: 200; + src: local("Segoe UI Light"); +} +@font-face { + font-family: "Segoe UI"; + font-weight: 300; + src: local("Segoe UI Semilight"); +} +@font-face { + font-family: "Segoe UI"; + font-weight: 400; + src: local("Segoe UI"); +} +@font-face { + font-family: "Segoe UI"; + font-weight: 600; + src: local("Segoe UI Semibold"); +} +@font-face { + font-family: "Segoe UI"; + font-weight: 700; + src: local("Segoe UI Bold"); +} +@font-face { + font-family: "Segoe UI"; + font-style: italic; + font-weight: 400; + src: local("Segoe UI Italic"); +} +@font-face { + font-family: "Segoe UI"; + font-style: italic; + font-weight: 700; + src: local("Segoe UI Bold Italic"); +} + +/* +Explicitly define font-families for Microsoft Yahei UI and Microsoft JhengHei UI so that we can fallback requests for, + Microsoft Yahei UI Semilight and Microsoft JhengHei UI Semilight, to Regular instead of Light fonts. +*/ +@font-face { + font-family: "Microsoft Yahei UI"; + font-weight: 200; + src: local("Microsoft Yahei UI Light"); +} +@font-face { + font-family: "Microsoft Yahei UI"; + font-weight: 300; + src: local("Microsoft Yahei UI"); +} +@font-face { + font-family: "Microsoft Yahei UI"; + font-weight: 500; + src: local("Microsoft Yahei UI"); +} +@font-face { + font-family: "Microsoft Yahei UI"; + font-weight: 600; + src: local("Microsoft Yahei UI Bold"); +} + +@font-face { + font-family: "Microsoft JhengHei UI"; + font-weight: 200; + src: local("Microsoft JhengHei UI Light"); +} +@font-face { + font-family: "Microsoft JhengHei UI"; + font-weight: 300; + src: local("Microsoft JhengHei UI"); +} +@font-face { + font-family: "Microsoft JhengHei UI"; + font-weight: 500; + src: local("Microsoft JhengHei UI"); +} +@font-face { + font-family: "Microsoft JhengHei UI"; + font-weight: 600; + src: local("Microsoft JhengHei UI Bold"); +} + +/* +Weight and size definitions for typographic classes and elements. +*/ +h1, .win-type-xx-large { + font-size: 42pt; + font-weight: 200; + letter-spacing: 0; + line-height: 1.1429; /* 64px when font-size is 42pt */ +} +h2, .win-type-x-large { + font-size: 20pt; + font-weight: 200; + line-height: 1.2; /* 32px when font-size is 20pt */ +} +h3, .win-type-large { + font-size: 11pt; + font-weight: 600; + line-height: 1.3636; /* 20px when font-size is 11pt */ +} +h4, .win-type-medium, code, pre, samp { + font-size: 11pt; + font-weight: 400; + line-height: 1.3636; /* 20px when font-size is 11pt */ +} +body, h5, .win-type-small, legend { + font-size: 11pt; + font-weight: 300; + line-height: 1.3636; /* 20px when font-size is 11pt */ +} +.win-type-x-small { + font-size: 11pt; + font-weight: 300; + line-height: 1.3636; /* 20px when font-size is 11pt */ +} +h6, caption, figcaption, small, .win-type-xx-small { + font-size: 9pt; + font-weight: 400; + line-height: 1.6667; /* 20px when font-size is 9pt */ +} +h1, h2, h3, h4, h5, h6 { + margin-top: 0; + margin-bottom: 0; +} +.win-type-ellipsis { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} +h1.win-type-ellipsis, .win-type-xx-large.win-type-ellipsis { + line-height: 1.4286; /* 80px when font-size is 42pt */ +} +h2.win-type-ellipsis, .win-type-x-large.win-type-ellipsis { + line-height: 1.5; /* 40px when font-size is 20pt */ +} +dt, th { + font-size: 11pt; + font-weight: 700; + line-height: 1.3636; /* 20px when font-size is 11pt */ +} +abbr, acronym, address, blockquote, cite, dl, dd, li, ol, p, q, td, tr { + font-weight: 300; +} +b, strong { + font-weight: 700; +} +em { + font-style: italic; +} + +/* +Additional letter-spacing for the document and its controls. +*/ +body, +button, input, +textarea, .win-textarea, +select, option { + letter-spacing: 0.02em; +} + +/* +Fixed font-family rules. +*/ +code, pre, samp { + font-family: "Consolas"; +} + +/* +Text input controls. +*/ + +input[type=text], input[type=password], input[type=email], input[type=number], +input[type=tel], input[type=url], input[type=search], textarea, .win-textarea { + -ms-user-select: element; + min-height: 28px; + min-width: 64px; + margin: 4px 0; + border-width: 2px; + border-style: solid; + font-size: 11pt; + font-weight: 400; + line-height: 1.3636; /* 20px when font-size is 11pt */ +} + +input[type=text], input[type=password], input[type=email], input[type=number], +input[type=tel], input[type=url], input[type=search] { + width: 276px; + padding: 0; +} +input::-ms-value { + margin: 4px 8px; +} + +textarea, .win-textarea { + overflow-y: scroll; + word-wrap: break-word; + padding: 4px 8px; + /* Leave space for autohiding scrollbar on elements that enable text selection. */ + padding-right: 17px; +} +textarea:-ms-lang(ar, dv, fa, he, ku-Arab, pa-Arab, prs, ps, sd-Arab, syr, ug, ur, qps-plocm), .win-textarea:-ms-lang(ar, dv, fa, he, ku-Arab, pa-Arab, prs, ps, sd-Arab, syr, ug, ur, qps-plocm) { + /* Flip sides for autohiding scrollbar padding on RTL layouts. */ + padding-right: 8px; + padding-left: 17px; +} +textarea { + min-width: 260px; + min-height: 39px; +} +.win-textarea { + width: 260px; + height: 39px; +} + +/* +Clear and reveal button styles. +*/ + +input::-ms-clear, input::-ms-reveal { + margin-left: 2px; +} +input:-ms-lang(ar, dv, fa, he, ku-Arab, pa-Arab, prs, ps, sd-Arab, syr, ug, ur, qps-plocm)::-ms-clear, +input:-ms-lang(ar, dv, fa, he, ku-Arab, pa-Arab, prs, ps, sd-Arab, syr, ug, ur, qps-plocm)::-ms-reveal { + margin-left: 0; + margin-right: 2px; +} + +/* +Button control. +*/ +button, input[type=button], input[type=reset], input[type=submit] { + min-height: 32px; /* content (20px) + padding + border */ + min-width: 90px; + padding: 4px 8px; + border-width: 2px; + border-style: solid; + background-clip: padding-box; + font-size: 11pt; + font-weight: 600; + line-height: 1.3636; /* 20px when font-size is 11pt */ +} + +/* +File upload control. +*/ +input[type=file] { + border: none; + min-width: 100px; + min-height: 20px; + width: 340px; + height: 32px; + padding: 0; + margin: 7px 8px 21px 8px; + background-clip: padding-box; +} +input[type=file]::-ms-value { + margin: 0; + border-width: 2px; + border-style: solid; + border-right-style: none; + border-radius: 0; + background-clip: padding-box; + font-size: 11pt; + font-weight: 400; + line-height: 1.3636; /* 20px when font-size is 11pt */ +} +input[type=file]:-ms-lang(ar, dv, fa, he, ku-Arab, pa-Arab, prs, ps, sd-Arab, syr, ug, ur, qps-plocm)::-ms-value { + border-left-style: none; + border-right-style: solid; +} +input[type=file]::-ms-browse { + margin: 0; + padding: 0 18px; + border-width: 2px; + border-style: solid; + background-clip: padding-box; + font-size: 11pt; + font-weight: 600; + line-height: 1.3636; /* 20px when font-size is 11pt */ +} + +/* +No underline or special cursor on links. +*/ +a { + text-decoration: none; + cursor: default; +} + +/* +No border on images when in a link. +*/ +img { + border-style: none; +} + +/* +Select control. +*/ +select { + min-height: 32px; + min-width: 80px; + border-width: 2px; + border-style: solid; + margin: 4px 0; + font-size: 11pt; + font-weight: 400; + line-height: 1.3636; /* 20px when font-size is 11pt */ +} +select::-ms-value { + padding: 4px 8px; + margin: 0; +} +select::-ms-expand { + border: 0; + padding-left: 6px; + padding-right: 6px; + background-color: transparent; +} + +/* +Radio button and checkbox shared styles. +*/ +input::-ms-check { + border-width: 2px; + border-style: solid; + display: inline-block; +} + +/* +Radio button. +*/ +input[type=radio] { + width: 23px; + height: 23px; + margin-left: -1px; + margin-right: 4px; + margin-bottom: -2px; +} +input[type=radio]:-ms-lang(ar, dv, fa, he, ku-Arab, pa-Arab, prs, ps, sd-Arab, syr, ug, ur, qps-plocm) { + margin-left: 4px; + margin-right: -1px; +} + +/* +Checkbox. +*/ +input[type=checkbox] { + width: 21px; + height: 21px; + margin-right: 5px; +} +input[type=checkbox]:-ms-lang(ar, dv, fa, he, ku-Arab, pa-Arab, prs, ps, sd-Arab, syr, ug, ur, qps-plocm) { + margin-right: 0; + margin-left: 5px; +} + +/* +Range control. +*/ +input[type=range] { + width: 280px; + height: auto; + padding: 17px 0 32px 0; +} +input[type=range]::-ms-track { + padding: 0; + width: auto; + height: 11px; + border-style: none; + color: transparent; /* ticks hidden by default */ +} +input[type=range]::-ms-thumb { + width: 11px; + height: 11px; + border-style: none; +} +input[type=range]::-ms-ticks-before, input[type=range]::-ms-ticks-after { + width: 100%; + height: 5px; + display: none; +} +input[type=range]:disabled::-ms-fill-lower { + margin-right: 5px; +} +input[type=range]:disabled::-ms-fill-upper { + margin-left: 6px; +} +input[type=range]:disabled:-ms-lang(ar, dv, fa, he, ku-Arab, pa-Arab, prs, ps, sd-Arab, syr, ug, ur, qps-plocm)::-ms-fill-lower { + margin-right: 0; + margin-left: 5px; +} +input[type=range]:disabled:-ms-lang(ar, dv, fa, he, ku-Arab, pa-Arab, prs, ps, sd-Arab, syr, ug, ur, qps-plocm)::-ms-fill-upper { + margin-left: 0; + margin-right: 6px; +} + +/* +Vertical range control. +*/ +input[type=range].win-vertical { + width: auto; + height: 191px; + padding: 0 17px; + writing-mode: bt-lr; +} +input[type=range].win-vertical:-ms-lang(ar, dv, fa, he, ku-Arab, pa-Arab, prs, ps, sd-Arab, syr, ug, ur, qps-plocm) { + writing-mode: bt-rl; +} +input[type=range].win-vertical::-ms-track { + width: 11px; + height: auto; +} +input[type=range].win-vertical:disabled::-ms-fill-lower { + margin-right: 0; + margin-top: 6px; +} +input[type=range].win-vertical:disabled::-ms-fill-upper { + margin-left: 0; + margin-bottom: 5px; +} +input[type=range].win-vertical:disabled:-ms-lang(ar, dv, fa, he, ku-Arab, pa-Arab, prs, ps, sd-Arab, syr, ug, ur, qps-plocm)::-ms-fill-lower { + margin-left: 0; +} +input[type=range].win-vertical:disabled:-ms-lang(ar, dv, fa, he, ku-Arab, pa-Arab, prs, ps, sd-Arab, syr, ug, ur, qps-plocm)::-ms-fill-upper { + margin-right: 0; +} + +input[type=range].win-vertical::-ms-ticks-before, +input[type=range].win-vertical::-ms-ticks-after { + width: 5px; + height: 100%; + display: none; +} + +/* +Progress control. +*/ +progress { + width: 180px; + height: 6px; + border-style: none; +} +progress.win-medium { + width: 280px; +} +progress.win-large { + width: 100%; +} +progress::-ms-fill { + border-style: none; +} +progress:indeterminate { + height: 4px; + padding: 1px 0; /* Indeterminate dots should be 4px tall, but overall control stays 6px. */ +} +@keyframes win-progress-fade-out { + from { + opacity: 1.0; + } + to { + opacity: 0.5; + } +} +progress.win-paused:not(:indeterminate) { + animation-name: win-progress-fade-out; + animation-duration: 3s; + animation-timing-function: cubic-bezier(0.03, 0.76, 0.31, 1.0); + opacity: 0.5; +} +progress.win-error::-ms-fill { + opacity: 0; +} +progress.win-ring:indeterminate::-ms-fill { + animation-name: -ms-ring; +} +progress.win-ring { + width: 20px; + height: 20px; +} +progress.win-medium.win-ring { + width: 40px; + height: 40px; +} +progress.win-large.win-ring { + width: 60px; + height: 60px; +} + +form { + margin: 0; + padding: 0; +} +legend { + margin: 0 0 10px 0; + padding: 0; + color: inherit; +} + +/* +AppBar/Flyout z-index values: + 1000 - AppBar/settings click-eating div + 1002 - AppBar + 1004 - Settings Flyout + 1006 - Flyout click-eating div + 1008 - Flyout +*/ + +@font-face { + font-family: "Segoe UI Command"; + src: local("Segoe UI Symbol"); + font-weight: normal; + font-style: normal; +} + +.win-appbarclickeater { + z-index: 1000; + background-color: transparent; + display: none; + width: 110%; + height: 110%; + left: -5%; + top: -5%; + position: fixed; + touch-action: none; + outline: 1px solid Purple; /*Necessary to block passthrough over webviews*/ + -ms-high-contrast-adjust:none; +} +.win-flyoutmenuclickeater { + z-index: 1006; + background-color: transparent; + display: none; + width: 110%; + height: 110%; + left: -5%; + top: -5%; + position: fixed; + touch-action: none; + outline: 1px solid Lime; /*Necessary to block passthrough over webviews*/ + -ms-high-contrast-adjust:none; +} + +/* +Back buttons. +*/ +.win-backbutton, .win-back { + display: inline-block; + min-width: 0; + min-height: 0; + background-clip: border-box; + box-sizing: border-box; + border-radius: 50%; + border-width: 2px; + border-style: solid; + padding: 0; + text-align: center; + + /* Normal sizing. */ + width: 41px; + height: 41px; + font-size: 14pt; + line-height: 37px; /* line-height must match the content box height. */ + vertical-align: baseline; +} + +.win-backbutton:hover, .win-backbutton:hover:active { + border-width: 2px; + border-style: solid; + border-radius: 50%; +} +.win-backbutton::before, .win-back::before { + font-family: "Segoe UI Symbol"; + font-weight: normal; + content: "\E0D5"; + vertical-align: 50%; +} +.win-backbutton:-ms-lang(ar, dv, fa, he, ku-Arab, pa-Arab, prs, ps, sd-Arab, syr, ug, ur, qps-plocm)::before, .win-back:-ms-lang(ar, dv, fa, he, ku-Arab, pa-Arab, prs, ps, sd-Arab, syr, ug, ur, qps-plocm)::before { + content: "\E0AE"; +} +.win-backbutton:disabled, .win-backbutton:disabled:active, +.win-navigation-backbutton:disabled, .win-navigation-backbutton:disabled:active { + visibility: hidden; +} + +button.win-navigation-backbutton, button.win-navigation-backbutton:active, +button.win-navigation-backbutton:hover, button.win-navigation-backbutton:active:hover +{ + display: inline-block; + padding: 2px; + min-height: 0px; + min-width: 0px; + + /* Overwrite default background and border styles from HTML button element */ + background-color: transparent; + border: none; + + /* Normal Sizing */ + height: 45px; + width: 45px; +} + +/* Default positioning for a Navigation BackButton in the App Body*/ +body > .win-navigation-backbutton { + position: absolute; + top: 50px; + left: 20px; +} + +/* +Command buttons. +*/ +button.win-command { + background: none; + background-clip: border-box; + height: auto; /* height is 88px (label) or 68px (no label) */ + padding: 12px 0; /* bottom dependent on label/img, 2px margin */ + margin: 0; + border: 2px solid; /* reserve focus rect */ + min-width: 40px; + text-align: center; + font-size: 9pt; + line-height: 16px; + font-weight: normal; + + /* Commands are lrtb */ + writing-mode: lr-tb; +} +button.win-command:-ms-lang(ar, dv, fa, he, ku-Arab, pa-Arab, prs, ps, sd-Arab, syr, ug, ur, qps-plocm) { + writing-mode: rl-tb; +} +button.win-command:focus { + outline: none; +} + +/* +Command button icons. +*/ +.win-commandicon { + display: inline-block; + margin: 0 28px; /* padding for command buttons, 2px/side for focus */ + min-width: 0; + min-height: 0; + padding: 0; + + /* Normal sizing */ + width: 40px; + height: 40px; + box-sizing: border-box; + cursor: default; +} +.win-commandimage { + /* Default font for glyphs. */ + font-family: "Segoe UI Command"; + letter-spacing: 0; + + /* Applications provide their own content, like . */ + vertical-align: middle; + font-size: 14pt; + margin: -2px; + line-height: 40px; /* line-height must match the content box height */ + background-position: 0 0; + background-origin: border-box; + display: inline-block; + /* Still 40px */ + width: 40px; + height: 40px; + background-size: 160px 80px; +} + +/* +Offsets for sprite versions. +*/ +button:hover .win-commandimage, button:active .win-commandimage { + background-position: -40px 0; +} +button:hover:active .win-commandimage { + background-position: -80px 0; +} + +button:-ms-keyboard-active .win-commandimage { + background-position: -80px 0; +} +button:disabled .win-commandimage, +button:disabled:active .win-commandimage { + background-position: -120px 0; +} + +/* +Offsets for sprite versions in selected state. +*/ +button[aria-checked=true] .win-commandimage { + background-position: 0 -40px; +} +button[aria-checked=true]:hover .win-commandimage, +button[aria-checked=true]:active .win-commandimage { + background-position: -40px -40px; +} +button[aria-checked=true]:hover:active .win-commandimage { + background-position: -80px -40px; +} +button[aria-checked=true]:-ms-keyboard-active .win-commandimage { + background-position: -80px -40px; +} +button[aria-checked=true]:disabled .win-commandimage, +button[aria-checked=true]:disabled:active .win-commandimage { + background-position: -120px -40px; +} + +/* +Command button "ring". +*/ +.win-commandring, +button:hover .win-commandring, +button:active .win-commandring, +button.win-command:disabled .win-commandring, +button.win-command:disabled:active .win-commandring { + border-width: 2px; + border-style: solid; + border-radius: 50%; + background-clip: border-box; +} +button:hover:active .win-commandring, +button[aria-checked=true] .win-commandring, +button[aria-checked=true]:active .win-commandring, +button[aria-checked=true]:disabled .win-commandring, +button[aria-checked=true]:disabled:active .win-commandring { + background-clip: border-box; +} + +/* +Command button labels. +*/ +button.win-command .win-label { + position: relative; + line-height: 16px; + display: block; + max-width: 88px; /* 100px button, but allow for 2px margins and 4px padding on each side */ + margin-top: 5px; + margin-bottom: -1px; + padding-left: 4px; /* 12px between buttons, 6px per side, minus 2px margins */ + padding-right: 4px; + overflow: hidden; + word-wrap: break-word; + word-break: keep-all; +} + +/* +AppBarCommand separator types. +*/ +hr.win-command { + display: inline-block; + padding: 0; + margin: 14px 29px 34px 30px; + width: 1px; + height: 40px; + border: 0; + vertical-align: top; +} +hr.win-command:-ms-lang(ar, dv, fa, he, ku-Arab, pa-Arab, prs, ps, sd-Arab, syr, ug, ur, qps-plocm) { + margin-left: 29px; + margin-right: 30px; +} + +/* +AppBarCommand content types. +*/ +div.win-command { + + display: inline-block; + min-width: 0; + min-height: 0; + padding: 12px 30px; + border: 2px solid; /* reserve focus rect */ + text-align: center; + font-size: 9pt; + line-height: 16px; + font-weight: normal; + vertical-align: top; + + /* Content Commands are lrtb */ + writing-mode: lr-tb; + +} +div.win-command:-ms-lang(ar, dv, fa, he, ku-Arab, pa-Arab, prs, ps, sd-Arab, syr, ug, ur, qps-plocm) { + writing-mode:rl-tb; +} +div.win-command:focus { + outline: none; +} + +/* +AppBar Edgy Container +*/ +.win-appbar { + z-index: 1002; + border-width: 0; + width: 100%; + height: auto; + left: 0; + position: -ms-device-fixed; +} + +/* +AppBar control. +*/ +.win-commandlayout { + text-align: right; + + /* Hide whitespace between buttons. */ + font-size: 0; + + min-height: 88px; +} +.win-commandlayout .win-selection { + float: left; +} +.win-commandlayout:-ms-lang(ar, dv, fa, he, ku-Arab, pa-Arab, prs, ps, sd-Arab, syr, ug, ur, qps-plocm) { + text-align: left; +} +.win-commandlayout .win-selection:-ms-lang(ar, dv, fa, he, ku-Arab, pa-Arab, prs, ps, sd-Arab, syr, ug, ur, qps-plocm) { + float: right; +} + +/* Narrow buttons and no labels */ +.win-reduced button.win-command .win-label { + display: none; +} +.win-reduced button.win-command .win-commandicon { + margin: 0 8px; /* 2px margin for focus */ +} + +/* Narrow Separators */ +.win-reduced hr.win-command { + margin-bottom: 14px; + margin-top: 14px; +} +.win-reduced hr.win-command { + margin-left: 10px; + margin-right: 9px; +} + .win-reduced hr.win-command:-ms-lang(ar, dv, fa, he, ku-Arab, pa-Arab, prs, ps, sd-Arab, syr, ug, ur, qps-plocm) { + margin-left: 9px; + margin-right: 10px; +} + +/* Narrow Content Commands */ +.win-reduced div.win-command { + padding-left: 10px; + padding-right: 10px; +} + +/* AppBar reduced height and width. */ +.win-commandlayout.win-reduced { + padding-left: 10px; + padding-right: 10px; + width: calc(100% - 20px); + min-height: 68px; +} + +/* +High contrast AppBar needs a border +*/ +@media (-ms-high-contrast) { + /* + AppBar Borders + */ + .win-appbar { + border: solid 2px; + } + .win-appbar.win-top { + border-top: none; + border-left: none; + border-right: none; + } + .win-appbar.win-bottom { + border-bottom: none; + border-left: none; + border-right: none; + } + .win-appbar.win-top button.win-command, + .win-appbar.win-top div.win-command { + padding-bottom: 10px; + } + .win-appbar.win-bottom button.win-command, + .win-appbar.win-bottom div.win-command { + padding-top: 10px; + } + .win-appbar.win-top hr.win-command { + margin-bottom: 32px; + } + .win-appbar.win-bottom hr.win-command { + margin-top: 12px; + } + .win-commandlayout { + min-height: 86px; + } + + /* High Contrast Reduced sizes slightly different */ + .win-commandlayout.win-reduced { + min-height: 66px; + } + .win-appbar.win-reduced.win-top hr.win-command { + margin-bottom: 12px; + } + .win-appbar.win-reduced.win-bottom hr.win-command { + margin-top: 12px; + } +} + +/* +Flyout control. +*/ +.win-flyout { + z-index: 1008; + position: -ms-device-fixed; + padding: 25px 20px 20px 20px; + border-style: solid; + border-width: 2px; + margin: 5px; + min-width: 26px; /* 70px - padding - border = 26px */ + max-width: 466px; /* 510px - padding - border = 466px */ + min-height: 5px; /* 54px - padding - border = 5px */ + max-height: calc(100% - 59px); /* 768px - margin - padding - border = 709px */ + width: auto; + height: auto; + word-wrap: break-word; + overflow: auto; + font-size: 11pt; + font-weight: 400; + line-height: 1.3636; /* 20px when font-size is 11pt */ +} +.win-flyout.win-leftalign { + margin-left: 0; +} +.win-flyout.win-rightalign { + margin-right: 0; +} +.win-flyout.win-scrolls { + overflow: auto; +} + +/* + Hub Control. +*/ +.win-hub { + height: 100%; + width: 100%; + position: relative; +} +.win-hub-progress { + position: absolute; + top: 10px; + width: 100%; + z-index: 1; +} +.win-hub-viewport { + height: 100%; + width: 100%; + /* Allow win-hub-surface margin/padding right to work */ + display: flex; + flex-shrink: 0; + -ms-scroll-snap-type: proximity; +} +.win-hub-horizontal .win-hub-viewport { + overflow-x: auto; + overflow-y: hidden; +} +.win-hub-vertical .win-hub-viewport { + position: relative; + overflow-y: auto; + overflow-x: hidden; + flex-direction: column; +} +.win-hub-surface { + display: flex; + flex-shrink: 0; +} +.win-hub-vertical .win-hub-surface { + width: 100%; + padding: 15px 0; + flex-direction: column; +} +.win-hub-horizontal .win-hub-surface { + height: 100%; + padding: 0 80px; +} + +/* + HubSection Control. +*/ +.win-hub-section { + display: -ms-grid; + -ms-grid-columns: 1fr; + -ms-grid-rows: auto 1fr; + padding: 0 40px; +} +.win-hub-horizontal .win-hub-section { + height: 100%; +} +.win-hub-vertical .win-hub-section { + width: calc(100% - 80px); + padding: 14px 40px; +} +.win-hub-section-header { + -ms-grid-column: 1; + -ms-grid-row: 1; + margin: 4px 0; + font-size: 20pt; + text-align: left; +} +.win-hub-section-header:-ms-lang(ar, dv, fa, he, ku-Arab, pa-Arab, prs, ps, sd-Arab, syr, ug, ur, qps-plocm) { + text-align: right; +} +.win-hub-horizontal .win-hub-section-header { + margin-top: 129px; +} +.win-hub-vertical .win-hub-section-header { + margin-top: 1px; +} +/* Override default button styles */ +/* Button is used so that both piece of UI change color. button:active becomes active if the children are pressed. */ +button.win-hub-section-header-tabstop, +button.win-hub-section-header-tabstop:hover, +button.win-hub-section-header-tabstop:hover:active { + display: -ms-inline-grid; + background-color: transparent; + border: 0; + padding: 0; + min-height: 0; + min-width: 0; + font-size: 20pt; + -ms-grid-columns: minmax(0, max-content) 3px; + -ms-grid-rows: max-content; +} +button.win-hub-section-header-tabstop:focus { + outline: none; +} + +button.win-hub-section-header-tabstop:-ms-keyboard-active { + background-color: transparent; +} +/* Use grid and top level layout for truncation */ +button.win-hub-section-header-tabstop.win-hub-section-header-interactive, +button.win-hub-section-header-tabstop.win-hub-section-header-interactive:hover, +button.win-hub-section-header-tabstop.win-hub-section-header-interactive:hover:active { + -ms-grid-columns: minmax(0, max-content) 7px max-content 3px; +} +.win-hub-section-header-content { + display: inline-block; +} +.win-hub-section-header-chevron { + display: none; +} +.win-hub-section-header-interactive .win-hub-section-header-chevron { + -ms-grid-column: 3; + display: inline-block; +} +.win-hub-section-header-chevron::before { + font-family: 'Segoe UI Symbol'; + content: "\E26B"; +} +.win-hub-section-header-chevron:-ms-lang(ar, dv, fa, he, ku-Arab, pa-Arab, prs, ps, sd-Arab, syr, ug, ur, qps-plocm)::before { + font-family: 'Segoe UI Symbol'; + content: "\E26C"; +} +.win-hub-section-content { + -ms-grid-column: 1; + -ms-grid-row: 2; +} +.win-hub-horizontal .win-hub-section-content { + height: calc(100% - 44px); +} +.win-hub-vertical .win-hub-section-content { + width: 100%; +} + +/* + NavBar +*/ +.win-navbar { + min-height: 60px; +} + +/* + NavBarContainer +*/ +.win-navbarcontainer { + width: 100%; + position: relative; +} +.win-navbarcontainer-pageindicator-box { + position: absolute; + width: 100%; + text-align: center; + pointer-events: none; +} +.win-navbarcontainer-vertical .win-navbarcontainer-pageindicator-box { + display: none; +} +.win-navbarcontainer-pageindicator { + display: inline-block; + width: 40px; + height: 4px; + margin: 5px 2.5px 11px 2.5px; +} +.win-navbarcontainer-horizontal .win-navbarcontainer-viewport { + padding: 15px 0; + overflow-x: auto; + overflow-y: hidden; + -ms-scroll-snap-type: mandatory; + -ms-scroll-snap-points-x: snapInterval(0%, 100%); + -ms-overflow-style: none; + touch-action: pan-x; +} +.win-navbarcontainer-vertical .win-navbarcontainer-viewport { + padding: 10px 0; + overflow-x: hidden; + overflow-y: auto; + max-height: 250px; + -ms-overflow-style: -ms-autohiding-scrollbar; + touch-action: pan-y; +} +.win-navbarcontainer-horizontal .win-navbarcontainer-surface { + display: -ms-grid; + -ms-grid-columns: auto; + -ms-grid-rows: auto; +} +.win-navbarcontainer-navarrow { + position: absolute; + top: 0; + + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + + height: calc(100% - 40px); + width: 17px; + margin: 20px 0; + font-size: 16pt; + overflow: hidden; +} +.win-navbarcontainer-vertical .win-navbarcontainer-navarrow { + display: none; +} +.win-navbarcontainer-navleft { + left: 0; + margin-right: 8px; +} +.win-navbarcontainer-navleft::before { + content: '\E0E2'; +} +.win-navbarcontainer-navright { + right: 0; + margin-left: 8px; +} +.win-navbarcontainer-navright::before { + content: '\E0E3'; +} + +/* + NavBarCommand +*/ +.win-navbarcommand { + display: -ms-grid; + -ms-grid-columns: 1fr auto; + -ms-grid-rows: 1fr; +} +.win-navbarcontainer-horizontal .win-navbarcommand { + margin: 5px; + width: 210px; +} +.win-navbarcontainer-vertical .win-navbarcommand { + margin: 10px 30px; +} +.win-navbarcommand-button { + -ms-grid-column: 1; + -ms-grid-row: 1; + width: 100%; + position: relative; +} +.win-navbarcommand-button-content { + position: relative; + padding: 5px 10px; + display: -ms-grid; + -ms-grid-columns: auto 1fr; + -ms-grid-rows: auto; +} +.win-navbarcommand-button:focus { + z-index: 1; + outline: none; +} +.win-navbarcommand-icon { + -ms-grid-column: 1; + -ms-grid-row: 1; + font-family: "Segoe UI Symbol"; + height: 40px; + width: 40px; + font-size: 20pt; + margin-left: 0; + margin-right: 10px; + display: flex; + align-items: center; + justify-content: center; +} +.win-navbarcommand-icon:-ms-lang(ar, dv, fa, he, ku-Arab, pa-Arab, prs, ps, sd-Arab, syr, ug, ur, qps-plocm) { + margin-right: 0; + margin-left: 10px; +} +.win-navbarcommand-label { + -ms-grid-column: 2; + -ms-grid-row: 1; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + font-size: 11pt; + margin: 10px 0; +} +.win-navbarcommand-splitbutton { + -ms-grid-column: 2; + -ms-grid-row: 1; + display: flex; + justify-content: center; + align-items: center; + height: 100%; + width: 40px; + font-family: 'Segoe UI Symbol'; + font-size: 11pt; + margin-right: 0; + margin-left: 2px; + position: relative; +} +.win-navbarcommand-splitbutton:-ms-lang(ar, dv, fa, he, ku-Arab, pa-Arab, prs, ps, sd-Arab, syr, ug, ur, qps-plocm) { + margin-left: 0; + margin-right: 2px; +} +.win-navbarcommand-splitbutton::before { + content: '\E019'; +} +.win-navbarcommand-splitbutton.win-navbarcommand-splitbutton-opened::before { + content: '\E018'; +} +.win-navbarcommand-splitbutton:focus { + outline: none; +} + +/* +Menu control. +*/ +.win-menu { + padding: 5px 0 5px 0; + line-height: 33px; + text-align: left; /* Set explicitly in case our parent has different alignment, like appbar overflow. */ + min-height: 38px; /* 54px - padding - border = 38px */ + max-height: calc(100% - 26px); /* 768px - margin - padding - border = 742px */ + max-width: none; +} +.win-menu:-ms-lang(ar, dv, fa, he, ku-Arab, pa-Arab, prs, ps, sd-Arab, syr, ug, ur, qps-plocm) { + text-align: right; /* Set explicitly in case our parent has different alignment, like appbar overflow. */ +} + +/* +Menu commands. +*/ +.win-menu button.win-command { + display: block; + border: none; + padding: 10px 20px 12px 20px; + margin-left: 0; + margin-right: 0; + float: none; + text-align: left; + width: 100%; + font-size: 11pt; + font-weight: 600; + line-height: 18px; /* 40px - 10px top padding - 12px bottom padding */ +} +.win-menu button.win-command:-ms-lang(ar, dv, fa, he, ku-Arab, pa-Arab, prs, ps, sd-Arab, syr, ug, ur, qps-plocm) { + text-align: right; +} +.win-menu button.win-command:focus { + outline: none; +} +.win-menu hr.win-command { + display: block; + height: 1px; + width: auto; + border: 0; + + padding: 0; + margin: 9px 20px 10px 20px; +} + +/* +Menu toggle buttons. +*/ +.win-menu-toggle button.win-command::before { + font-family: 'Segoe UI Symbol'; + content: "\E0E7"; + visibility: hidden; + padding-left: 0px; + padding-right: 10px; +} +.win-menu-toggle button[aria-checked=true].win-command::before { + /* Display a checkbox if aria-checked is set */ + visibility: visible; +} +.win-menu-toggle button.win-command:-ms-lang(ar, dv, fa, he, ku-Arab, pa-Arab, prs, ps, sd-Arab, syr, ug, ur, qps-plocm)::before { + padding-right: 0; + padding-left: 10px; + float: right; +} + +/* +Settings Pane +*/ +.win-settingsflyout { + z-index: 1004; /* above appbar and below flyouts */ + border-left: 1px solid; + position: fixed; + top: 0; + right: 0; + height: 100%; + width: 345px; /* 346px - border (1px) */ +} +.win-settingsflyout:-ms-lang(ar, dv, fa, he, ku-Arab, pa-Arab, prs, ps, sd-Arab, syr, ug, ur, qps-plocm) { + border-left: none; + border-right: 1px solid; +} +.win-settingsflyout.win-wide {/* .win-wide is deprecated in Windows 8.1 */ + width: 645px; /* 646px - border (1px) */ +} +.win-settingsflyout .win-header { + padding-left: 40px; + padding-right: 40px; + padding-top: 32px; + height: 48px; +} +/* Settings back button is slightly smaller. */ +.win-settingsflyout .win-backbutton { + position: absolute; + width: 30px; + height: 30px; + font-size: 8pt; + line-height: 26px; + margin-top: 3px; +} +.win-settingsflyout .win-header .win-label { + display: inline-block; + padding-left: 40px; + font-size: 20pt; + line-height: 33px; +} +.win-settingsflyout .win-content { + overflow: auto; + padding-left: 40px; + padding-right: 40px; + padding-top: 33px; + height: calc(100% - 112px); +} + +.win-settingsflyout .win-label { + font-size: 20pt; + font-weight: 200; + line-height: normal; +} +.win-settingsflyout:-ms-lang(ar, dv, fa, he, ku-Arab, pa-Arab, prs, ps, sd-Arab, syr, ug, ur, qps-plocm) { + right: auto; + left: 0; + border-left: none; + border-right: 1px solid; +} +.win-settingsflyout .win-header .win-label:-ms-lang(ar, dv, fa, he, ku-Arab, pa-Arab, prs, ps, sd-Arab, syr, ug, ur, qps-plocm) { + padding-right: 40px; + padding-left: 0; +} + +.win-settingsflyout .win-content .win-settings-section { + padding-top: 0; + padding-bottom: 39px; +} + +.win-settingsflyout .win-content .win-settings-section p { + margin: 0; + padding-top: 0; + padding-bottom: 25px; +} + +.win-settingsflyout .win-content .win-settings-section a { + margin: 0; + padding-top: 0; + padding-bottom: 25px; + display:inline-block; +} + +.win-settingsflyout .win-content .win-settings-section .win-toggleswitch { + margin: 0; + padding-top: 0; + padding-bottom: 20px; +} + +.win-settingsflyout .win-content .win-settings-section label { + display:block; + padding-bottom: 7px; +} + +.win-settingsflyout .win-content .win-settings-section button, +.win-settingsflyout .win-content .win-settings-section select, +.win-settingsflyout .win-content .win-settings-section input[type=button], +.win-settingsflyout .win-content .win-settings-section input[type=text] { + margin-bottom: 25px; + margin-left: 0; + margin-right: 20px; +} + +.win-settingsflyout .win-content .win-settings-section input[type=radio] { + margin-top: 0; + margin-bottom: 0; + padding-bottom: 15px; +} + +.win-settingsflyout .win-content .win-settings-section button:-ms-lang(ar, dv, fa, he, ku-Arab, pa-Arab, prs, ps, sd-Arab, syr, ug, ur, qps-plocm) { + margin-bottom: 25px; + margin-left: 20px; + margin-right: 0; +} +.win-settingsflyout .win-content .win-settings-section select:-ms-lang(ar, dv, fa, he, ku-Arab, pa-Arab, prs, ps, sd-Arab, syr, ug, ur, qps-plocm) { + margin-bottom: 25px; + margin-left: 20px; + margin-right: 0; +} + +.win-settingsflyout .win-content .win-settings-section input[type=text]:-ms-lang(ar, dv, fa, he, ku-Arab, pa-Arab, prs, ps, sd-Arab, syr, ug, ur, qps-plocm) { + margin-bottom: 25px; + margin-left: 20px; + margin-right: 0; +} + +.win-settingsflyout .win-content .win-settings-section input[type=button]:-ms-lang(ar, dv, fa, he, ku-Arab, pa-Arab, prs, ps, sd-Arab, syr, ug, ur, qps-plocm) { + margin-bottom: 25px; + margin-left: 20px; + margin-right: 0; +} + +/* +Grippers in touch selection do not dissapear when focus moves to an element outside of the selection range and they are always drawn on a layer above all HTML elemements. +When an _Overlay derived control such as AppBar/Flyout/Menu/SettingsFlyout is invoked and steals focus, if that _Overlay is laid out on top of the elements in the touch selection, +the grippers can still be seen over the _Overlay and its contents. However, all grippers any where in the document will be hidden whenever the current active element has or inherits +the style "-ms-touch-select: none;" +*/ +.win-overlay { + -ms-touch-select: none; +} + +/* For input elements we filter type using the :not selector to capture any unrecognized user specified types which would just default to the form and function of a textbox*/ +.win-overlay input:not([type="file"]), +.win-overlay input:not([type="radio"]), +.win-overlay input:not([type="checkbox"]), +.win-overlay input:not([type="button"]), +.win-overlay input:not([type="range"]), +.win-overlay input:not([type="image"]), +.win-overlay input:not([type="reset"]), +.win-overlay input:not([type="hidden"]), +.win-overlay input:not([type="submit"]), +.win-overlay textarea, +.win-overlay [contenteditable=true]{ + -ms-touch-select: grippers; +} + +/* Singleton element maintained by _Overlay, used for getting accurate floating point measurements of the total size of the visual viewport. + Floating point is necesary in high DPI resolutions. */ +.win-visualviewport-space { + position:-ms-device-fixed; + height: 100vh; + width: 100vw; + visibility:hidden; +} + +/* +Rating control. +*/ +.win-rating { + display: inline-flex; + height: auto; + width: auto; + white-space: normal; + align-items: stretch; + justify-content: center; +} + +.win-rating .win-star { + flex: auto; + height:28px; + width: 28px; + padding: 0 6px; + font-family: "Segoe UI Symbol"; + font-size: 28px; + overflow: hidden; + text-indent: 0; + line-height: 1; + cursor: default; + position: relative; + letter-spacing: 0; /* Use letter-spacing: 0 to make average star look like one glyph. */ + touch-action: none; +} +.win-rating.win-small .win-star { + width: 14px; + height: 14px; + font-size: 14px; + padding: 0 3px; +} +.win-rating .win-star:before { + content: "\E082"; +} +.win-rating .win-star.win-disabled { + cursor: default; + touch-action: auto; +} + +/* +DatePicker control. +*/ +.win-datepicker { + display: inline-flex; + height: auto; + width: auto; +} +.win-datepicker .win-datepicker-month { + margin-right: 20px; +} +.win-datepicker .win-datepicker-date.win-order0, +.win-datepicker .win-datepicker-date.win-order1 { + margin-right: 20px; +} +.win-datepicker .win-datepicker-year.win-order0 { + margin-right: 20px; +} +.win-datepicker .win-datepicker-month:-ms-lang(ar, dv, fa, he, ku-Arab, pa-Arab, prs, ps, sd-Arab, syr, ug, ur, qps-plocm) { + margin-right: 0; + margin-left: 20px; +} +.win-datepicker .win-datepicker-date.win-order0:-ms-lang(ar, dv, fa, he, ku-Arab, pa-Arab, prs, ps, sd-Arab, syr, ug, ur, qps-plocm), +.win-datepicker .win-datepicker-date.win-order1:-ms-lang(ar, dv, fa, he, ku-Arab, pa-Arab, prs, ps, sd-Arab, syr, ug, ur, qps-plocm) { + margin-right: 0; + margin-left: 20px; +} +.win-datepicker .win-datepicker-year.win-order0:-ms-lang(ar, dv, fa, he, ku-Arab, pa-Arab, prs, ps, sd-Arab, syr, ug, ur, qps-plocm){ + margin-right: 0; + margin-left: 20px; +} + +/* +TimePicker control. +*/ +.win-timepicker { + display: inline-flex; + height: auto; + width: auto; +} +.win-timepicker .win-timepicker-hour { + margin-right: 20px; +} +.win-timepicker .win-timepicker-period.win-order0 { + margin-right: 20px; +} +.win-timepicker .win-timepicker-minute.win-order1 { + margin-right: 20px; +} +.win-timepicker .win-timepicker-period.win-order0:-ms-lang(ar, dv, fa, he, ku-Arab, pa-Arab, prs, ps, sd-Arab, syr, ug, ur, qps-plocm) { + margin-right: 0; + margin-left: 20px; +} +.win-timepicker .win-timepicker-minute.win-order1:-ms-lang(ar, dv, fa, he, ku-Arab, pa-Arab, prs, ps, sd-Arab, syr, ug, ur, qps-plocm), +.win-timepicker .win-timepicker-minute.win-order0:-ms-lang(ar, dv, fa, he, ku-Arab, pa-Arab, prs, ps, sd-Arab, syr, ug, ur, qps-plocm) { + margin-left: 20px; + margin-right: 0; +} +.win-timepicker .win-timepicker-hour:-ms-lang(ar, dv, fa, he, ku-Arab, pa-Arab, prs, ps, sd-Arab, syr, ug, ur, qps-plocm) { + margin-right: 0; + margin-left: 20px; +} + +/* +Toggle control. +*/ +.win-toggleswitch { + padding: 0; + background-color: transparent; + vertical-align: top; + margin: 0; + display: block; +} +.win-toggleswitch .win-switch { + display: inline-block; + height: 19px; + width: 50px; + padding: 5px; + margin: 0; + background-color: transparent; + -ms-grid-column: 2; +} +.win-toggleswitch .win-switch::-ms-tooltip { + display: none; +} +.win-toggleswitch .win-switch::-ms-ticks-before, +.win-toggleswitch .win-switch::-ms-ticks-after { + display: none; +} +.win-toggleswitch .win-switch::-ms-track { + height: 15px; /* 19px - 2px borders */ + width: 46px; + padding: 0; + margin: 0; + border-width: 2px; + border-style: solid; +} +.win-toggleswitch .win-switch::-ms-fill-lower, +.win-toggleswitch .win-switch::-ms-fill-upper { + height: 13px; /* 19px - 2px borders - 1px margins */ + padding: 0; +} +.win-toggleswitch .win-switch::-ms-fill-lower { + margin-left: 1px; +} +.win-toggleswitch .win-switch:-ms-lang(ar, dv, fa, he, ku-Arab, pa-Arab, prs, ps, sd-Arab, syr, ug, ur, qps-plocm)::-ms-fill-lower { + margin-left: 0; + margin-right: 1px; +} +.win-toggleswitch .win-switch::-ms-fill-upper { + margin-right: 1px; +} +.win-toggleswitch .win-switch:-ms-lang(ar, dv, fa, he, ku-Arab, pa-Arab, prs, ps, sd-Arab, syr, ug, ur, qps-plocm)::-ms-fill-upper { + margin-right: 0; + margin-left: 1px; +} +.win-toggleswitch .win-switch::-ms-thumb { + height: 19px; + width: 12px; +} +.win-toggleswitch .win-title { + display: block; + vertical-align: top; + max-width:470px; + font-size: 11pt; + font-weight: 300; + line-height: 1.3636; /* 20px when font-size is 11pt */ +} +.win-toggleswitch .win-label { + display: inline-block; + vertical-align: top; + padding: 5px 20px 5px 0px; + min-width: 65px; + direction: inherit; + -ms-grid-column: 1; + -ms-grid-row: 1; + font-size: 11pt; + font-weight: 600; + line-height: 1.3636; /* 20px when font-size is 11pt */ +} +.win-toggleswitch .win-label:-ms-lang(ar, dv, fa, he, ku-Arab, pa-Arab, prs, ps, sd-Arab, syr, ug, ur, qps-plocm) { + padding: 5px 0px 5px 20px; +} +.win-toggleswitch .win-label.win-hidden { + visibility: hidden; +} +.win-focus-hide { + outline: none; +} + +/* +Tooltip control. +*/ +.win-tooltip { + display: block; + position: fixed; + top: 30px; + left: 30px; + max-width: 380px; + box-sizing: border-box; + margin: 0; + padding: 6px 10px 7px 10px; + border-style: solid; + border-width: 2px; + z-index: 9999; + word-wrap: break-word; + animation-fill-mode: both; + font-size: 9pt; + font-weight: 400; + line-height: 1.6667; /* 20px when font-size is 9pt */ +} +.win-tooltip-phantom { + display: block; + position: fixed; + top: 30px; + left: 30px; + background-color: transparent; + border-width: 0; + margin: 0; + padding: 0; +} + +.win-viewbox { + width: 100%; + height: 100%; + position: relative; +} + +.win-semanticzoom { + height: 400px; + touch-action: pan-x pan-y double-tap-zoom; +} +.win-semanticzoom * { + touch-action: inherit; +} + +.win-semanticzoom-button { + z-index: 100; + position: absolute; + min-width: 25px; + min-height: 25px; + width: 25px; + height: 25px; + padding: 0px; + bottom: 21px; + touch-action: none; +} + +.win-semanticzoom-button::before { + font-family: "Segoe UI Symbol"; + font-weight: normal; + font-size: 11pt; + content: "\E0B8"; /* minus sign */ +} + +.win-semanticzoom-button-location{ + left: auto; + right: 4px; +} + +.win-semanticzoom-button-location:-ms-lang(ar, dv, fa, he, ku-Arab, pa-Arab, prs, ps, sd-Arab, syr, ug, ur, qps-plocm) { + left: 4px; + right: auto; +} + +/* +ScrollView control. +*/ +.win-scrollview { + overflow-x: auto; + overflow-y: hidden; + height: 400px; + width: 100%; +} + +/*Flyout control animations*/ +@keyframes WinJS-showFlyoutTop { from { transform: translateY(50px); } to { transform: none; } } +@keyframes WinJS-showFlyoutBottom { from { transform: translateY(-50px); } to { transform: none; } } +@keyframes WinJS-showFlyoutLeft { from { transform: translateX(50px); } to { transform: none; } } +@keyframes WinJS-showFlyoutRight { from { transform: translateX(-50px); } to { transform: none; } } + +/* +SearchBox control. +*/ + +.win-searchbox { + white-space: normal; + position: relative; + width: 265px; + height: 28px; + border-style: solid; + border-width: 2px; +} + +.win-searchbox-flyout { + position: absolute; + min-width: 265px; + border-style: solid; + border-width: 2px 2px 2px 2px; + z-index: 100; + padding: 5px 0 10px 0; + top: 30px; + left: -2px; + right: -2px; + max-height: 272px; + overflow: auto; + -ms-scroll-chaining:none; + touch-action:none; +} + +.win-searchbox input { + height: 100%; + border-style: none; + margin: 0px; + padding-right: 28px; + width: calc(100% - 28px); + font-size: 11pt; + -ms-ime-align: after; + line-height: 20px; +} + +.win-searchbox input:-ms-lang(ar, dv, fa, he, ku-Arab, pa-Arab, prs, ps, sd-Arab, syr, ug, ur, qps-plocm) { + padding-right: 0; + padding-left: 28px; +} + +/* +Hide clear button in search box control. +*/ + +.win-searchbox input[type=search]::-ms-clear { + display: none; +} + +.win-searchbox-button { + position: absolute; + right: 0; + top: 0; + width: 28px; + font-family: "Segoe UI Symbol"; + font-size: 15pt; + border-style: none; + height: 100%; + text-align:center; +} + +.win-searchbox-button:-ms-lang(ar, dv, fa, he, ku-Arab, pa-Arab, prs, ps, sd-Arab, syr, ug, ur, qps-plocm) { + right: auto; + left: 0; +} + +.win-searchbox-button.win-searchbox-button:before { + content: "\E094"; +} + +.win-searchbox-suggestion-result { + padding: 0 18px; + display: flex; + height: 60px; + font-family: "Segoe UI"; + font-size: 11pt; + outline: none; +} + +.win-searchbox-suggestion-result div { + line-height: 20px; + text-overflow: ellipsis; + white-space: nowrap; + overflow: hidden; +} + +.win-searchbox-suggestion-result-text { + padding-top: 9px; + padding-bottom: 11px; + height: 60px; + width: 179px; + text-overflow: ellipsis; + white-space: nowrap; + overflow: hidden; + line-height: 20px; +} + +.win-searchbox-suggestion-result-detailed-text { + display: inline-block; + overflow: hidden; + -ms-text-overflow: ellipsis; + line-height: 22px; /* Some characters get clipped if line height is < 22px. Work around by setting -2 margin. */ + margin-top: -1px; + width: 100%; +} + +.win-searchbox-suggestion-result img { + width: 40px; + height: 40px; + margin-left: 0; + padding-right: 10px; + padding-top: 10px; + padding-bottom: 10px; +} + +.win-searchbox-suggestion-result img:-ms-lang(ar, dv, fa, he, ku-Arab, pa-Arab, prs, ps, sd-Arab, syr, ug, ur, qps-plocm) { + margin-right: 0; + margin-left: auto; + padding-left: 10px; + padding-right: 0; +} + +.win-searchbox-suggestion-query { + padding: 9px 18px 11px 18px; + font-family: "Segoe UI"; + font-weight: normal; + outline: none; + text-overflow: ellipsis; + white-space: nowrap; + overflow: hidden; + font-size: 11pt; + line-height: 20px; +} + +.win-searchbox-suggestion-separator { + display: -ms-grid; + -ms-grid-columns: auto 1fr; + padding: 0 18px; + height: 40px; + font-family: "Segoe UI"; + font-size: 11pt; +} + +.win-searchbox-suggestion-separator hr { + -ms-grid-column: 2; + -ms-grid-row: 1; + margin-top: 18px; + border-style: solid; + border-width: 1px 0px 0px 0px; +} + +.win-searchbox-suggestion-separator hr:-ms-lang(ar, dv, fa, he, ku-Arab, pa-Arab, prs, ps, sd-Arab, syr, ug, ur, qps-plocm) { + margin-right: 10px; + margin-left: auto; +} + +.win-searchbox-suggestion-separator div { + -ms-grid-column: 1; + -ms-grid-row: 1; + text-overflow: ellipsis; + white-space: nowrap; + overflow: hidden; + padding-top: 9px; + padding-bottom: 11px; + line-height: 20px; + margin-right: 10px; +} + +.win-searchbox-suggestion-separator div:-ms-lang(ar, dv, fa, he, ku-Arab, pa-Arab, prs, ps, sd-Arab, syr, ug, ur, qps-plocm) { + margin-left: 10px; + margin-right: auto; +} + +/* +Search box control animations +*/ +@keyframes WinJS-flyoutBelowSearchBox-showPopup { from { transform: translateY(0px); } to { transform: none; } } +@keyframes WinJS-flyoutAboveSearchBox-showPopup { from { transform: translateY(0px); } to { transform: none; } } + +/* +ListView control. +*/ +.win-listview { + overflow: hidden; + height: 400px; +} +.win-listview .win-surface { + overflow: visible; +} +.win-listview > .win-viewport.win-horizontal .win-surface { + height: 100%; +} +.win-listview > .win-viewport.win-vertical .win-surface { + width: 100%; +} +.win-listview > .win-viewport { + position: relative; + width: 100%; + height: 100%; + z-index: 0; + -ms-overflow-style: -ms-autohiding-scrollbar; +} +.win-listview > .win-viewport.win-horizontal { + overflow-x: auto; + overflow-y: hidden; +} +.win-listview > .win-viewport.win-vertical { + overflow-x: hidden; + overflow-y: auto; +} +.win-listview .win-itemscontainer { + overflow:hidden; +} +.win-listview .win-itemscontainer-padder { + width: 0; + height: 0; + margin: 0; + padding: 0; + border: 0; + overflow: hidden; +} +.win-listview > .win-horizontal .win-container { + margin: 10px 5px 0px 5px; +} +.win-listview > .win-vertical .win-container { + margin: 10px 24px 0px 7px; +} +.win-listview.win-rtl > .win-vertical .win-container { + margin: 10px 7px 0px 24px; +} +.win-listview .win-container, .win-listview .win-itembox, +.win-itemcontainer.win-container, .win-itemcontainer .win-itembox { + cursor: default; + z-index: 0; +} +.win-listview .win-container { + touch-action:pan-x pan-y pinch-zoom; +} +.win-listview.win-swipeable .win-horizontal .win-container, +.win-listview.win-swipeable .win-horizontal .win-itembox.win-nonswipeable { + touch-action:pan-x pinch-zoom; +} +.win-listview.win-swipeable .win-vertical .win-container, +.win-listview.win-swipeable .win-vertical .win-itembox.win-nonswipeable { + touch-action:pan-y pinch-zoom; +} +.win-listview.win-swipeable .win-horizontal .win-itembox { + touch-action:pan-x pinch-zoom double-tap-zoom cross-slide-y; +} +.win-listview.win-swipeable .win-vertical .win-itembox { + touch-action:pan-y pinch-zoom double-tap-zoom cross-slide-x; +} +.win-semanticzoom .win-listview > .win-viewport * { + touch-action: auto; +} +.win-semanticzoom .win-listview > .win-viewport.win-zooming-x { + overflow-x: visible; +} +.win-semanticzoom .win-listview > .win-viewport.win-zooming-y { + overflow-y: visible; +} +.win-listview .win-itembox, +.win-itemcontainer .win-itembox { + width:100%; + height:100%; +} +.win-listview .win-item, +.win-itemcontainer .win-item { + /* Used to place above .win-selectionbackground in ListView */ + z-index: 1; +} +.win-listview .win-item, +.win-itemcontainer .win-item { + overflow: hidden; + position: relative; +} +.win-listview > .win-vertical .win-item +{ + /* Allow flex box to fill win-item in list layout */ + width: 100%; +} +.win-listview .win-item:focus, +.win-itemcontainer .win-item:focus { + outline-style: none; +} +.win-listview .win-focusedoutline, +.win-itemcontainer .win-focusedoutline { + width: calc(100% + 4px); + height: calc(100% + 4px); + left: -2px; + top: -2px; + position: absolute; + z-index: 5; + pointer-events: none; +} +.win-container.win-selected .win-selectionborder { + border-width: 4px; + border-style: solid; +} +.win-container.win-selected:hover .win-selectionborder { + border-width: 4px; + border-style: solid; +} +.win-listview .win-groupheader { + padding: 10px 10px 10px 2px; + overflow: hidden; + outline-width: 0.01px; + outline-style: none; + /* Causes the focus outline to tightly wrap the header content */ + float: left; + font-size: 20pt; + font-weight: 200; + line-height: 1.2; /* 32px when font-size is 20pt */ +} +.win-listview .win-groupheadercontainer { + z-index: 1; + touch-action:pan-x pan-y pinch-zoom; + overflow: hidden; +} +.win-listview .win-groupheader.win-focused { + outline-style: dotted; +} +.win-listview.win-rtl .win-groupheader { + padding-left: 10px; + padding-right: 2px; + float: right; +} +.win-listview.win-groups .win-horizontal .win-groupleader { + margin-left: 70px; +} +.win-listview.win-groups.win-rtl .win-horizontal .win-groupleader { + margin-left: 0; + margin-right: 70px; +} +.win-listview.win-groups .win-vertical .win-listlayout .win-groupleader, +.win-listview.win-groups .win-vertical .win-gridlayout .win-groupleader { + margin-top: 70px; +} +.win-listview.win-groups > .win-vertical .win-surface.win-listlayout, +.win-listview.win-groups > .win-vertical .win-surface.win-gridlayout { + margin-top: -65px; +} +.win-listview.win-groups > .win-horizontal .win-surface { + margin-left: -70px; +} +.win-listview.win-groups.win-rtl > .win-horizontal .win-surface { + margin-left: 0; + margin-right: -70px; +} +.win-surface ._win-proxy { + /* Position relative + overflow hidden are applied to this element to stop margin collapsing of a margin-top on a header + the margin-top on the surface in list layout */ + position: relative; + overflow: hidden; + width: 0; + height: 0; + touch-action: none; +} +.win-selectionborder { + position: absolute; + opacity: inherit; + z-index: 2; + pointer-events: none; +} +.win-container.win-selected .win-selectionborder { + top: 0; + left: 0; + right: 0; + bottom: 0; +} +.win-selectionbackground { + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + /* Used to place behind .win-item in the ListView/ItemContainer */ + z-index: 0; +} +.win-selectioncheckmarkbackground { + position: absolute; + top: 0; + right: 0; + width: 0; + height: 0; + margin: 0; + padding: 0; + border-width: 20px; + border-style: solid; + z-index: 3; +} +.win-listview.win-rtl .win-selectioncheckmarkbackground, +.win-itemcontainer.win-rtl .win-selectioncheckmarkbackground { + left: 0; + right: auto; +} +.win-selectioncheckmark { + position: absolute; + margin: 0; + padding: 2px; + right: 0; + top: 0; + font-family: Segoe UI Symbol; + font-size: 11pt; + z-index: 4; +} +.win-listview.win-rtl .win-selectioncheckmark, +.win-itemcontainer.win-rtl .win-selectioncheckmark { + left: 0; + right: auto; +} +.win-selectionhint { + position: absolute; + margin: 0; + padding: 2px; + right: 0; + font-family: Segoe UI Symbol; + font-size: 11pt; + opacity: 0.5; +} +.win-selectionhint.win-revealed { + opacity: 1; +} +.win-listview.win-rtl .win-selectionhint, +.win-itemcontainer.win-rtl .win-selectionhint { + left: 0; + right: auto; +} +.win-listview .win-progress { + left: 50%; + top: 50%; + width: 60px; + height: 60px; + margin-left: -30px; + margin-top: -30px; + z-index: 1; + position: absolute; +} +.win-listview .win-progress::-ms-fill { + animation-name: -ms-ring; +} + +.win-listview .win-itemsblock { + overflow: hidden; +} +.win-listview .win-horizontal .win-itemscontainer, .win-listview .win-horizontal .win-itemsblock:not(.win-clip) { + margin-top: -200px; + margin-bottom: -200px; + padding-top: 200px; + padding-bottom: 200px; +} +.win-listview .win-vertical .win-itemscontainer, .win-listview .win-vertical .win-itemsblock:not(.win-clip) { + margin-left: -200px; + margin-right: -200px; + padding-left: 200px; + padding-right: 200px; +} +/* +ListLayout +*/ +.win-listview.win-groups .win-vertical .win-listlayout.win-headerpositionleft .win-itemscontainer, +.win-listview.win-groups .win-vertical .win-listlayout.win-headerpositionleft .win-groupheadercontainer, +.win-listview.win-groups .win-horizontal .win-listlayout .win-itemscontainer, +.win-listview.win-groups .win-horizontal .win-listlayout .win-groupheadercontainer { + display: none; +} +.win-listview.win-groups .win-vertical .win-listlayout.win-headerpositionleft .win-itemscontainer.win-laidout, +.win-listview.win-groups .win-vertical .win-listlayout.win-headerpositionleft .win-groupheadercontainer.win-laidout, +.win-listview.win-groups .win-horizontal .win-listlayout .win-groupheadercontainer.win-laidout { + display: block; +} +.win-listview .win-listlayout .win-itemscontainer { + overflow: visible; +} +.win-listview .win-listlayout .win-itemsblock { + /* Avoid clipping of the focus and hover visuals */ + padding-bottom: 4px; + margin-bottom: -4px; +} +/* +ListLayout - Vertical +*/ +/* Vertical grouped list layout needs to remove float from group headers when headers are above so that static positioning gets applied properly */ +.win-listview > .win-vertical .win-listlayout.win-headerpositiontop .win-groupheader { + float: none; +} +.win-listview > .win-vertical .win-surface.win-listlayout { + margin-bottom: 5px; +} +/* Headers on the left */ +.win-listview.win-groups > .win-vertical .win-surface.win-listlayout.win-headerpositionleft { + margin-left: 70px; +} +.win-listview.win-groups.win-rtl > .win-vertical .win-surface.win-listlayout.win-headerpositionleft { + margin-left: 0px; + margin-right: 70px; +} +.win-listview .win-vertical .win-listlayout.win-headerpositionleft.win-surface { + display: -ms-inline-grid; + -ms-grid-columns: auto 1fr; + -ms-grid-rows: auto; +} +.win-listview .win-vertical .win-listlayout.win-headerpositionleft .win-groupheadercontainer { + -ms-grid-column: 1; +} +.win-listview .win-vertical .win-listlayout.win-headerpositionleft .win-itemscontainer { + -ms-grid-column: 2; +} +/* +ListLayout - Horizontal +*/ +.win-listview > .win-horizontal .win-surface.win-listlayout { + display: -ms-inline-grid; + -ms-grid-columns: auto; + -ms-grid-rows: auto; + /* Use vertical-align:top when using display:-ms-inline-grid to avoid extra spacing around itself */ + vertical-align:top; +} +.win-listview > .win-horizontal .win-surface.win-listlayout { + margin-left: 70px; +} +.win-listview.win-rtl > .win-horizontal .win-surface.win-listlayout { + margin-left: 0px; + margin-right: 70px; +} +.win-listview .win-horizontal .win-listlayout .win-itemsblock { + height: 100%; +} +.win-listview .win-horizontal .win-listlayout .win-itemscontainer { + /* itemscontainers have a -200px margin by default, but here we want to increase it by 24px to leave room for the scrollbar */ + margin-bottom: -176px; +} +.win-listview .win-horizontal .win-listlayout .win-container { + height: calc(100% - 10px); +} +/* Headers above */ +.win-listview > .win-horizontal .win-surface.win-listlayout.win-headerpositiontop { + -ms-grid-rows: auto 1fr; +} +.win-listview .win-horizontal .win-listlayout.win-headerpositiontop .win-groupheadercontainer { + -ms-grid-row: 1; +} +.win-listview .win-horizontal .win-listlayout.win-headerpositiontop .win-itemscontainer { + -ms-grid-row: 2; +} +/* +GridLayout +*/ +.win-listview .win-gridlayout.win-surface { + /* Use a grid even in non-grouped layouts so that there is consistent margin + collapsing behavior between the surface and itemsContainer in all grid layouts */ + display: -ms-inline-grid; + /* Use vertical-align:top when using display:-ms-inline-grid to avoid extra spacing around itself */ + vertical-align:top; +} +.win-listview .win-vertical .win-gridlayout.win-surface { + margin-left: 20px; +} +.win-listview.win-rtl .win-vertical .win-gridlayout.win-surface { + margin-left: 0px; + margin-right: 20px; +} +.win-horizontal .win-gridlayout .win-container { + margin: 5px; +} +.win-vertical .win-gridlayout .win-container { + margin: 5px 24px 5px 7px; +} +.win-rtl > .win-vertical .win-gridlayout .win-container { + margin: 5px 7px 5px 24px; +} +/* Headers */ +.win-listview .win-vertical .win-gridlayout.win-headerpositionleft .win-groupheadercontainer, +.win-listview .win-vertical .win-gridlayout.win-headerpositiontop .win-groupheadercontainer { + -ms-grid-column: 1; +} +.win-listview.win-groups .win-gridlayout .win-itemscontainer, +.win-listview.win-groups .win-gridlayout .win-groupheadercontainer { + display: none; +} +.win-listview.win-groups .win-gridlayout .win-groupheadercontainer.win-laidout { + display: block; +} +/* Horizontal - Headers above */ +.win-listview .win-horizontal .win-gridlayout.win-headerpositiontop.win-surface { + -ms-grid-columns: auto; + -ms-grid-rows: auto 1fr; +} +.win-listview .win-horizontal .win-gridlayout.win-headerpositiontop .win-groupheadercontainer { + -ms-grid-row: 1; +} +.win-listview .win-horizontal .win-gridlayout.win-headerpositiontop .win-itemscontainer { + -ms-grid-row: 2; +} +/* Horizontal - Headers on the left */ +.win-listview .win-horizontal .win-gridlayout.win-headerpositionleft.win-surface { + -ms-grid-columns: auto; + -ms-grid-rows: auto; +} +.win-listview .win-horizontal .win-gridlayout.win-headerpositionleft .win-groupheadercontainer { + -ms-grid-row: 1; +} +.win-listview .win-horizontal .win-gridlayout.win-headerpositionleft .win-itemscontainer { + -ms-grid-row: 1; +} +/* Vertical - headers above */ +.win-listview .win-vertical .win-gridlayout.win-headerpositiontop.win-surface { + -ms-grid-columns: auto; + -ms-grid-rows: auto; +} +.win-listview .win-vertical .win-gridlayout.win-headerpositiontop .win-itemscontainer { + -ms-grid-column: 1; +} +/* Vertical - headers on the left */ +.win-listview .win-vertical .win-gridlayout.win-headerpositionleft.win-surface { + -ms-grid-columns: auto 1fr; + -ms-grid-rows: auto; +} +.win-listview .win-vertical .win-gridlayout.win-headerpositionleft .win-itemscontainer { + -ms-grid-column: 2; +} +/* Uniform and cell spanning groups */ +.win-listview .win-gridlayout.win-structuralnodes .win-uniformgridlayout.win-itemscontainer.win-laidout { + display: flex; + flex-flow: row; +} +.win-listview .win-horizontal .win-listlayout .win-itemscontainer, +.win-listview.win-groups .win-horizontal .win-listlayout .win-itemscontainer.win-laidout, +.win-listview .win-horizontal .win-listlayout .win-itemsblock, +.win-listview .win-horizontal .win-gridlayout .win-uniformgridlayout.win-itemscontainer.win-laidout, +.win-listview .win-horizontal .win-gridlayout .win-uniformgridlayout .win-itemsblock { + display: flex; + flex-flow: column wrap; + align-content: flex-start; +} +.win-listview .win-horizontal .win-itemscontainer-padder { + height: 100%; +} +.win-listview .win-horizontal .win-gridlayout .win-uniformgridlayout .win-itemsblock { + height: 100%; +} +.win-listview .win-horizontal .win-gridlayout .win-cellspanninggridlayout.win-itemscontainer.win-laidout { + display: -ms-grid; +} +/* Vertical - uniform group */ +.win-listview .win-vertical .win-gridlayout.win-structuralnodes .win-uniformgridlayout.win-itemscontainer.win-laidout { + display: flex; + flex-flow: column; +} +.win-listview .win-vertical .win-gridlayout .win-uniformgridlayout.win-itemscontainer.win-laidout, +.win-listview .win-vertical .win-gridlayout .win-uniformgridlayout .win-itemsblock { + display: flex; + flex-flow: row wrap; + align-content: flex-start; +} +.win-listview .win-vertical .win-gridlayout .win-uniformgridlayout .win-itemsblock { + width: 100%; +} +/* Cell spanning */ +.win-listview .win-cellspanninggridlayout .win-container.win-laidout { + display: block; +} +.win-listview .win-cellspanninggridlayout .win-container { + display: none; +} +.win-listview .win-itembox { + position: relative; + transition: transform cubic-bezier(0.1, 0.9, 0.2, 1) 220ms; +} +.win-listview.win-dragover .win-itembox { + transform: scale(0.86); +} +.win-listview .win-itembox.win-dragsource, +.win-itemcontainer .win-itembox.win-dragsource { + opacity: 0.5; + transition: opacity cubic-bezier(0.1, 0.9, 0.2, 1) 167ms, transform cubic-bezier(0.1, 0.9, 0.2, 1) 220ms; +} +.win-listview.win-dragover .win-itembox.win-dragsource { + opacity: 0; + transition: none; +} +.win-listview.win-dragover .win-container:hover { + outline: none; +} +.win-listview .win-container.win-swipe, .win-listview .win-container.win-swipe .win-itembox, +.win-itemcontainer.win-container.win-swipe, .win-itemcontainer.win-container.win-swipe .win-itembox { + position: relative; + z-index: 1; +} +/* +FlipView control. +*/ +.win-flipview { + overflow: hidden; + height: 400px; +} + +.win-flipview .win-surface { + -ms-scroll-chaining: none; +} + +.win-flipview .win-navleft { + left: 0%; + top: 50%; + margin-top: -19px; +} +.win-flipview .win-navright { + left: 100%; + top: 50%; + margin-left: -69px; + margin-top: -19px; +} +.win-flipview .win-navtop { + left: 50%; + top: 0%; + margin-left: -35px; +} +.win-flipview .win-navbottom { + left: 50%; + top: 100%; + margin-left: -35px; + margin-top: -39px; +} +.win-flipview .win-navbutton { + border: none; + width: 69px; + height: 39px; + z-index: 1; + position: absolute; + font-family: "Segoe UI Symbol"; + font-size: 16pt; + padding: 0; + min-width: 0; +} +.win-flipview .win-item, .win-flipview .win-item > .win-template { + display: flex; + height: 100%; + width: 100%; + align-items: center; + justify-content: center; +} + +/* +ItemContainer control. +*/ +.win-itemcontainer .win-itembox, +.win-itemcontainer.win-container { + position:relative; +} +.win-itemcontainer { + touch-action:pan-x pan-y pinch-zoom; +} +.win-itemcontainer.win-swipeable.win-horizontal .win-itembox { + touch-action:pan-y pinch-zoom cross-slide-x; +} +.win-itemcontainer.win-swipeable.win-vertical .win-itembox { + touch-action:pan-x pinch-zoom cross-slide-y; +} + +/* +Typographic color definitions. +*/ + +body { + color: rgb(255, 255, 255); + background-color: rgb(29, 29, 29); +} + +.win-type-interactive:hover { + color: rgba(255, 255, 255, 0.8); +} +.win-type-interactive:hover:active { + color: rgba(255, 255, 255, 0.4); +} + +.win-type-interactive:-ms-keyboard-active { + color: rgba(255, 255, 255, 0.4); +} +.win-ui-light .win-type-interactive:hover { + color: rgba(0, 0, 0, 0.8); +} +.win-ui-light .win-type-interactive:hover:active { + color: rgba(0, 0, 0, 0.4); +} +.win-ui-light .win-type-interactive:-ms-keyboard-active { + color: rgba(0, 0, 0, 0.4); +} + +/* +These classes reverse the colors on the subtree to which they are applied. +*/ + +.win-ui-dark { + color: rgb(255, 255, 255); + background-color: rgb(29, 29, 29); +} +.win-ui-light { + color: rgb(0, 0, 0); + background-color: rgb(255, 255, 255); +} + +/* +Text selection color +*/ +::selection { + background-color: rgb(87, 41, 193); + color: rgb(255, 255, 255); +} + +/* +Text input, checkbox, radio, and select control colors. +*/ +input[type=text], input[type=password], +input[type=email], input[type=number], +input[type=tel], input[type=url], +input[type=search], textarea, .win-textarea, +select, input::-ms-check { + background-clip: border-box; + background-color: rgba(255, 255, 255, 0.8); + border-color: transparent; + color: rgb(0, 0, 0); +} +input[type=text]:hover, input[type=password]:hover, +input[type=email]:hover, input[type=number]:hover, +input[type=tel]:hover, input[type=url]:hover, +input[type=search]:hover, textarea:hover, +.win-textarea:hover, select:hover, input:hover::-ms-check { + background-clip: border-box; + background-color: rgba(255, 255, 255, 0.87); + border-color: transparent; + color: rgb(0, 0, 0); +} +input:hover:active::-ms-check { /* only checkbox and radio have press state */ + background-color: rgb(255, 255, 255); + color: rgb(0, 0, 0); +} +input:-ms-keyboard-active::-ms-check { /* only checkbox and radio have press state */ + background-color: rgb(255, 255, 255); +} +input[type=text]:focus, input[type=text]:active, +input[type=password]:focus, input[type=password]:active, +input[type=email]:focus, input[type=email]:active, +input[type=number]:focus, input[type=number]:active, +input[type=tel]:focus, input[type=tel]:active, +input[type=url]:focus, input[type=url]:active, +input[type=search]:focus, input[type=search]:active, +textarea:focus, textarea:active, +.win-textarea:focus, .win-textarea:active, +select:focus, select:active { + background-clip: border-box; + background-color: rgb(255, 255, 255); + border-color: transparent; + color: rgb(0, 0, 0); +} +input[type=text]:disabled, input[type=password]:disabled, +input[type=email]:disabled, input[type=number]:disabled, +input[type=tel]:disabled, input[type=url]:disabled, +input[type=search]:disabled, input[type=file]::-ms-value, +textarea:disabled, .win-textarea:disabled, select:disabled { + background-clip: border-box; + background-color: transparent; + border-color: rgba(255, 255, 255, 0.4); + color: rgba(255, 255, 255, 0.4); +} +input:disabled::-ms-check, input:disabled:active::-ms-check { /* checkbox and radio have filled disabled state in dark */ + background-clip: border-box; + background-color: rgba(255, 255, 255, 0.4); + border-color: transparent; + color: rgba(0, 0, 0, 0.4); +} +.win-ui-light input[type=text], .win-ui-light input[type=password], +.win-ui-light input[type=email], .win-ui-light input[type=number], +.win-ui-light input[type=tel], .win-ui-light input[type=url], +.win-ui-light input[type=search], .win-ui-light select, .win-ui-light textarea, +.win-ui-light .win-textarea, .win-ui-light input::-ms-check { + background-clip: padding-box; + background-color: rgba(255, 255, 255, 0.8); + border-color: rgba(0, 0, 0, 0.27); + color: rgb(0, 0, 0); +} +.win-ui-light input[type=text]:hover, .win-ui-light input[type=password]:hover, +.win-ui-light input[type=email]:hover, .win-ui-light input[type=number]:hover, +.win-ui-light input[type=tel]:hover, .win-ui-light input[type=url]:hover, +.win-ui-light input[type=search]:hover, .win-ui-light textarea:hover, +.win-ui-light select:hover, .win-ui-light .win-textarea:hover, .win-ui-light input:hover::-ms-check { + background-color: rgba(255, 255, 255, 0.87); + border-color: rgba(0, 0, 0, 0.44); + color: rgb(0, 0, 0); +} +.win-ui-light input[type=text]:focus, .win-ui-light input[type=text]:active, +.win-ui-light input[type=password]:focus, .win-ui-light input[type=password]:active, +.win-ui-light input[type=email]:focus, .win-ui-light input[type=email]:active, +.win-ui-light input[type=number]:focus, .win-ui-light input[type=number]:active, +.win-ui-light input[type=tel]:focus, .win-ui-light input[type=tel]:active, +.win-ui-light input[type=url]:focus, .win-ui-light input[type=url]:active, +.win-ui-light input[type=search]:focus, .win-ui-light input[type=search]:active, +.win-ui-light textarea:focus, .win-ui-light textarea:active, +.win-ui-light .win-textarea:focus, .win-ui-light .win-textarea:active, +.win-ui-light select:focus, .win-ui-light select:active { + background-color: rgb(255, 255, 255); + border-color: rgba(0, 0, 0, 0.6); + color: rgb(0, 0, 0); +} +.win-ui-light input:hover:active::-ms-check { /* only checkbox and radio have press state */ + background-clip: border-box; + background-color: rgb(0, 0, 0); + border-color: transparent; + color: rgb(255, 255, 255); +} +.win-ui-light input:-ms-keyboard-active::-ms-check { /* only checkbox and radio have press state */ + background-clip: border-box; + background-color: rgb(0, 0, 0); + border-color: transparent; + color: rgb(255, 255, 255); +} +.win-ui-light input[type=text]:disabled, .win-ui-light input[type=password]:disabled, +.win-ui-light input[type=email]:disabled, .win-ui-light input[type=number]:disabled, +.win-ui-light input[type=tel]:disabled, .win-ui-light input[type=url]:disabled, +.win-ui-light input[type=search]:disabled, .win-ui-light input[type=file]::-ms-value, +.win-ui-light textarea:disabled, .win-ui-light .win-textarea:disabled, .win-ui-light select:disabled, +.win-ui-light input:disabled::-ms-check, .win-ui-light input:disabled:active::-ms-check { + background-clip: padding-box; + background-color: rgba(202, 202, 202, 0.4); + border-color: rgba(0, 0, 0, 0.15); + color: rgba(0, 0, 0, 0.4); +} + +/* +Placeholder text style. +*/ +input[type]:-ms-input-placeholder, .win-ui-light input[type]:-ms-input-placeholder, +textarea:-ms-input-placeholder, .win-ui-light textarea:-ms-input-placeholder { + color: rgba(0, 0, 0, 0.6); /* same in dark and light */ +} +input[type]:disabled:-ms-input-placeholder, textarea:disabled:-ms-input-placeholder { + color: rgba(255, 255, 255, 0.22); +} +.win-ui-light input[type]:disabled:-ms-input-placeholder, .win-ui-light textarea:disabled:-ms-input-placeholder { + color: rgba(0, 0, 0, 0.22); +} + +/* +Invalid style. +*/ +input[type]:invalid { + outline-color: rgb(255, 128, 51); + outline-width: 2px; +} + +/* +Clear and reveal buttons. +*/ +input::-ms-clear, input::-ms-reveal { + background-color: rgb(255, 255, 255); + color: rgb(0, 0, 0); +} +input::-ms-clear:hover, input::-ms-reveal:hover { + background-color: rgb(222, 222, 222); +} +input::-ms-clear:hover:active, input::-ms-reveal:hover:active { + background-color: rgb(0, 0, 0); + color: rgb(255, 255, 255); +} + +/* +Option for select control (all colors are same for both light and dark). +*/ +option:checked, select:focus::-ms-value { + color: rgb(255, 255, 255); + background-color: rgb(70, 23, 180); +} +option:hover { + color: rgb(0, 0, 0); + background-color: rgb(197, 197, 197); +} +select:focus option:hover { + color: rgb(0, 0, 0); + background-color: rgb(222, 222, 222); +} +option:checked:hover, select:focus option:checked:hover { + color: rgb(255, 255, 255); + background-color: rgb(95, 55, 190); +} +option:hover:active, select:focus option:hover:active{ + color: rgb(0, 0, 0); + background-color: rgb(211, 211, 211); +} +option:checked:disabled, option:checked:disabled:active, +optgroup:disabled option:checked, optgroup:disabled option:checked:active, +select:disabled option:checked, select:disabled option:checked:active, +select:disabled:focus::-ms-value { + background-color: rgba(255, 255, 255, 0.4); + color: rgba(0, 0, 0, 0.6); +} +.win-ui-light option:checked:disabled, .win-ui-light option:checked:disabled:active, +.win-ui-light optgroup:disabled option:checked, .win-ui-light optgroup:disabled option:checked:active, +.win-ui-light select:disabled option:checked, .win-ui-light select:disabled option:checked:active, +.win-ui-light select:disabled:focus::-ms-value { + background-color: rgba(0, 0, 0, 0.55); + color: rgba(255, 255, 255, 0.6); +} + +/* +Button control colors. +*/ +button, input[type=button], input[type=submit], +input[type=reset], input[type=file]::-ms-browse { + background-color: transparent; + border-color: rgb(255, 255, 255); + color: rgb(255, 255, 255); +} +button[type=submit], input[type=submit] { + background-clip: padding-box; + background-color: rgb(70, 23, 180); +} +button:hover, input[type=button]:hover, input[type=reset]:hover, +input[type=file]::-ms-browse:hover { + background-color: rgba(255, 255, 255, 0.13); + border-color: rgb(255, 255, 255); +} +button[type=submit]:hover, input[type=submit]:hover { + background-clip: padding-box; + background-color: rgb(95, 55, 190); + border-color: rgb(255, 255, 255); +} +button:hover:active, button[type=submit]:hover:active, +input[type=button]:hover:active, input[type=reset]:hover:active, +input[type=submit]:hover:active, input[type=file]::-ms-browse:hover:active { + background-clip: border-box; + background-color: rgb(255, 255, 255); + border-color: transparent; + color: rgb(0, 0, 0); +} +button:-ms-keyboard-active, button[type=submit]:-ms-keyboard-active, +input[type=button]:-ms-keyboard-active, input[type=reset]:-ms-keyboard-active, +input[type=submit]:-ms-keyboard-active { + background-clip: border-box; + background-color: rgb(255, 255, 255); + border-color: transparent; + color: rgb(0, 0, 0); +} +button:disabled, button[type=submit]:disabled, +input[type=button]:disabled, input[type=reset]:disabled, +input[type=submit]:disabled, input[type=file]:disabled::-ms-browse, +button:disabled:active, button[type=submit]:disabled:active, +input[type=button]:disabled:active, input[type=reset]:disabled:active, +input[type=submit]:disabled:active, input[type=file]:disabled::-ms-browse:active { + background-color: transparent; + border-color: rgba(255, 255, 255, 0.4); + color: rgba(255, 255, 255, 0.4); +} +.win-ui-light button, .win-ui-light input[type=button], .win-ui-light input[type=submit], +.win-ui-light input[type=reset], .win-ui-light input[type=file]::-ms-browse { + background-color: rgba(182, 182, 182, 0.7); + border-color: rgba(0, 0, 0, 0.2); + color: rgb(0, 0, 0); +} +.win-ui-light button[type=submit], .win-ui-light input[type=submit]{ + background-clip: border-box; + background-color: rgb(70, 23, 180); + border-color: transparent; + color: rgb(255, 255, 255); +} +.win-ui-light button:hover, .win-ui-light input[type=button]:hover, .win-ui-light input[type=reset]:hover, +.win-ui-light input[type=file]::-ms-browse:hover { + background-color: rgba(205, 205, 205, 0.82); + border-color: rgba(164, 164, 164, 0.45); +} +.win-ui-light button[type=submit]:hover, .win-ui-light input[type=submit]:hover { + background-clip: border-box; + background-color: rgb(95, 55, 190); + border-color: transparent; +} +.win-ui-light button:hover:active, .win-ui-light button[type=submit]:hover:active, +.win-ui-light input[type=button]:hover:active, .win-ui-light input[type=reset]:hover:active, +.win-ui-light input[type=submit]:hover:active, .win-ui-light input[type=file]::-ms-browse:hover:active { + background-clip: border-box; + background-color: rgb(0, 0, 0); + border-color: transparent; + color: rgb(255, 255, 255); +} +.win-ui-light button:-ms-keyboard-active, .win-ui-light button[type=submit]:-ms-keyboard-active, +.win-ui-light input[type=button]:-ms-keyboard-active, .win-ui-light input[type=reset]:-ms-keyboard-active, +.win-ui-light input[type=submit]:-ms-keyboard-active { + background-clip: border-box; + background-color: rgb(0, 0, 0); + border-color: transparent; + color: rgb(255, 255, 255); +} +.win-ui-light button:disabled, .win-ui-light button[type=submit]:disabled, +.win-ui-light input[type=button]:disabled, .win-ui-light input[type=reset]:disabled, +.win-ui-light input[type=submit]:disabled, .win-ui-light input[type=file]:disabled::-ms-browse, +.win-ui-light button:disabled:active, .win-ui-light button[type=submit]:disabled:active, +.win-ui-light input[type=button]:disabled:active, .win-ui-light input[type=reset]:disabled:active, +.win-ui-light input[type=submit]:disabled:active, .win-ui-light input[type=file]:disabled::-ms-browse:active { + background-color: rgba(202, 202, 202, 0.4); + border-color: rgba(0, 0, 0, 0.08); + color: rgba(0, 0, 0, 0.4); +} +.win-ui-light input[type=file]:disabled::-ms-browse, .win-ui-light input[type=file]:disabled::-ms-browse:active { + border-color: rgba(0, 0, 0, 0.15); +} + +/* +File upload control colors. +*/ +input[type=file] { + background-color: transparent; + border-color: transparent; + color: transparent; +} + +/* +Link colors. +*/ +a { + color: rgb(156, 114, 255); +} +a:hover:active { + color: rgba(156, 114, 255, 0.6); +} +a:hover { + color: rgba(156, 114, 255, 0.8); +} +a[disabled], a[disabled]:active { /* :disabled pseudo-class doesn't apply to a, even though disabled attribute stops navigation */ + color: rgba(255, 255, 255, 0.4); +} +.win-ui-light a { + color: rgb(79, 26, 203); +} +.win-ui-light a:hover:active { + color: rgba(79, 26, 203, 0.6); +} +.win-ui-light a:hover { + color: rgba(79, 26, 203, 0.8); +} +.win-ui-light a[disabled], .win-ui-light a[disabled]:active { /* :disabled pseudo-class doesn't apply to a, even though disabled attribute stops navigation */ + color: rgba(0, 0, 0, 0.4); +} + +/* +Range control colors. +*/ +input[type=range], input[type=range]::-ms-track { + background-color: transparent; +} + +input[type=range]::-ms-fill-lower { + background-color: rgb(91, 46, 197); +} +input[type=range]:hover::-ms-fill-lower { + background-color: rgb(114, 75, 205); +} +input[type=range]:active::-ms-fill-lower { + background-color: rgb(129, 82, 239); +} +input[type=range]:disabled::-ms-fill-lower { + background-color: rgba(255, 255, 255, 0.23); +} +.win-ui-light input[type=range]::-ms-fill-lower { + background-color: rgb(70, 23, 180); +} +.win-ui-light input[type=range]:hover::-ms-fill-lower { + background-color: rgb(95, 55, 190); +} +.win-ui-light input[type=range]:active::-ms-fill-lower { + background-color: rgb(114, 65, 228); +} +.win-ui-light input[type=range]:disabled::-ms-fill-lower { + background-color: rgba(0, 0, 0, 0.2); +} + +input[type=range]::-ms-fill-upper { + background-color: rgba(255, 255, 255, 0.16); +} +input[type=range]:hover::-ms-fill-upper { + background-color: rgba(255, 255, 255, 0.18); +} +input[type=range]:active::-ms-fill-upper { + background-color: rgba(255, 255, 255, 0.23); +} +input[type=range]:disabled::-ms-fill-upper { + background-color: rgba(255, 255, 255, 0.16); +} +.win-ui-light input[type=range]::-ms-fill-upper { + background-color: rgba(0, 0, 0, 0.1); +} +.win-ui-light input[type=range]:hover::-ms-fill-upper { + background-color: rgba(0, 0, 0, 0.15); +} +.win-ui-light input[type=range]:active::-ms-fill-upper { + background-color: rgba(0, 0, 0, 0.2); +} +.win-ui-light input[type=range]:disabled::-ms-fill-upper { + background-color: rgba(0, 0, 0, 0.1); +} + +input[type=range]::-ms-thumb { + background-color: rgb(255, 255, 255); +} +input[type=range]:disabled::-ms-thumb { + background-color: rgb(126, 126, 126); +} +.win-ui-light input[type=range]::-ms-thumb { + background-color: rgb(0, 0, 0); +} +.win-ui-light input[type=range]:disabled::-ms-thumb { + background-color: rgb(146, 146, 146); +} + +input[type=range]::-ms-ticks-before, +input[type=range]::-ms-ticks-after { + color: rgba(255, 255, 255, 0.5); +} +.win-ui-light input[type=range]::-ms-ticks-before, +.win-ui-light input[type=range]::-ms-ticks-after { + color: rgba(0, 0, 0, 0.5); +} + +/* +Progress control colors. +*/ +progress { + background-color: rgba(255,255,255,0.35); + color: rgb(91, 46, 197); +} +.win-ui-light progress { + background-color: rgba(0, 0, 0, 0.2); + color: rgb(70, 23, 180); +} +progress:indeterminate { + color: rgb(138, 87, 255); +} +.win-ui-light progress:indeterminate { + color: rgb(70, 23, 180); +} +progress::-ms-fill { + background-color: currentColor; +} +/* +Explicitly define indeterminate background transparent for dark and light +because ".win-ui-light progress" has higher specificity than "progress:indeterminate". +*/ +progress:indeterminate, .win-ui-light progress:indeterminate { + background-color: transparent; +} + +/* +Hub colors. +*/ +button.win-hub-section-header-tabstop, +button.win-hub-section-header-tabstop:hover, +button.win-hub-section-header-tabstop:hover:active { + color: rgb(255, 255, 255); +} +button.win-hub-section-header-tabstop.win-keyboard:focus { + outline: 1px dotted rgb(255, 255, 255); +} +button.win-hub-section-header-tabstop:-ms-keyboard-active { + color: rgb(255, 255, 255); +} +button.win-hub-section-header-tabstop.win-hub-section-header-interactive:hover { + color: rgba(255, 255, 255, 0.8); +} +button.win-hub-section-header-tabstop.win-hub-section-header-interactive:hover:active { + color: rgba(255, 255, 255, 0.4); +} +button.win-hub-section-header-tabstop.win-hub-section-header-interactive:-ms-keyboard-active { + color: rgba(255, 255, 255, 0.4); +} +.win-ui-light button.win-hub-section-header-tabstop, +.win-ui-light button.win-hub-section-header-tabstop:hover, +.win-ui-light button.win-hub-section-header-tabstop:hover:active { + color: rgb(0, 0, 0); +} +.win-ui-light button.win-hub-section-header-tabstop.win-keyboard:focus { + outline: 1px dotted rgb(0, 0, 0); +} +.win-ui-light button.win-hub-section-header-tabstop:-ms-keyboard-active { + color: rgb(0, 0, 0); +} +.win-ui-light button.win-hub-section-header-tabstop.win-hub-section-header-interactive:hover { + color: rgba(0, 0, 0, 0.8); +} +.win-ui-light button.win-hub-section-header-tabstop.win-hub-section-header-interactive:hover:active { + color: rgba(0, 0, 0, 0.4); +} +.win-ui-light button.win-hub-section-header-tabstop.win-hub-section-header-interactive:-ms-keyboard-active { + color: rgba(0, 0, 0, 0.4); +} + +/* +NavBarContainer colors. +*/ +.win-navbarcontainer-pageindicator { + background-color: rgba(255,255,255,0.4); +} +.win-ui-light .win-navbarcontainer-pageindicator { + background-color: rgba(0,0,0,0.3); +} +.win-navbarcontainer-pageindicator-current { + background-color: rgba(255,255,255,0.8); +} +.win-ui-light .win-navbarcontainer-pageindicator-current { + background-color: rgba(0,0,0,0.66); +} +.win-navbarcontainer-pageindicator:hover { + background-color: rgba(255,255,255,0.6); +} +.win-ui-light .win-navbarcontainer-pageindicator:hover { + background-color: rgba(0,0,0,0.44); +} +.win-navbarcontainer-pageindicator:hover:active { + background-color: rgba(255,255,255,0.7); +} +.win-ui-light .win-navbarcontainer-pageindicator:hover:active { + background-color: rgba(0,0,0,0.58); +} +.win-navbarcontainer-navarrow { + background-color: rgb(74,74,74); + color: rgb(30,30,30) +} +.win-navbarcontainer-navarrow:hover { + background-color: rgb(202,202,202); + color: rgb(0,0,0) +} +.win-navbarcontainer-navarrow:hover:active { + background-color: rgb(30,30,30); + color: rgb(255,255,255); +} +.win-ui-light .win-navbarcontainer-navarrow { + background-color: rgb(231,231,231); + color: rgb(92,92,92) +} +.win-ui-light .win-navbarcontainer-navarrow:hover { + background-color: rgb(218,218,218); + color: rgb(0,0,0) +} +.win-ui-light .win-navbarcontainer-navarrow:hover:active { + background-color: rgb(93,93,93); + color: rgb(255,255,255); +} + +/* + NavBarCommand colors. +*/ +.win-navbarcommand-button, +.win-navbarcommand-splitbutton { + background-color: rgba(255,255,255,0.11); + color: rgb(255,255,255); +} +.win-ui-light .win-navbarcommand-button, +.win-ui-light .win-navbarcommand-splitbutton { + background-color: rgba(0,0,0,0.09); + color: rgb(0,0,0); +} +.win-navbarcommand-button:hover, +.win-navbarcommand-splitbutton:hover { + background-color: rgba(255,255,255,0.22); +} +.win-ui-light .win-navbarcommand-button:hover, +.win-ui-light .win-navbarcommand-splitbutton:hover { + background-color: rgba(199,199,199,0.31); +} +.win-navbarcommand-button.win-keyboard:focus, +.win-navbarcommand-splitbutton.win-keyboard:focus { + outline: 2px solid rgb(255,255,255); +} +.win-ui-light .win-navbarcommand-button.win-keyboard:focus, +.win-ui-light .win-navbarcommand-splitbutton.win-keyboard:focus { + outline: 2px solid rgb(0,0,0); +} +.win-navbarcommand-splitbutton.win-navbarcommand-splitbutton-opened, +.win-navbarcommand-splitbutton.win-navbarcommand-splitbutton-opened:hover, +.win-navbarcommand-button.win-pressed, +.win-navbarcommand-button.win-pressed:hover, +.win-navbarcommand-splitbutton.win-pressed, +.win-navbarcommand-splitbutton.win-pressed:hover { + background-color: rgb(255,255,255); + color: rgb(0,0,0); +} +.win-ui-light .win-navbarcommand-splitbutton.win-navbarcommand-splitbutton-opened, +.win-ui-light .win-navbarcommand-splitbutton.win-navbarcommand-splitbutton-opened:hover, +.win-ui-light .win-navbarcommand-button.win-pressed, +.win-ui-light .win-navbarcommand-button.win-pressed:hover, +.win-ui-light .win-navbarcommand-splitbutton.win-pressed, +.win-ui-light .win-navbarcommand-splitbutton.win-pressed:hover { + background-color: rgb(0,0,0); + color: rgb(255,255,255); +} + +/* +FlipView control colors. +*/ +.win-flipview .win-navbutton { + background-color: rgba(213, 213, 213, 0.35); + color: rgba(0, 0, 0, 0.60); +} +.win-flipview .win-navbutton:hover { + background-color: rgba(215, 215, 215, 0.94); + color: rgba(0, 0, 0, 1.0); +} +.win-flipview .win-navbutton:hover:active { + background-color: rgba(41, 41, 41, 0.74); + color: rgba(255, 255, 255, 1.0); +} + +/* +ListView control colors. +*/ +.win-listview .win-itembox, +.win-itemcontainer .win-itembox { + background-color: rgb(29, 29, 29); +} +.win-ui-light .win-listview .win-itembox, +.win-ui-light .win-itemcontainer .win-itembox { + background-color: rgb(255, 255, 255); +} +.win-listview .win-container.win-backdrop { + background-color: rgba(155,155,155,0.23); +} +.win-ui-light .win-listview .win-container.win-backdrop { + background-color: rgba(155,155,155,0.23); +} +.win-listview .win-container:hover, +.win-itemcontainer.win-container:hover { + outline: rgba(255, 255, 255, 0.3) solid 3px; +} +.win-listview .win-groupheader { + outline-color: rgb(255, 255, 255); +} +.win-listview.win-selectionstylefilled .win-container:hover .win-itembox, +.win-itemcontainer.win-selectionstylefilled.win-container:hover .win-itembox { + background-color: rgba(255, 255, 255, 0.3); +} +.win-listview .win-focusedoutline, +.win-itemcontainer .win-focusedoutline { + outline: rgb(255, 255, 255) solid 2px; +} +.win-ui-light .win-listview .win-container:hover, +.win-ui-light .win-itemcontainer.win-container:hover { + outline: rgba(0, 0, 0, 0.3) solid 3px; +} +.win-ui-light .win-listview .win-groupheader{ + outline-color: rgb(0, 0, 0); +} +.win-ui-light .win-listview.win-selectionstylefilled .win-container:hover .win-itembox, +.win-ui-light .win-itemcontainer.win-selectionstylefilled.win-container:hover .win-itembox { + background-color: rgba(0, 0, 0, 0.3); +} +.win-ui-light .win-listview .win-focusedoutline, +.win-ui-light .win-itemcontainer .win-focusedoutline { + outline: rgb(0, 0, 0) solid 2px; +} +.win-listview.win-selectionstylefilled .win-container.win-selected:hover .win-itembox, +.win-itemcontainer.win-selectionstylefilled.win-container.win-selected:hover .win-itembox +{ + background-color: rgb(95, 55, 190); +} +.win-listview .win-container.win-swipe:hover .win-itembox, +.win-itemcontainer.win-container.win-swipe:hover .win-itembox { + background-color: rgb(29, 29, 29); + outline: none; +} +.win-ui-light .win-listview .win-container.win-swipe:hover .win-itembox, +.win-ui-light .win-itemcontainer.win-container.win-swipe:hover .win-itembox { + background-color: rgb(255, 255, 255); + outline: none; +} +.win-listview.win-selectionstylefilled .win-selected, +.win-itemcontainer.win-selectionstylefilled.win-selected { + color: rgb(255, 255, 255); +} +.win-listview:not(.win-selectionstylefilled) .win-container.win-selected .win-selectionborder, +.win-itemcontainer:not(.win-selectionstylefilled).win-container.win-selected .win-selectionborder { + border-color: rgb(70, 23, 180); +} +.win-listview.win-selectionstylefilled .win-container.win-selected .win-selectionborder, +.win-itemcontainer.win-selectionstylefilled.win-container.win-selected .win-selectionborder { + border-color: transparent; +} +.win-listview:not(.win-selectionstylefilled) .win-container.win-selected:hover .win-selectionborder, +.win-itemcontainer:not(.win-selectionstylefilled).win-container.win-selected:hover .win-selectionborder { + border-color: rgb(95, 55, 190); +} +.win-listview.win-selectionstylefilled .win-selected .win-selectionbackground, +.win-itemcontainer.win-selectionstylefilled.win-selected .win-selectionbackground { + background-color: rgb(70, 23, 180); +} +.win-listview.win-selectionstylefilled .win-selected:hover .win-selectionbackground, +.win-itemcontainer.win-selectionstylefilled.win-selected:hover .win-selectionbackground { + background-color: rgb(95, 55, 190); +} +.win-selectioncheckmark { + color: rgb(255, 255, 255); +} +.win-selectionhint { + color: rgb(255, 255, 255); +} +.win-ui-light .win-selectionhint { + color: rgb(70, 23, 180); +} +.win-listview:not(.win-selectionstylefilled) .win-selectioncheckmarkbackground, +.win-itemcontainer:not(.win-selectionstylefilled) .win-selectioncheckmarkbackground { + border-top-color: rgb(70, 23, 180); + border-right-color: rgb(70, 23, 180); + border-left-color: transparent; + border-bottom-color: transparent; +} +.win-listview.win-selectionstylefilled .win-selectioncheckmarkbackground, +.win-itemcontainer.win-selectionstylefilled .win-selectioncheckmarkbackground { + border-color: transparent; +} +.win-listview:not(.win-selectionstylefilled) .win-container.win-selected:hover .win-selectioncheckmarkbackground, +.win-itemcontainer:not(.win-selectionstylefilled).win-container.win-selected:hover .win-selectioncheckmarkbackground { + border-top-color: rgb(95, 55, 190); + border-right-color: rgb(95, 55, 190); + border-left-color: transparent; + border-bottom-color: transparent; +} +.win-listview.win-rtl:not(.win-selectionstylefilled) .win-selectioncheckmarkbackground, +.win-itemcontainer.win-rtl:not(.win-selectionstylefilled) .win-selectioncheckmarkbackground { + border-left-color: rgb(70, 23, 180); + border-right-color: transparent; +} +.win-listview.win-rtl:not(.win-selectionstylefilled) .win-container.win-selected:hover .win-selectioncheckmarkbackground, +.win-itemcontainer.win-rtl:not(.win-selectionstylefilled).win-container.win-selected:hover .win-selectioncheckmarkbackground { + border-left-color: rgb(95, 55, 190); + border-right-color: transparent; +} + +.win-listview.win-selectionstylefilled .win-selected a, +.win-listview.win-selectionstylefilled .win-selected progress, +.win-listview.win-selectionstylefilled .win-selected .win-rating .win-star.win-full, +.win-itemcontainer.win-selectionstylefilled.win-selected a, +.win-itemcontainer.win-selectionstylefilled.win-selected progress, +.win-itemcontainer.win-selectionstylefilled.win-selected .win-rating .win-star.win-full { + color: rgb(255, 255, 255); +} +.win-listview.win-selectionstylefilled .win-selected a:hover:active, +.win-itemcontainer.win-selectionstylefilled.win-selected a:hover:active { + color: rgba(255, 255, 255, 0.6); +} +.win-listview.win-selectionstylefilled .win-selected a:hover, +.win-itemcontainer.win-selectionstylefilled.win-selected a:hover { + color: rgba(255, 255, 255, 0.8); +} +.win-listview.win-selectionstylefilled .win-selected button, +.win-listview.win-selectionstylefilled .win-selected input[type=button], +.win-listview.win-selectionstylefilled .win-selected input[type=reset], +.win-listview.win-selectionstylefilled .win-selected input[type=text], +.win-listview.win-selectionstylefilled .win-selected input[type=password], +.win-listview.win-selectionstylefilled .win-selected input[type=email], +.win-listview.win-selectionstylefilled .win-selected input[type=number], +.win-listview.win-selectionstylefilled .win-selected input[type=tel], +.win-listview.win-selectionstylefilled .win-selected input[type=url], +.win-listview.win-selectionstylefilled .win-selected input[type=search], +.win-listview.win-selectionstylefilled .win-selected input::-ms-check, +.win-listview.win-selectionstylefilled .win-selected textarea, +.win-listview.win-selectionstylefilled .win-selected .win-textarea, +.win-listview.win-selectionstylefilled .win-selected select, +.win-itemcontainer.win-selectionstylefilled.win-selected button, +.win-itemcontainer.win-selectionstylefilled.win-selected input[type=button], +.win-itemcontainer.win-selectionstylefilled.win-selected input[type=reset], +.win-itemcontainer.win-selectionstylefilled.win-selected input[type=text], +.win-itemcontainer.win-selectionstylefilled.win-selected input[type=password], +.win-itemcontainer.win-selectionstylefilled.win-selected input[type=email], +.win-itemcontainer.win-selectionstylefilled.win-selected input[type=number], +.win-itemcontainer.win-selectionstylefilled.win-selected input[type=tel], +.win-itemcontainer.win-selectionstylefilled.win-selected input[type=url], +.win-itemcontainer.win-selectionstylefilled.win-selected input[type=search], +.win-itemcontainer.win-selectionstylefilled.win-selected input::-ms-check, +.win-itemcontainer.win-selectionstylefilled.win-selected textarea, +.win-itemcontainer.win-selectionstylefilled.win-selected .win-textarea, +.win-itemcontainer.win-selectionstylefilled.win-selected select { + background-clip: border-box; + background-color: rgba(255, 255, 255, 0.8); + border-color: transparent; + color: rgb(0, 0, 0); +} +.win-listview.win-selectionstylefilled .win-selected button[type=submit], +.win-listview.win-selectionstylefilled .win-selected input[type=submit], +.win-itemcontainer.win-selectionstylefilled.win-selected button[type=submit], +.win-itemcontainer.win-selectionstylefilled.win-selected input[type=submit] { + border-color: rgb(255, 255, 255); +} +.win-listview.win-selectionstylefilled .win-selected input[type=range]::-ms-fill-lower, +.win-itemcontainer.win-selectionstylefilled.win-selected input[type=range]::-ms-fill-lower { + background-color: rgb(255, 255, 255); +} +.win-listview.win-selectionstylefilled .win-selected input[type=range]::-ms-thumb, +.win-itemcontainer.win-selectionstylefilled.win-selected input[type=range]::-ms-thumb { + background-color: rgb(0,0,0); +} +.win-listview.win-selectionstylefilled .win-selected input[type=range]::-ms-fill-upper, +.win-listview.win-selectionstylefilled .win-selected progress, +.win-itemcontainer.win-selectionstylefilled.win-selected input[type=range]::-ms-fill-upper, +.win-itemcontainer.win-selectionstylefilled.win-selected progress { + background-color: rgba(255, 255, 255, 0.16); +} +.win-listview.win-selectionstylefilled .win-selected progress:indeterminate, +.win-itemcontainer.win-selectionstylefilled.win-selected progress:indeterminate { + background-color: transparent; +} +.win-listview.win-selectionstylefilled .win-selected .win-rating .win-star.win-empty, +.win-itemcontainer.win-selectionstylefilled.win-selected .win-rating .win-star.win-empty { + color: rgba(255, 255, 255, 0.16); +} + +/* +Color for the BackButton controls +*/ +.win-navigation-backbutton, .win-navigation-backbutton:active, +.win-navigation-backbutton:hover, .win-navigation-backbutton:active:hover +{ + background-color: transparent; +} +.win-backbutton, .win-back { + background-color: transparent; + border-color: rgb(255, 255, 255); + color: rgb(255, 255, 255); +} +.win-backbutton:hover, +.win-navigation-backbutton:hover .win-back { + background-color: rgba(255, 255, 255, 0.13); + border-color: rgb(255, 255, 255); +} +.win-backbutton:hover:active, +.win-navigation-backbutton:hover:active .win-back, +.win-navigation-backbutton:active .win-back { + background-color: rgb(255, 255, 255); + border-color: rgb(255, 255, 255); + color: rgb(0, 0, 0); +} +.win-backbutton:-ms-keyboard-active, +.win-navigation-backbutton:-ms-keyboard-active .win-back { + background-color: rgb(255, 255, 255); + border-color: rgb(255, 255, 255); + color: rgb(0, 0, 0); +} +.win-backbutton:disabled, .win-backbutton:disabled:active, +.win-navigation-backbutton:disabled .win-back, +.win-navigation-backbutton:disabled:active .win-back { + background-clip: padding-box; + background-color: transparent; + border-color: rgba(255, 255, 255, 0.4); + color: rgba(255, 255, 255, 0.4); +} +.win-ui-light .win-navigation-backbutton, .win-ui-light .win-navigation-backbutton:active, +.win-ui-light .win-navigation-backbutton:hover, .win-ui-light .win-navigation-backbutton:active:hover +{ + background-color: transparent; +} +.win-ui-light .win-backbutton, .win-ui-light .win-back { + background-color: transparent; + border-color: rgb(0, 0, 0); + color: rgb(0, 0, 0); +} +.win-ui-light .win-backbutton:hover, +.win-ui-light .win-navigation-backbutton:hover .win-back { + background-color: rgba(0, 0, 0, 0.13); + border-color: rgb(0, 0, 0); +} +.win-ui-light .win-backbutton:hover:active, +.win-ui-light .win-navigation-backbutton:hover:active .win-back { + background-color: rgb(0, 0, 0); + border-color: rgb(0, 0, 0); + color: rgb(255, 255, 255); +} +.win-ui-light .win-backbutton:-ms-keyboard-active, +.win-ui-light .win-navigation-backbutton:-ms-keyboard-active .win-back { + background-color: rgb(0, 0, 0); + border-color: rgb(0, 0, 0); + color: rgb(255, 255, 255); +} +.win-ui-light .win-backbutton:disabled, .win-ui-light .win-backbutton:disabled:active, +.win-ui-light .win-navigation-backbutton:disabled .win-back, +.win-ui-light .win-navigation-backbutton:disabled:active .win-back { + background-clip: padding-box; + background-color: transparent; + border-color: rgba(0, 0, 0, 0.4); + color: rgba(0, 0, 0, 0.4); +} + +/* +Colors for command buttons and AppBarCommand "content" types. +*/ +button.win-command, div.win-command { + background-color: transparent; + border-color: transparent; +} +button.win-command:hover, div.win-command:hover { + background-color: transparent; + border-color: transparent; +} +button.win-command:active, div.win-command:active { + background-color: transparent; + color: inherit; +} +button.win-command:hover:active, div.win-command:hover:active { + background-color: transparent; + color: inherit; +} +button.win-command:-ms-keyboard-active, div.win-command:-ms-keyboard-active { + background-color: transparent; + color: inherit; +} +button.win-command:disabled, div.win-command:disabled { + border-color: transparent; + background-color: transparent; +} +button.win-command:focus, div.win-command:focus { + border-color: rgb(255, 255, 255); +} +.win-ui-light button.win-command:focus, .win-ui-light div.win-command:focus { + border-color: rgb(0, 0, 0); +} +.win-hidefocus:focus { + outline: none; +} +/* + win-hide-focus needs the qualifier for win-ui-light or dark, + otherwise .win-ui-dark button.win-command:focus is more + qualified and stomps on the transparent that doesn't have .win-ui-dark +*/ +.win-command.win-hidefocus:focus, +.win-ui-light .win-command.win-hidefocus:focus { + border-color: transparent; +} +.win-commandimage { + color: rgb(255, 255, 255); +} +button:hover:active .win-commandimage { + color: rgb(0, 0, 0); +} +button:-ms-keyboard-active .win-commandimage { + color: rgb(0, 0, 0); +} +button:disabled .win-commandimage, +button:disabled:active .win-commandimage { + color: rgba(255, 255, 255, 0.4); +} +button[aria-checked=true] .win-commandimage, +button[aria-checked=true]:active .win-commandimage { + color: rgb(0, 0, 0); +} + +button[aria-checked=true]:hover .win-commandimage { + color: rgb(0, 0, 0); +} + +button[aria-checked=true]:hover:active .win-commandimage { + color: rgb(255, 255, 255); +} +button[aria-checked=true]:-ms-keyboard-active .win-commandimage { + color: rgb(255, 255, 255); +} +button[aria-checked=true]:disabled .win-commandimage, +button[aria-checked=true]:disabled:active .win-commandimage { + color: rgb(0, 0, 0); +} +.win-ui-light .win-commandimage { + color: rgb(0, 0, 0); +} +.win-ui-light button:hover:active .win-commandimage { + color: rgb(255, 255, 255); +} +.win-ui-light button:-ms-keyboard-active .win-commandimage { + color: rgb(255, 255, 255); +} +.win-ui-light button:disabled .win-commandimage, +.win-ui-light button:disabled:active .win-commandimage { + color: rgba(0, 0, 0, 0.4); +} +.win-ui-light button[aria-checked=true] .win-commandimage, +.win-ui-light button[aria-checked=true]:active .win-commandimage { + color: rgb(255, 255, 255); +} +.win-ui-light button[aria-checked=true]:hover .win-commandimage { + color: rgb(255, 255, 255); +} +.win-ui-light button[aria-checked=true]:hover:active .win-commandimage { + color: rgb(0, 0, 0); +} +.win-ui-light button[aria-checked=true]:-ms-keyboard-active .win-commandimage { + color: rgb(0, 0, 0); +} +.win-ui-light button[aria-checked=true]:disabled .win-commandimage, +.win-ui-light button[aria-checked=true]:disabled:active .win-commandimage { + color: rgb(255, 255, 255); +} + +/* +Command ring colors. +*/ +.win-commandring, button:active .win-commandring { + background-color: transparent; + border-color: rgb(255, 255, 255); +} +button:hover .win-commandring { + background-color: rgba(255, 255, 255, 0.13); + border-color: rgb(255, 255, 255); +} +button:hover:active .win-commandring { + background-color: rgb(255, 255, 255); + border-color: rgb(255, 255, 255); +} +button:-ms-keyboard-active .win-commandring { + background-color: rgb(255, 255, 255); + border-color: rgb(255, 255, 255); +} +button:disabled .win-commandring, +button:disabled:active .win-commandring { + background-color: transparent; + border-color: rgba(255, 255, 255, 0.4); +} +button[aria-checked=true] .win-commandring, +button[aria-checked=true]:active .win-commandring { + background-color: rgb(255, 255, 255); + border-color: rgb(255, 255, 255); +} +button[aria-checked=true]:hover .win-commandring { + background-color: rgb(222, 222, 222); + border-color: rgb(222, 222, 222); +} +button[aria-checked=true]:hover:active .win-commandring { + background-color: transparent; + border-color: rgb(255, 255, 255); +} +button[aria-checked=true]:-ms-keyboard-active .win-commandring { + background-color: transparent; + border-color: rgb(255, 255, 255); +} +button[aria-checked=true]:disabled .win-commandring, +button[aria-checked=true]:disabled:active .win-commandring { + background-color: rgba(255, 255, 255, 0.4); + border-color: rgba(255, 255, 255, 0.4); +} +.win-ui-light .win-commandring, .win-ui-light button:active .win-commandring { + background-color: transparent; + border-color: rgb(0, 0, 0); +} +.win-ui-light button:hover .win-commandring { + background-color: rgba(0, 0, 0, 0.13); + border-color: rgb(0, 0, 0); +} +.win-ui-light button:hover:active .win-commandring { + background-color: rgb(0, 0, 0); + border-color: rgb(0, 0, 0); +} +.win-ui-light button:-ms-keyboard-active .win-commandring { + background-color: rgb(0, 0, 0); + border-color: rgb(0, 0, 0); +} +.win-ui-light button:disabled .win-commandring, +.win-ui-light button:disabled:active .win-commandring { + background-color: transparent; + border-color: rgba(0, 0, 0, 0.4); +} +.win-ui-light button[aria-checked=true] .win-commandring, +.win-ui-light button[aria-checked=true]:active .win-commandring { + background-color: rgb(0, 0, 0); + border-color: rgb(0, 0, 0); +} +.win-ui-light button[aria-checked=true]:hover .win-commandring { + background-color: rgb(33, 33, 33); + border-color: rgb(33, 33, 33); +} +.win-ui-light button[aria-checked=true]:hover:active .win-commandring { + background-color: transparent; + border-color: rgb(0, 0, 0); +} +.win-ui-light button[aria-checked=true]:-ms-keyboard-active .win-commandring { + background-color: transparent; + border-color: rgb(0, 0, 0); +} +.win-ui-light button[aria-checked=true]:disabled .win-commandring, +.win-ui-light button[aria-checked=true]:disabled:active .win-commandring { + background-color: rgba(0, 0, 0, 0.4); + border-color: rgba(0, 0, 0, 0.4); +} + +/* +Command button labels colors. +*/ +button.win-command:disabled .win-label, +button.win-command:disabled:active .win-label { + color: rgba(255, 255, 255, 0.4); +} +.win-ui-light button.win-command:disabled .win-label, +.win-ui-light button.win-command:disabled:active .win-label { + color: rgba(0, 0, 0, 0.4); +} + +/* +AppBarCommand (and MenuCommand) separator. +*/ +hr.win-command { + /* Same color in both light and dark. */ + background-color: rgb(123, 123, 123); +} + +/* +AppBar control colors. +*/ +.win-appbar { + background-color: rgb(0, 0, 0); + border-color: rgb(0, 0, 0); +} +.win-ui-light.win-appbar, .win-ui-light .win-appbar { + background-color: rgb(240, 240, 240); + border-color: rgb(240, 240, 240); +} + +/* +Flyout control colors, flyout normally has light theme. +*/ +.win-ui-light.win-flyout, .win-ui-light .win-flyout { + border-color: rgb(0, 0, 0); + background-color: rgb(255, 255, 255); +} + +.win-ui-light.win-settingsflyout, .win-ui-light .win-settingsflyout { + border-color: rgba(0, 0, 0, 0.24); +} + +.win-flyout { + border-color: rgb(255, 255, 255); + background-color: rgb(0, 0, 0); +} + +.win-settingsflyout { + border-color: rgba(255, 255, 255, 0.24); +} + +/* +Settings flyout is light in both themes unless the app explicitly overrides it. +*/ +.win-settingsflyout { + background-color: rgb(255, 255, 255); +} +.win-settingsflyout.win-ui-dark { + background-color: rgb(0, 0, 0); +} + +/* +Settings flyout will be white in dark theme so we need to have the header stay +aligned with the theme unless the app explicitly overrides it. +*/ + +.win-settingsflyout .win-header { + background-color: rgb(0, 0, 0); +} +.win-settingsflyout .win-header.win-ui-light { + background-color: rgb(255, 255, 255); +} + +/* +Menu button colors, menu is always light theme. +*/ +.win-ui-light.win-menu button, .win-ui-light .win-menu button { + background-color: transparent; + color: rgb(0, 0, 0); +} +.win-ui-light.win-menu button:focus, .win-ui-light .win-menu button:focus, +.win-ui-light.win-menu button:active, .win-ui-light .win-menu button:active { + background-color: rgb(222, 222, 222); +} +.win-ui-light.win-menu button:hover:active, .win-ui-light .win-menu button:hover:active { + color: rgb(255, 255, 255); + background-color: rgb(0, 0, 0); +} +.win-ui-light.win-menu button:-ms-keyboard-active, .win-ui-light .win-menu button:-ms-keyboard-active { + color: rgb(255, 255, 255); + background-color: rgb(0, 0, 0); +} +.win-ui-light.win-menu button:disabled, .win-ui-light .win-menu button:disabled, +.win-ui-light.win-menu button:disabled:active, .win-ui-light .win-menu button:disabled:active { + background-color: transparent; + color: rgba(0, 0, 0, 0.1); +} +.win-menu button { + background-color: transparent; + color: rgb(255, 255, 255); +} +.win-menu button:focus, +.win-menu button:active { + background-color: rgb(222, 222, 222); +} +.win-menu button:hover:active { + color: rgb(0, 0, 0); + background-color: rgb(255, 255, 255); +} +.win-menu button:-ms-keyboard-active { + color: rgb(0, 0, 0); + background-color: rgb(255, 255, 255); +} +.win-menu button:disabled, +.win-menu button:disabled:active { + background-color: transparent; + color: rgba(255, 255, 255, 0.1); +} + +/* +Rating control colors. +*/ +.win-rating .win-star.win-user.win-full, +.win-rating .win-star.win-user.win-full.win-disabled { + color: rgb(91, 46, 197); +} +.win-rating .win-star.win-tentative.win-full { + color: rgb(129, 82, 239); +} +.win-rating .win-star.win-average.win-full, +.win-rating .win-star.win-average.win-full.win-disabled { + color: rgb(255, 255, 255); +} +.win-rating .win-star.win-empty { + color: rgba(255, 255, 255, 0.35); +} +.win-ui-light .win-rating .win-star.win-user.win-full, +.win-ui-light .win-rating .win-star.win-user.win-full.win-disabled { + color: rgb(70, 23, 180); +} +.win-ui-light .win-rating .win-star.win-tentative.win-full { + color: rgb(114, 65, 228); +} +.win-ui-light .win-rating .win-star.win-average.win-full, +.win-ui-light .win-rating .win-star.win-average.win-full.win-disabled { + color: rgb(0, 0, 0); +} +.win-ui-light .win-rating .win-star.win-empty { + color: rgba(0, 0, 0, 0.35); +} + +/* +SemanticZoom button colors. +*/ +.win-semanticzoom-button { + background-color: rgba(216, 216, 216, 0.33); + border-color: transparent; +} +button:hover.win-semanticzoom-button { + background-color: rgba(216, 216, 216, 1.0); +} +button.win-semanticzoom-button:active { + background-color: rgb(255, 255, 255); +} +.win-ui-light button.win-semanticzoom-button:active { + background-color: rgb(0, 0, 0); +} + +/* +Toggleswitch control colors. +*/ + +.win-toggleswitch .win-title { + color: rgb(255, 255, 255); +} +.win-toggleswitch .win-title.win-disabled { + color: rgba(255, 255, 255, 0.4); +} +.win-ui-light .win-toggleswitch .win-title { + color: rgb(0, 0, 0); +} +.win-ui-light .win-toggleswitch .win-title.win-disabled { + color: rgba(0, 0, 0, 0.4); +} + +.win-toggleswitch .win-label { + color: rgb(255, 255, 255); +} +.win-toggleswitch .win-label.win-disabled { + color: rgba(255, 255, 255, 0.4); +} +.win-ui-light .win-toggleswitch .win-label { + color: rgb(0, 0, 0); +} +.win-ui-light .win-toggleswitch .win-label.win-disabled { + color: rgba(0, 0, 0, 0.4); +} + +.win-toggleswitch .win-switch::-ms-track { + background-color: transparent; +} +.win-toggleswitch .win-switch::-ms-track { + border-color: rgba(255, 255, 255, 0.35); +} +.win-toggleswitch .win-switch::-ms-fill-upper { + background-color: rgba(255, 255, 255, 0.26); +} +.win-toggleswitch .win-switch:hover::-ms-fill-upper { + background-color: rgba(255, 255, 255, 0.29); +} +.win-toggleswitch .win-switch:active::-ms-fill-upper { + background-color: rgba(255, 255, 255, 0.35); +} +.win-toggleswitch .win-switch:disabled::-ms-track { + border-color: rgba(255, 255, 255, 0.2); +} +.win-toggleswitch .win-switch:disabled::-ms-fill-lower, +.win-toggleswitch .win-switch:disabled::-ms-fill-upper { + background-color: rgba(255, 255, 255, 0.12); +} +.win-ui-light .win-toggleswitch .win-switch::-ms-track { + border-color: rgba(0, 0, 0, 0.35); +} +.win-ui-light .win-toggleswitch .win-switch::-ms-fill-upper { + background-color: rgba(0, 0, 0, 0.26); +} +.win-ui-light .win-toggleswitch .win-switch:hover::-ms-fill-upper { + background-color: rgba(0, 0, 0, 0.29); +} +.win-ui-light .win-toggleswitch .win-switch:active::-ms-fill-upper { + background-color: rgba(0, 0, 0, 0.35); +} +.win-ui-light .win-toggleswitch .win-switch:disabled::-ms-track { + border-color: rgba(0, 0, 0, 0.2); +} +.win-ui-light .win-toggleswitch .win-switch:disabled::-ms-fill-lower, +.win-ui-light .win-toggleswitch .win-switch:disabled::-ms-fill-upper { + background-color: rgba(0, 0, 0, 0.12); +} + +/* +Tooltip control colors. +*/ +.win-tooltip { + background-color: rgb(255, 255, 255); + border-color: rgb(128, 128, 128); + color: rgba(0, 0, 0, 0.6); +} + +/* +SearchBox control colors. +*/ + +.win-searchbox { + border-color:rgba(255, 255, 255, 0.8); + background-color:rgba(255, 255, 255, 0.8); + color:rgba(0, 0, 0, 0.6); +} + +.win-searchbox-button { + color:rgba(0, 0, 0, 0.6); +} + +.win-searchbox[disabled=false]:hover { + border-color:rgba(255, 255, 255, 0.87); + background-color:rgba(255, 255, 255, 0.87); +} + +.win-searchbox-input-focus { + border-color:rgb(42, 42, 42); + background-color:rgb(255, 255, 255); +} + +.win-searchbox-button[disabled=false]:hover { + background-color: rgb(95, 55, 190); +} + +.win-searchbox-button:active { + background-color:rgb(0, 0, 0); + color:rgb(255,255,255); +} + +.win-searchbox-input-focus:hover { + border-color:rgb(42, 42, 42); + background-color:rgb(255, 255, 255); +} + +.win-searchbox-button-input-focus { + background-color: rgb(70, 23, 180); + color: rgb(255, 255, 255); +} + +.win-searchbox-button-input-focus:hover { + background-color: rgb(95, 55, 190); +} + +.win-searchbox-flyout { + border-color:rgb(42, 42, 42); + background-color:rgb(255, 255, 255); +} + +.win-searchbox-suggestion-result:hover, +.win-searchbox-suggestion-query:hover { + background-color:rgb(229, 229, 229); +} + +.win-searchbox-suggestion-selected { + background-color: rgb(70, 23, 180); +} + +.win-searchbox-suggestion-query, +.win-searchbox-suggestion-result { + color: rgb(0, 0, 0); +} + +.win-searchbox-suggestion-separator { + color:rgb(122, 122, 122); +} + +.win-searchbox-suggestion-separator hr { + border-color:rgb(122, 122, 122); +} + +.win-searchbox-flyout-highlighttext { + color: rgb(70, 23, 180); +} + +.win-searchbox-disabled { + background-color:transparent; + border-color:rgba(255, 255, 255, 0.4); +} + +.win-searchbox-disabled input[disabled] { + background-color:transparent; + color:rgba(255, 255, 255, 0.4); +} + +.win-searchbox-disabled div { + background-color:transparent; + color:rgba(255, 255, 255, 0.4); +} + +.win-searchbox-suggestion-selected:hover { + background-color: rgb(95, 55, 190); +} + +.win-searchbox-suggestion-selected .win-searchbox-flyout-highlighttext { + color: rgb(163,139,218); +} + +.win-ui-light .win-searchbox { + border-color:rgba(0, 0, 0, 0.27); + background-color:rgba(255, 255, 255, 0.8); + color:rgba(0, 0, 0, 0.6); +} + +.win-ui-light .win-searchbox-button { + color:rgba(0, 0, 0, 0.6); +} + +.win-ui-light .win-searchbox[disabled=false]:hover { + background-color:rgba(255, 255, 255, 0.87); +} + +.win-ui-light .win-searchbox-input-focus { + border-color:rgb(42, 42, 42); +} + +.win-ui-light .win-searchbox-button[disabled=false]:hover { + background-color: rgb(95, 55, 190); +} + +.win-ui-light .win-searchbox-button:active { + background-color:rgb(0, 0, 0); + color:rgb(255,255,255); +} + +.win-ui-light .win-searchbox-button-input-focus { + background-color: rgb(70, 23, 180); + color: rgb(255, 255, 255); +} + +.win-ui-light .win-searchbox-button-input-focus:hover { + background-color: rgb(95, 55, 190); +} + +.win-ui-light .win-searchbox-flyout-highlighttext { + color: rgb(70, 23, 180); +} + +.win-ui-light .win-searchbox-flyout { + border-color:rgb(42, 42, 42); + background-color:rgb(255, 255, 255); +} + +.win-ui-light .win-searchbox-suggestion-result:hover, +.win-ui-light .win-searchbox-suggestion-query:hover { + background-color:rgb(229,229,229); +} + +.win-ui-light .win-searchbox-suggestion-selected { + background-color: rgb(70, 23, 180); +} + +.win-ui-light .win-searchbox-suggestion-query, +.win-ui-light .win-searchbox-suggestion-result { + color: rgb(0,0,0); +} + +.win-ui-light .win-searchbox-suggestion-separator { + color:rgb(122,122,122); +} + +.win-ui-light .win-searchbox-suggestion-separator hr { + border-color:rgb(122,122,122); +} + +.win-ui-light .win-searchbox-disabled { + background-color:rgba(202, 202, 202, 0.4); + border-color:rgba(0, 0, 0, 0.15); +} + +.win-ui-light .win-searchbox-disabled input[disabled] { + background-color:rgba(202, 202, 202, 0.4); + color: rgba(0, 0, 0, 0.22); +} + +.win-ui-light .win-searchbox-disabled div { + background-color:rgba(202, 202, 202, 0.4); + color: rgba(0, 0, 0, 0.22); +} + +.win-ui-light .win-searchbox-suggestion-selected:hover { + background-color: rgb(95, 55, 190); +} + +.win-ui-light .win-searchbox-suggestion-selected .win-searchbox-flyout-highlighttext { + color: rgb(163,139,218); +} + +/* +High contrast colors. +*/ +@media (-ms-high-contrast) +{ + /* + Typographic color definitions. + */ + .win-type-interactive { + color: WindowText; + } + .win-type-interactive:hover, .win-type-interactive:hover:active { + color: -ms-hotlight; + } + .win-type-interactive:-ms-keyboard-active { + color: -ms-hotlight; + } + + /* + Text selection high contrast color. + */ + ::selection, select:focus::-ms-value { + background-color: Highlight; + color: HighlightText; + } + + /* + Button high contrast colors. + */ + button.win-navigation-backbutton, button.win-navigation-backbutton:active, + button.win-navigation-backbutton:hover, button.win-navigation-backbutton:active:hover + { + /* Overwrite default background and border styles from BackButton control's ]]> + /// The BackButton control itself + /// The Back Arrow glyph + /// + /// + /// + BackButton: WinJS.Namespace._lazy(function () { + // Statics + var strings = { + get ariaLabel() { return WinJS.Resources._getWinJSString("ui/backbuttonarialabel").value; }, + get duplicateConstruction() { return WinJS.Resources._getWinJSString("ui/duplicateConstruction").value; }, + get badButtonElement() { return WinJS.Resources._getWinJSString("ui/badButtonElement").value; } + }; + + var BackButton = WinJS.Class.define(function BackButton_ctor(element, options) { + /// + /// + /// Creates a new BackButton control + /// + /// + /// The DOM element that will host the control. If this parameter is null, this constructor creates one for you. + /// + /// + /// An object that contains one or more property/value pairs to apply to the new control. Each property of the options object corresponds to + /// one of the control's properties or events. + /// + /// + /// A BackButton control. + /// + /// + /// + + // Check to make sure we weren't duplicated + if (element && element.winControl) { + throw new WinJS.ErrorFromName("WinJS.UI.BackButton.DuplicateConstruction", strings.duplicateConstruction); + } + + this._element = element || document.createElement("button"); + options = options || {}; + + this._initializeButton(); // This will also set the aria-label and tooltip + + this._disposed = false; + + // Remember ourselves + this._element.winControl = this; + + WinJS.UI.setOptions(this, options); + + // Add event handlers for this back button instance + this._buttonClickHandler = this._handleBackButtonClick.bind(this); + this._element.addEventListener('click', this._buttonClickHandler, false); + this._navigatedHandler = this._handleNavigatedEvent.bind(this); + nav.addEventListener('navigated', this._navigatedHandler, false); + + // Increment reference count / manage add global event handlers + singleton.addRef(); + }, { + + /// + element: { + get: function () { + return this._element; + } + }, + + dispose: function () { + /// + /// + /// Disposes this control. + /// + /// + /// + if (this._disposed) { + return; + } + this._disposed = true; // Mark this control as disposed. + + // Remove 'navigated' eventhandler for this BackButton + nav.removeEventListener('navigated', this._navigatedHandler, false); + + singleton.release(); // Decrement reference count. + + }, + + refresh: function () { + /// + /// + /// Sets the 'disabled' attribute to correct the value based on the current navigation history stack. + /// + /// + /// + if (nav.canGoBack) { + this._element.disabled = false; + } else { + this._element.disabled = true; + } + }, + + _initializeButton: function () { + //Final EN-US HTML should be: + // + //Button will automatically be disabled if WinJS.Navigation.history.canGoBack is false. + + // Verify the HTML is a button + if (this._element.tagName !== "BUTTON") { + throw new WinJS.ErrorFromName("WinJS.UI.BackButton.BadButtonElement", strings.badButtonElement); + } + + // Attach our css classes + WinJS.Utilities.addClass(this._element, navigationBackButtonClass); + + // Attach disposable class. + WinJS.Utilities.addClass(this._element, "win-disposable"); + + // Create inner glyph element + this._element.innerHTML = ''; + + // Set the 'disabled' property to the correct value based on the current navigation history stack. + this.refresh(); + + // Set Aria-label and native tooltip to the same localized string equivalent of "Back" + this._element.setAttribute("aria-label", strings.ariaLabel); + this._element.setAttribute("title", strings.ariaLabel); + + // Explicitly set type attribute to avoid the default '; + this._headerTabStopElement = this._headerElement.firstElementChild; + this._headerContentElement = this._headerTabStopElement.firstElementChild; + this._headerChevronElement = this._headerTabStopElement.lastElementChild; + element.appendChild(this._headerElement); + + this._winKeyboard = new WinJS.UI._WinKeyboard(this._headerElement); + + this._contentElement = document.createElement("DIV"); + this._contentElement.className = WinJS.UI.HubSection._ClassName.hubSectionContent; + this._contentElement.style.visibility = "hidden"; + element.appendChild(this._contentElement); + + // Reparent any existing elements inside the new hub section content element. + var elementToMove = this.element.firstChild; + while (elementToMove !== this._headerElement) { + var nextElement = elementToMove.nextSibling; + this._contentElement.appendChild(elementToMove); + elementToMove = nextElement; + } + + this._processors = [WinJS.UI.processAll]; + + WinJS.UI.setOptions(this, options); + }, { + /// + element: { + get: function () { + return this._element; + } + }, + /// + /// Gets or sets a value that specifies whether the header is static. Set this value to true to disable clicks and other interactions. + /// + /// + isHeaderStatic: { + get: function () { + return this._isHeaderStatic; + }, + set: function (value) { + this._isHeaderStatic = value; + if (!this._isHeaderStatic) { + this._headerTabStopElement.setAttribute("role", "link"); + WinJS.Utilities.addClass(this._headerTabStopElement, WinJS.UI.HubSection._ClassName.hubSectionInteractive); + } else { + this._headerTabStopElement.setAttribute("role", "heading"); + WinJS.Utilities.removeClass(this._headerTabStopElement, WinJS.UI.HubSection._ClassName.hubSectionInteractive); + } + } + }, + /// + /// Gets the DOM element that hosts the HubSection's content. + /// + /// + contentElement: { + get: function () { + return this._contentElement; + } + }, + /// + /// Get or set the HubSection's header. After you set this property, the Hub renders the header again. + /// + /// + header: { + get: function () { + return this._header; + }, + set: function (value) { + // Render again even if it is equal to itself. + this._header = value; + this._renderHeader(); + } + }, + _setHeaderTemplate: function HubSection_setHeaderTemplate(template) { + this._template = WinJS.Utilities._syncRenderer(template); + this._renderHeader(); + }, + _renderHeader: function HubSection_renderHeader() { + if (this._template) { + WinJS.Utilities._disposeElement(this._headerContentElement); + WinJS.Utilities.empty(this._headerContentElement); + this._template(this, this._headerContentElement); + } + }, + _process: function HubSection_process() { + var that = this; + + this._processed = (this._processors || []).reduce(function (promise, processor) { + return promise.then(function () { + return processor(that.contentElement); + }); + }, this._processed || WinJS.Promise.as()); + this._processors = null; + + return this._processed; + }, + dispose: function HubSection_dispose() { + /// + /// + /// Disposes this control. + /// + /// + /// + if (this._disposed) { + return; + } + this._disposed = true; + this._processors = null; + + WinJS.Utilities._disposeElement(this._headerContentElement); + WinJS.Utilities.disposeSubTree(this.contentElement); + } + }, { + // Names of classes used by the HubSection. + _ClassName: { + hubSection: "win-hub-section", + hubSectionHeader: "win-hub-section-header", + hubSectionHeaderTabStop: "win-hub-section-header-tabstop", + hubSectionInteractive: "win-hub-section-header-interactive", + hubSectionHeaderContent: "win-hub-section-header-content", + hubSectionHeaderChevron: "win-hub-section-header-chevron", + hubSectionContent: "win-hub-section-content" + }, + _Constants: { + ellipsisTypeClassName: "win-type-ellipsis", + xLargeTypeClassName: "win-type-x-large" + }, + isDeclarativeControlContainer: WinJS.Utilities.markSupportedForProcessing(function (section, callback) { + if (callback === WinJS.UI.processAll) { + return; + } + + section._processors = section._processors || []; + section._processors.push(callback); + + // Once processed the first time synchronously queue up new processors as they come in + if (section._processed) { + section._process(); + } + }) + }); + }) + }); + +})(this, WinJS); + +/// animatable,appbar,appbars,divs,Flyout,Flyouts,iframe,Statics,unfocus,unselectable +(function overlayInit(WinJS) { + "use strict"; + + WinJS.Namespace.define("WinJS.UI", { + _Overlay: WinJS.Namespace._lazy(function () { + var thisWinUI = WinJS.UI; + var utilities = WinJS.Utilities; + var createEvent = utilities._createEventProperty; + + // Class Names + var overlayClass = "win-overlay"; + var hideFocusClass = "win-hidefocus"; // Prevents the element from showing a focus rect + + // Event Names + var BEFORESHOW = "beforeshow"; + var AFTERSHOW = "aftershow"; + var BEFOREHIDE = "beforehide"; + var AFTERHIDE = "afterhide"; + + // Helper to get DOM elements from input single object or array or IDs/toolkit/dom elements + function _resolveElements(elements) { + // No input is just an empty array + if (!elements) { + return []; + } + + // Make sure it's in array form. + if (typeof elements === "string" || !elements || !elements.length) { + elements = [elements]; + } + + // Make sure we have a DOM element for each one, (could be string id name or toolkit object) + var i, + realElements = []; + for (i = 0; i < elements.length; i++) { + if (elements[i]) { + if (typeof elements[i] === "string") { + var element = document.getElementById(elements[i]); + if (element) { + realElements.push(element); + } + } else if (elements[i].element) { + realElements.push(elements[i].element); + } else { + realElements.push(elements[i]); + } + } + } + + return realElements; + } + + // Helpers for keyboard showing related events + function _allOverlaysCallback(event, command) { + var elements = document.querySelectorAll("." + overlayClass); + if (elements) { + var len = elements.length; + for (var i = 0; i < len; i++) { + var element = elements[i]; + var control = element.winControl; + if (!control._disposed) { + if (control) { + control[command](event); + } + } + } + } + } + + function _edgyMayHideFlyouts() { + if (!_Overlay._rightMouseMightEdgy) { + _Overlay._hideAllFlyouts(); + } + } + + var _Overlay = WinJS.Class.define(function _Overlay_ctor(element, options) { + /// + /// + /// Constructs the Overlay control and associates it with the underlying DOM element. + /// + /// + /// The DOM element to be associated with the Overlay control. + /// + /// + /// The set of options to be applied initially to the Overlay control. + /// + /// A fully constructed Overlay control. + /// + this._baseOverlayConstructor(element, options); + }, { + // Functions/properties + _baseOverlayConstructor: function _Overlay_baseOverlayConstructor(element, options) { + this._disposed = false; + + // Make sure there's an input element + if (!element) { + element = document.createElement("div"); + } + + // Check to make sure we weren't duplicated + var overlay = element.winControl; + if (overlay) { + throw new WinJS.ErrorFromName("WinJS.UI._Overlay.DuplicateConstruction", strings.duplicateConstruction); + } + + if (!this._element) { + this._element = element; + } + this._sticky = false; + this._doNext = ""; + + this._element.style.visibility = "hidden"; + this._element.style.opacity = 0; + + // Remember ourselves + element.winControl = this; + + // Attach our css class + WinJS.Utilities.addClass(this._element, overlayClass); + WinJS.Utilities.addClass(this._element, "win-disposable"); + + // We don't want to be selectable, set UNSELECTABLE + var unselectable = this._element.getAttribute("unselectable"); + if (unselectable === null || unselectable === undefined) { + this._element.setAttribute("unselectable", "on"); + } + + // Base animation is popIn/popOut + this._currentAnimateIn = this._baseAnimateIn; + this._currentAnimateOut = this._baseAnimateOut; + this._animationPromise = WinJS.Promise.as(); + + // Command Animations to Queue + this._queuedToShow = []; + this._queuedToHide = []; + this._queuedCommandAnimation = false; + + if (options) { + WinJS.UI.setOptions(this, options); + } + }, + + /// + element: { + get: function () { + return this._element; + } + }, + + /// Disable an Overlay, setting or getting the HTML disabled attribute. When disabled the Overlay will no longer display with show(), and will hide if currently visible. + disabled: { + get: function () { + // Ensure it's a boolean because we're using the DOM element to keep in-sync + return !!this._element.disabled; + }, + set: function (value) { + // Force this check into a boolean because our current state could be a bit confused since we tie to the DOM element + value = !!value; + var oldValue = !!this._element.disabled; + if (oldValue !== value) { + this._element.disabled = value; + if (!this.hidden && this._element.disabled) { + this._hideOrDismiss(); + } + } + } + }, + + /// + /// Occurs immediately before the control is shown. + /// + onbeforeshow: createEvent(BEFORESHOW), + + /// + /// Occurs immediately after the control is shown. + /// + onaftershow: createEvent(AFTERSHOW), + + /// + /// Occurs immediately before the control is hidden. + /// + onbeforehide: createEvent(BEFOREHIDE), + + /// + /// Occurs immediately after the control is hidden. + /// + onafterhide: createEvent(AFTERHIDE), + + dispose: function () { + /// + /// + /// Disposes this Overlay. + /// + /// + if (this._disposed) { + return; + } + + this._disposed = true; + this._dispose(); + }, + + _dispose: function _Overlay_dispose() { + // To be overridden by subclasses + }, + + show: function () { + /// + /// + /// Shows the Overlay, if hidden, regardless of other state + /// + /// + // call private show to distinguish it from public version + this._show(); + }, + + _show: function _Overlay_show() { + // We call our base _baseShow because AppBar may need to override show + this._baseShow(); + }, + + hide: function () { + /// + /// + /// Hides the Overlay, if visible, regardless of other state + /// + /// + // call private hide to distinguish it from public version + this._hide(); + }, + + _hide: function _Overlay_hide() { + // We call our base _baseHide because AppBar may need to override hide + this._baseHide(); + }, + + // Is the overlay "hidden"? + /// + hidden: { + get: function () { + return (this._element.style.visibility === "hidden" || + this._element.winAnimating === "hiding" || + this._doNext === "hide" || + this._fakeHide); + } + }, + + addEventListener: function (type, listener, useCapture) { + /// + /// + /// Add an event listener to the DOM element for this Overlay + /// + /// Required. Event type to add, "beforehide", "afterhide", "beforeshow", or "aftershow" + /// Required. The event handler function to associate with this event. + /// Optional. True, register for the event capturing phase. False for the event bubbling phase. + /// + return this._element.addEventListener(type, listener, useCapture); + }, + + removeEventListener: function (type, listener, useCapture) { + /// + /// + /// Remove an event listener to the DOM element for this Overlay + /// + /// Required. Event type to remove, "beforehide", "afterhide", "beforeshow", or "aftershow" + /// Required. The event handler function to associate with this event. + /// Optional. True, register for the event capturing phase. False for the event bubbling phase. + /// + return this._element.removeEventListener(type, listener, useCapture); + }, + + _baseShow: function _Overlay_baseShow() { + // If we are already animating, just remember this for later + if (this._animating || this._keyboardShowing || this._keyboardHiding) { + this._doNext = "show"; + return false; + } + + // Each overlay tracks the window width for detecting resizes in the resize handler. + this._currentDocumentWidth = this._currentDocumentWidth || document.documentElement.offsetWidth; + + // "hiding" would need to cancel. + if (this._element.style.visibility !== "visible" || this._fakeHide) { + // Let us know we're showing. + this._element.winAnimating = "showing"; + + // Hiding, but not none + this._element.style.display = ""; + if (!this._fakeHide) { + this._element.style.visibility = "hidden"; + } + + // In case their event is going to manipulate commands, see if there are + // any queued command animations we can handle while we're still hidden. + if (this._queuedCommandAnimation) { + this._showAndHideFast(this._queuedToShow, this._queuedToHide); + this._queuedToShow = []; + this._queuedToHide = []; + } + + // Send our "beforeShow" event + this._sendEvent(_Overlay.beforeShow); + + // Need to measure + this._findPosition(); + + // Make sure it's visible, and fully opaque. + // Do the popup thing, sending event afterward. + var that = this; + this._animationPromise = this._currentAnimateIn(). + then(function () { + that._baseEndShow(); + }, function (err) { + that._baseEndShow(); + }); + this._fakeHide = false; + return true; + } + return false; + }, + + // Flyout in particular will need to measure our positioning. + _findPosition: function _Overlay_findPosition() { + }, + + _baseEndShow: function _Overlay_baseEndShow() { + if (this._disposed) { + return; + } + + // Make sure it's visible after showing + this._element.setAttribute("aria-hidden", "false"); + + this._element.winAnimating = ""; + + // Do our derived classes show stuff + this._endShow(); + + // We're shown now + if (this._doNext === "show") { + this._doNext = ""; + } + + // After showing, send the after showing event + this._sendEvent(_Overlay.afterShow); + this._writeProfilerMark("show,StopTM"); // Overlay writes the stop profiler mark for all of its derived classes. + + // If we had something queued, do that + WinJS.Utilities.Scheduler.schedule(this._checkDoNext, WinJS.Utilities.Scheduler.Priority.normal, this, "WinJS.UI._Overlay._checkDoNext"); + + }, + + _endShow: function _Overlay_endShow() { + // Nothing by default + }, + + _baseHide: function _Overlay_baseHide() { + // If we are already animating, just remember this for later + if (this._animating || this._keyboardShowing) { + this._doNext = "hide"; + return false; + } + + // In the unlikely event we're between the hiding keyboard and the resize events, just snap it away: + if (this._keyboardHiding) { + // use the "uninitialized" flag + this._element.style.visibility = ""; + } + + // "showing" would need to queue up. + if (this._element.style.visibility !== "hidden") { + // Let us know we're hiding, accessibility as well. + this._element.winAnimating = "hiding"; + this._element.setAttribute("aria-hidden", "true"); + + // Send our "beforeHide" event + this._sendEvent(_Overlay.beforeHide); + + // If our visibility is empty, then this is the first time, just hide it + if (this._element.style.visibility === "") { + // Initial hiding, just hide it + this._element.style.opacity = 0; + this._baseEndHide(); + } else { + // Make sure it's hidden, and fully transparent. + var that = this; + this._animationPromise = this._currentAnimateOut(). + then(function () { + that._baseEndHide(); + }, function (err) { + that._baseEndHide(); + }); + } + return true; + } + this._fakeHide = false; + + return false; + }, + + _baseEndHide: function _Overlay_baseEndHide() { + if (this._disposed) { + return; + } + + // Make sure animation is finished. + this._element.style.visibility = "hidden"; + this._element.style.display = "none"; + this._element.winAnimating = ""; + + // In case their event is going to manipulate commands, see if there + // are any queued command animations we can handle now we're hidden. + if (this._queuedCommandAnimation) { + this._showAndHideFast(this._queuedToShow, this._queuedToHide); + this._queuedToShow = []; + this._queuedToHide = []; + } + + // We're hidden now + if (this._doNext === "hide") { + this._doNext = ""; + } + + // After hiding, send our "afterHide" event + this._sendEvent(_Overlay.afterHide); + this._writeProfilerMark("hide,StopTM"); // Overlay writes the stop profiler mark for all of its derived classes. + + + // If we had something queued, do that. This has to be after + // the afterHide event in case it triggers a show() and they + // have something to do in beforeShow that requires afterHide first. + WinJS.Utilities.Scheduler.schedule(this._checkDoNext, WinJS.Utilities.Scheduler.Priority.normal, this, "WinJS.UI._Overlay._checkDoNext"); + }, + + _checkDoNext: function _Overlay_checkDoNext() { + // Do nothing if we're still animating + if (this._animating || this._keyboardShowing || this._keyboardHiding || this._disposed) { + return; + } + + if (this._doNext === "hide") { + // Do hide first because animating commands would be easier + this._hide(); + this._doNext = ""; + } else if (this._queuedCommandAnimation) { + // Do queued commands before showing if possible + this._showAndHideQueue(); + } else if (this._doNext === "show") { + // Show last so that we don't unnecessarily animate commands + this._show(); + this._doNext = ""; + } + }, + + // Default animations + _baseAnimateIn: function _Overlay_baseAnimateIn() { + this._element.style.opacity = 0; + this._element.style.visibility = "visible"; + // touch opacity so that IE fades from the 0 we just set to 1 + window.getComputedStyle(this._element, null).opacity; + return WinJS.UI.Animation.fadeIn(this._element); + }, + + _baseAnimateOut: function _Overlay_baseAnimateOut() { + this._element.style.opacity = 1; + // touch opacity so that IE fades from the 1 we just set to 0 + window.getComputedStyle(this._element, null).opacity; + return WinJS.UI.Animation.fadeOut(this._element); + }, + + _animating: { + get: function _Overlay_animating_get() { + // Ensure it's a boolean because we're using the DOM element to keep in-sync + return !!this._element.winAnimating; + } + }, + + // Send one of our events + _sendEvent: function _Overlay_sendEvent(eventName, detail) { + if (this._disposed) { + return; + } + var event = document.createEvent("CustomEvent"); + event.initEvent(eventName, true, true, (detail || {})); + this._element.dispatchEvent(event); + }, + + // Show commands + _showCommands: function _Overlay_showCommands(commands, immediate) { + var showHide = this._resolveCommands(commands); + this._showAndHideCommands(showHide.commands, [], immediate); + }, + + // Hide commands + _hideCommands: function _Overlay_hideCommands(commands, immediate) { + var showHide = this._resolveCommands(commands); + this._showAndHideCommands([], showHide.commands, immediate); + }, + + // Hide commands + _showOnlyCommands: function _Overlay_showOnlyCommands(commands, immediate) { + var showHide = this._resolveCommands(commands); + this._showAndHideCommands(showHide.commands, showHide.others, immediate); + }, + + _showAndHideCommands: function _Overlay_showAndHideCommands(showCommands, hideCommands, immediate) { + // Immediate is "easy" + if (immediate || (this.hidden && !this._animating)) { + // Immediate mode (not animated) + this._showAndHideFast(showCommands, hideCommands); + // Need to remove them from queues, but others could be queued + this._removeFromQueue(showCommands, this._queuedToShow); + this._removeFromQueue(hideCommands, this._queuedToHide); + } else { + + // Queue Commands + this._updateAnimateQueue(showCommands, this._queuedToShow, this._queuedToHide); + this._updateAnimateQueue(hideCommands, this._queuedToHide, this._queuedToShow); + } + }, + + _removeFromQueue: function _Overlay_removeFromQueue(commands, queue) { + // remove commands from queue. + var count; + for (count = 0; count < commands.length; count++) { + // Remove if it was in queue + var countQ; + for (countQ = 0; countQ < queue.length; countQ++) { + if (queue[countQ] === commands[count]) { + queue.splice(countQ, 1); + break; + } + } + } + }, + + _updateAnimateQueue: function _Overlay_updateAnimateQueue(addCommands, toQueue, fromQueue) { + if (this._disposed) { + return; + } + + // Add addCommands to toQueue and remove addCommands from fromQueue. + var count; + for (count = 0; count < addCommands.length; count++) { + // See if it's already in toQueue + var countQ; + for (countQ = 0; countQ < toQueue.length; countQ++) { + if (toQueue[countQ] === addCommands[count]) { + break; + } + } + if (countQ === toQueue.length) { + // Not found, add it + toQueue[countQ] = addCommands[count]; + } + // Remove if it was in fromQueue + for (countQ = 0; countQ < fromQueue.length; countQ++) { + if (fromQueue[countQ] === addCommands[count]) { + fromQueue.splice(countQ, 1); + break; + } + } + } + // If we haven't queued the actual animation + if (!this._queuedCommandAnimation) { + // If not already animating, we'll need to call _checkDoNext + if (!this._animating) { + WinJS.Utilities.Scheduler.schedule(this._checkDoNext, WinJS.Utilities.Scheduler.Priority.normal, this, "WinJS.UI._Overlay._checkDoNext"); + } + this._queuedCommandAnimation = true; + } + }, + + // show/hide commands without doing any animation. + _showAndHideFast: function _Overlay_showAndHideFast(showCommands, hideCommands) { + var count; + var command; + for (count = 0; count < showCommands.length; count++) { + command = showCommands[count]; + if (command && command.style) { + command.style.visibility = ""; + command.style.display = ""; + } + } + for (count = 0; count < hideCommands.length; count++) { + command = hideCommands[count]; + if (command && command.style) { + command.style.visibility = "hidden"; + command.style.display = "none"; + } + } + + this._contentChanged(); + + }, + + // show and hide the queued commands, perhaps animating if overlay isn't hidden. + _showAndHideQueue: function _Overlay_showAndHideQueue() { + // Only called if not currently animating. + // We'll be done with the queued stuff when we return. + this._queuedCommandAnimation = false; + + // Shortcut if hidden + if (this.hidden) { + this._showAndHideFast(this._queuedToShow, this._queuedToHide); + // Might be something else to do + WinJS.Utilities.Scheduler.schedule(this._checkDoNext, WinJS.Utilities.Scheduler.Priority.normal, this, "WinJS.UI._Overlay._checkDoNext"); + } else { + // Animation has 3 parts: "hiding", "showing", and "moving" + // PVL has "addToList" and "deleteFromList", both of which allow moving parts. + // So we'll set up "add" for showing, and use "delete" for "hiding" + moving, + // then trigger both at the same time. + var showCommands = this._queuedToShow; + var hideCommands = this._queuedToHide; + var siblings = this._findSiblings(showCommands.concat(hideCommands)); + + // Filter out the commands queued for animation that don't need to be animated. + var count; + for (count = 0; count < showCommands.length; count++) { + // If this one's not real or not attached, skip it + if (!showCommands[count] || + !showCommands[count].style || + !document.body.contains(showCommands[count])) { + // Not real, skip it + showCommands.splice(count, 1); + count--; + } else if (showCommands[count].style.visibility !== "hidden" && showCommands[count].style.opacity !== "0") { + // Don't need to animate showing this one, already visible, so now it's a sibling + siblings.push(showCommands[count]); + showCommands.splice(count, 1); + count--; + } + } + for (count = 0; count < hideCommands.length; count++) { + // If this one's not real or not attached, skip it + if (!hideCommands[count] || + !hideCommands[count].style || + !document.body.contains(hideCommands[count]) || + hideCommands[count].style.visibility === "hidden" || + hideCommands[count].style.opacity === "0") { + // Don't need to animate hiding this one, not real, or it's hidden, + // so don't even need it as a sibling. + hideCommands.splice(count, 1); + count--; + } + } + + // Start command animations. + var commandsAnimationPromise = this._baseBeginAnimateCommands(showCommands, hideCommands, siblings); + + // Hook end animations + var that = this; + if (commandsAnimationPromise) { + // Needed to animate + commandsAnimationPromise.done( + function () { that._baseEndAnimateCommands(hideCommands); }, + function () { that._baseEndAnimateCommands(hideCommands); } + ); + } else { + // Already positioned correctly + WinJS.Utilities.Scheduler.schedule(function () { that._baseEndAnimateCommands([]); }, + WinJS.Utilities.Scheduler.Priority.normal, null, + "WinJS.UI._Overlay._endAnimateCommandsWithoutAnimation"); + } + } + + // Done, clear queues + this._queuedToShow = []; + this._queuedToHide = []; + }, + + _baseBeginAnimateCommands: function _Overlay_baseBeginAnimateCommands(showCommands, hideCommands, siblings) { + // The parameters are 3 mutually exclusive arrays of win-command elements contained in this Overlay. + // 1) showCommands[]: All of the HIDDEN win-command elements that ARE scheduled to show. + // 2) hideCommands[]: All of the VISIBLE win-command elements that ARE shceduled to hide. + // 3) siblings[]: i. All VISIBLE win-command elements that ARE NOT scheduled to hide. + // ii. All HIDDEN win-command elements that ARE NOT scheduled to hide OR show. + this._beginAnimateCommands(showCommands, hideCommands, this._getVisibleCommands(siblings)); + + var showAnimated = null, + hideAnimated = null; + + // Hide commands first, with siblings if necessary, + // so that the showing commands don't disrupt the hiding commands position. + if (hideCommands.length > 0) { + hideAnimated = WinJS.UI.Animation.createDeleteFromListAnimation(hideCommands, showCommands.length === 0 ? siblings : undefined); + } + if (showCommands.length > 0) { + showAnimated = WinJS.UI.Animation.createAddToListAnimation(showCommands, siblings); + } + + // Update hiding commands + for (var count = 0, len = hideCommands.length; count < len; count++) { + // Need to fix our position + var rectangle = hideCommands[count].getBoundingClientRect(), + style = window.getComputedStyle(hideCommands[count]); + + // Use the bounding box, adjusting for margins + hideCommands[count].style.top = (rectangle.top - parseFloat(style.marginTop)) + "px"; + hideCommands[count].style.left = (rectangle.left - parseFloat(style.marginLeft)) + "px"; + hideCommands[count].style.opacity = 0; + hideCommands[count].style.position = "fixed"; + } + + // Mark as animating + this._element.winAnimating = "rearranging"; + + // Start hiding animations + // Hide needs extra cleanup when done + var promise = null; + if (hideAnimated) { + promise = hideAnimated.execute(); + } + + // Update showing commands, + // After hiding commands so that the hiding ones fade in the right place. + for (count = 0; count < showCommands.length; count++) { + showCommands[count].style.visibility = ""; + showCommands[count].style.display = ""; + showCommands[count].style.opacity = 1; + } + + // Start showing animations + if (showAnimated) { + var newPromise = showAnimated.execute(); + if (promise) { + promise = WinJS.Promise.join([promise, newPromise]); + } else { + promise = newPromise; + } + } + + return promise; + }, + + _beginAnimateCommands: function _Overlay_beginAnimateCommands(showCommands, hideCommands, siblings) { + // Nothing by default + }, + + _getVisibleCommands: function _Overlay_getVisibleCommands(commandSubSet) { + var command, + commands = commandSubSet, + visibleCommands = []; + + if (!commands) { + // Crawl the inner HTML for the commands. + commands = this.element.querySelectorAll(".win-command"); + } + + for (var i = 0, len = commands.length; i < len; i++) { + command = commands[i].winControl || commands[i]; + if (!command.hidden) { + visibleCommands.push(command); + } + } + + return visibleCommands; + }, + + // Once animation is complete, ensure that the commands are display:none + // and check if there's another animation to start. + _baseEndAnimateCommands: function _Overlay_baseEndAnimateCommands(hideCommands) { + if (this._disposed) { + return; + } + + // Update us + var count; + for (count = 0; count < hideCommands.length; count++) { + // Force us back into our appbar so that we can show again correctly + hideCommands[count].style.position = ""; + hideCommands[count].getBoundingClientRect(); + // Now make us really hidden + hideCommands[count].style.visibility = "hidden"; + hideCommands[count].style.display = "none"; + hideCommands[count].style.opacity = 1; + } + // Done animating + this._element.winAnimating = ""; + + this._endAnimateCommands(); + + // Might be something else to do + this._checkDoNext(); + }, + + _endAnimateCommands: function _Overlay_endAnimateCommands() { + // Nothing by default + }, + + // Resolves our commands + _resolveCommands: function _Overlay_resolveCommands(commands) { + // First make sure they're all DOM elements. + commands = _resolveElements(commands); + + // Now make sure they're all in this container + var result = {}; + result.commands = []; + result.others = []; + var allCommands = this.element.querySelectorAll(".win-command"); + var countAll, countIn; + for (countAll = 0; countAll < allCommands.length; countAll++) { + var found = false; + for (countIn = 0; countIn < commands.length; countIn++) { + if (commands[countIn] === allCommands[countAll]) { + result.commands.push(allCommands[countAll]); + commands.splice(countIn, 1); + found = true; + break; + } + } + if (!found) { + result.others.push(allCommands[countAll]); + } + } + return result; + }, + + // Find siblings, all DOM elements now. + // Returns all .win-commands in this Overlay that are NOT in the passed in 'commands' array. + _findSiblings: function _Overlay_findSiblings(commands) { + // Now make sure they're all in this container + var siblings = []; + var allCommands = this.element.querySelectorAll(".win-command"); + var countAll, countIn; + for (countAll = 0; countAll < allCommands.length; countAll++) { + var found = false; + for (countIn = 0; countIn < commands.length; countIn++) { + if (commands[countIn] === allCommands[countAll]) { + commands.splice(countIn, 1); + found = true; + break; + } + } + if (!found) { + siblings.push(allCommands[countAll]); + } + } + return siblings; + }, + + _baseResize: function _Overlay_baseResize(event) { + // Avoid the cost of a resize if the Overlay is hidden. + if (this._currentDocumentWidth !== undefined) { + if (this._element.style.visibility === "hidden") { + this._currentDocumentWidth = undefined; + } else { + // Overlays can light dismiss on horizontal resize. + var newWidth = document.documentElement.offsetWidth; + if (this._currentDocumentWidth !== newWidth) { + this._currentDocumentWidth = newWidth + if (!this._sticky) { + this._hideOrDismiss(); + } + } + } + } + + // Call specific resize + this._resize(event); + }, + + _hideOrDismiss: function _Overlay_hideOrDismiss() { + var element = this._element; + if (element && WinJS.Utilities.hasClass(element, "win-settingsflyout")) { + this._dismiss(); + } else { + this.hide(); + } + }, + + _resize: function _Overlay_resize(event) { + // Nothing by default + }, + + _contentChanged: function _Overlay_contentChanged(needToReMeasure) { + // Nothing by default + }, + + _checkScrollPosition: function _Overlay_checkScrollPosition(event) { + // Nothing by default + }, + + _showingKeyboard: function _Overlay_showingKeyboard(event) { + // Nothing by default + }, + + _hidingKeyboard: function _Overlay_hidingKeyboard(event) { + // Nothing by default + }, + + // Verify that this HTML AppBar only has AppBar/MenuCommands. + _verifyCommandsOnly: function _Overlay_verifyCommandsOnly(element, type) { + var children = element.children; + var commands = new Array(children.length); + for (var i = 0; i < children.length; i++) { + // If constructed they have win-command class, otherwise they have data-win-control + if (!WinJS.Utilities.hasClass(children[i], "win-command") && + children[i].getAttribute("data-win-control") !== type) { + // Wasn't tagged with class or AppBar/MenuCommand, not an AppBar/MenuCommand + throw new WinJS.ErrorFromName("WinJS.UI._Overlay.MustContainCommands", strings.mustContainCommands); + } else { + // Instantiate the commands. + WinJS.UI.processAll(children[i]); + commands[i] = children[i].winControl; + } + } + return commands; + }, + + // Sets focus on what we think is the last tab stop. If nothing is focusable will + // try to set focus on itself. + _focusOnLastFocusableElementOrThis: function _Overlay_focusOnLastFocusableElementOrThis() { + if (!this._focusOnLastFocusableElement()) { + // Nothing is focusable. Set focus to this. + _Overlay._trySetActive(this._element); + } + }, + + // Sets focus to what we think is the last tab stop. This element must have + // a firstDiv with tabIndex equal to the lowest tabIndex in the element + // and a finalDiv with tabIndex equal to the highest tabIndex in the element. + // Also the firstDiv must be its first child and finalDiv be its last child. + // Returns true if successful, false otherwise. + _focusOnLastFocusableElement: function _Overlay_focusOnLastFocusableElement() { + if (this._element.firstElementChild) { + var oldFirstTabIndex = this._element.firstElementChild.tabIndex; + var oldLastTabIndex = this._element.lastElementChild.tabIndex; + this._element.firstElementChild.tabIndex = -1; + this._element.lastElementChild.tabIndex = -1; + + var tabResult = WinJS.Utilities._focusLastFocusableElement(this._element); + + if (tabResult) { + _Overlay._trySelect(document.activeElement); + } + + this._element.firstElementChild.tabIndex = oldFirstTabIndex; + this._element.lastElementChild.tabIndex = oldLastTabIndex; + + return tabResult; + } else { + return false; + } + }, + + + // Sets focus on what we think is the first tab stop. If nothing is focusable will + // try to set focus on itself. + _focusOnFirstFocusableElementOrThis: function _Overlay_focusOnFirstFocusableElementOrThis() { + if (!this._focusOnFirstFocusableElement()) { + // Nothing is focusable. Set focus to this. + _Overlay._trySetActive(this._element); + } + }, + + // Sets focus to what we think is the first tab stop. This element must have + // a firstDiv with tabIndex equal to the lowest tabIndex in the element + // and a finalDiv with tabIndex equal to the highest tabIndex in the element. + // Also the firstDiv must be its first child and finalDiv be its last child. + // Returns true if successful, false otherwise. + _focusOnFirstFocusableElement: function _Overlay__focusOnFirstFocusableElement() { + if (this._element.firstElementChild) { + var oldFirstTabIndex = this._element.firstElementChild.tabIndex; + var oldLastTabIndex = this._element.lastElementChild.tabIndex; + this._element.firstElementChild.tabIndex = -1; + this._element.lastElementChild.tabIndex = -1; + + var tabResult = WinJS.Utilities._focusFirstFocusableElement(this._element); + + if (tabResult) { + _Overlay._trySelect(document.activeElement); + } + + this._element.firstElementChild.tabIndex = oldFirstTabIndex; + this._element.lastElementChild.tabIndex = oldLastTabIndex; + + return tabResult; + } else { + return false; + } + }, + + _addOverlayEventHandlers: function _Overlay_addOverlayEventHandlers(isFlyoutOrSettingsFlyout) { + // Set up global event handlers for all overlays + if (!_Overlay._flyoutEdgeLightDismissEvent) { + // Dismiss on blur & resize + window.addEventListener("blur", _Overlay._checkBlur, false); + + var that = this; + + // Be careful so it behaves in designer as well. + if (WinJS.Utilities.hasWinRT) { + // Catch edgy events too + var commandUI = Windows.UI.Input.EdgeGesture.getForCurrentView(); + commandUI.addEventListener("starting", _Overlay._hideAllFlyouts); + commandUI.addEventListener("completed", _edgyMayHideFlyouts); + + // React to Soft Keyboard events + var inputPane = Windows.UI.ViewManagement.InputPane.getForCurrentView(); + inputPane.addEventListener("showing", function (event) { + that._writeProfilerMark("_showingKeyboard,StartTM"); + _allOverlaysCallback(event, "_showingKeyboard"); + that._writeProfilerMark("_showingKeyboard,StopTM"); + }); + inputPane.addEventListener("hiding", function (event) { + that._writeProfilerMark("_hidingKeyboard,StartTM"); + _allOverlaysCallback(event, "_hidingKeyboard"); + that._writeProfilerMark("_hidingKeyboard,StopTM"); + }); + // Document scroll event + document.addEventListener("scroll", function (event) { + that._writeProfilerMark("_checkScrollPosition,StartTM"); + _allOverlaysCallback(event, "_checkScrollPosition"); + that._writeProfilerMark("_checkScrollPosition,StopTM"); + }); + } + // Window resize event + window.addEventListener("resize", function (event) { + that._writeProfilerMark("_baseResize,StartTM"); + _allOverlaysCallback(event, "_baseResize"); + that._writeProfilerMark("_baseResize,StopTM"); + }); + + _Overlay._flyoutEdgeLightDismissEvent = true; + } + + // Individual handlers for Flyouts only + if (isFlyoutOrSettingsFlyout) { + this._handleEventsForFlyoutOrSettingsFlyout(); + } + }, + + _handleEventsForFlyoutOrSettingsFlyout: function _Overlay_handleEventsForFlyoutOrSettingsFlyout() { + var that = this; + // Need to hide ourselves if we lose focus + this._element.addEventListener("focusout", function (e) { _Overlay._hideIfLostFocus(that, e); }, false); + + // Attempt to flag right clicks that may turn into edgy + this._element.addEventListener("pointerdown", _Overlay._checkRightClickDown, true); + this._element.addEventListener("pointerup", _Overlay._checkRightClickUp, true); + }, + + _writeProfilerMark: function _Overlay_writeProfilerMark(text) { + msWriteProfilerMark("WinJS.UI._Overlay:" + this._id + ":" + text); + } + }); + + // Statics + _Overlay._clickEatingAppBarDiv = false; + _Overlay._clickEatingFlyoutDiv = false; + _Overlay._flyoutEdgeLightDismissEvent = false; + + _Overlay._hideFlyouts = function (testElement, notSticky) { + var elements = testElement.querySelectorAll(".win-flyout"); + var len = elements.length; + for (var i = 0; i < len; i++) { + var element = elements[i]; + if (element.style.visibility !== "hidden") { + var flyout = element.winControl; + if (flyout && (!notSticky || !flyout._sticky)) { + flyout._hideOrDismiss(); + } + } + } + }; + + _Overlay._hideSettingsFlyouts = function (testElement, notSticky) { + var elements = testElement.querySelectorAll(".win-settingsflyout"); + var len = elements.length; + for (var i = 0; i < len; i++) { + var element = elements[i]; + if (element.style.visibility !== "hidden") { + var settingsFlyout = element.winControl; + if (settingsFlyout && (!notSticky || !settingsFlyout._sticky)) { + settingsFlyout._hideOrDismiss(); + } + } + } + }; + + _Overlay._hideAllFlyouts = function () { + _Overlay._hideFlyouts(document, true); + _Overlay._hideSettingsFlyouts(document, true) + }; + + _Overlay._createClickEatingDivTemplate = function (divClass, hideClickEatingDivFunction) { + var clickEatingDiv = document.createElement("section"); + WinJS.Utilities.addClass(clickEatingDiv, divClass); + clickEatingDiv.addEventListener("pointerup", function (event) { _Overlay._checkSameClickEatingPointerUp(event, true); }, true); + clickEatingDiv.addEventListener("pointerdown", function (event) { _Overlay._checkClickEatingPointerDown(event, true); }, true); + clickEatingDiv.addEventListener("click", hideClickEatingDivFunction, true); + // Tell Aria that it's clickable + clickEatingDiv.setAttribute("role", "menuitem"); + clickEatingDiv.setAttribute("aria-label", strings.closeOverlay); + // Prevent CED from removing any current selection + clickEatingDiv.setAttribute("unselectable", "on"); + document.body.appendChild(clickEatingDiv); + return clickEatingDiv; + }; + + // Used by AppBar, and Settings Pane + _Overlay._createClickEatingDivAppBar = function () { + if (!_Overlay._clickEatingAppBarDiv) { + _Overlay._clickEatingAppBarDiv = _Overlay._createClickEatingDivTemplate(_Overlay._clickEatingAppBarClass, _Overlay._handleAppBarClickEatingClick); + } + }; + + // Used by Flyout and Menu + _Overlay._createClickEatingDivFlyout = function () { + if (!_Overlay._clickEatingFlyoutDiv) { + _Overlay._clickEatingFlyoutDiv = _Overlay._createClickEatingDivTemplate(_Overlay._clickEatingFlyoutClass, _Overlay._handleFlyoutClickEatingClick); + } + }; + + // All click-eaters eat "down" clicks so that we can still eat + // the "up" click that'll come later. + _Overlay._checkClickEatingPointerDown = function (event, stopPropogation) { + var target = event.currentTarget; + if (target) { + try { + // Remember pointer id and remember right mouse + target._winPointerId = event.pointerId; + // Cache right mouse if that was what happened + target._winRightMouse = (event.button === 2); + } catch (e) { } + } + + if (stopPropogation && !target._winRightMouse) { + event.stopPropagation(); + event.preventDefault(); + } + }; + + // Make sure that if we have an up we had an earlier down of the same kind + _Overlay._checkSameClickEatingPointerUp = function (event, stopPropogation) { + var result = false, + rightMouse = false, + target = event.currentTarget; + + // Same pointer we were watching? + try { + if (target && target._winPointerId === event.pointerId) { + // Same pointer + result = true; + rightMouse = target._winRightMouse; + // For click-eaters, don't count right click the same because edgy will dismiss + if (rightMouse && stopPropogation) { + result = false; + } + } + } catch (e) { } + + + if (stopPropogation && !rightMouse) { + event.stopPropagation(); + event.preventDefault(); + } + + return result; + }; + + // If they click on a click eating div, even with a right click, + // touch or anything, then we want to light dismiss that layer. + _Overlay._handleAppBarClickEatingClick = function (event) { + event.stopPropagation(); + event.preventDefault(); + + thisWinUI.AppBar._hideLightDismissAppBars(null, false); + _Overlay._hideClickEatingDivAppBar(); + _Overlay._hideAllFlyouts(); + }; + + // If they click on a click eating div, even with a right click, + // touch or anything, then we want to light dismiss that layer. + _Overlay._handleFlyoutClickEatingClick = function (event) { + event.stopPropagation(); + event.preventDefault(); + + // Don't light dismiss AppBars because edgy will do that as needed, + // so flyouts only. + _Overlay._hideClickEatingDivFlyout(); + _Overlay._hideFlyouts(document, true); + }; + + _Overlay._checkRightClickDown = function (event) { + _Overlay._checkClickEatingPointerDown(event, false); + }; + + _Overlay._checkRightClickUp = function (event) { + if (_Overlay._checkSameClickEatingPointerUp(event, false)) { + // It was a right click we may want to eat. + _Overlay._rightMouseMightEdgy = true; + setImmediate(function () { _Overlay._rightMouseMightEdgy = false; }); + } + }; + + _Overlay._showClickEatingDivAppBar = function () { + WinJS.Utilities.Scheduler.schedule(function () { + _Overlay._clickEatingAppBarDiv.style.display = "block"; + }, WinJS.Utilities.Scheduler.Priority.high, null, "WinJS.UI._Overlay._showClickEatingDivAppBar"); + }; + + _Overlay._hideClickEatingDivAppBar = function () { + WinJS.Utilities.Scheduler.schedule(function () { + _Overlay._clickEatingAppBarDiv.style.display = "none"; + }, WinJS.Utilities.Scheduler.Priority.high, null, "WinJS.UI._Overlay._hideClickEatingDivAppBar"); + }; + + _Overlay._showClickEatingDivFlyout = function () { + WinJS.Utilities.Scheduler.schedule(function () { + _Overlay._clickEatingFlyoutDiv.style.display = "block"; + }, WinJS.Utilities.Scheduler.Priority.high, null, "WinJS.UI._Overlay._showClickEatingDivFlyout"); + }; + + _Overlay._hideClickEatingDivFlyout = function () { + WinJS.Utilities.Scheduler.schedule(function () { + _Overlay._clickEatingFlyoutDiv.style.display = "none"; + }, WinJS.Utilities.Scheduler.Priority.high, null, "WinJS.UI._Overlay._hideClickEatingDivFlyout"); + }; + + _Overlay._isFlyoutVisible = function () { + if (!_Overlay._clickEatingFlyoutDiv) { + return false; + } + return (_Overlay._clickEatingFlyoutDiv.style.display === "block"); + }; + + _Overlay._hideIfLostFocus = function (overlay, focusEvent) { + // If we're still showing we haven't really lost focus + if (overlay.hidden || overlay.element.winAnimating === "showing" || overlay._sticky) { + return; + } + // If the active thing is within our element, we haven't lost focus + var active = document.activeElement; + if (overlay._element && overlay._element.contains(active)) { + return; + } + // SettingFlyouts don't dismiss if they spawned a flyout + if (WinJS.Utilities.hasClass(overlay._element, "win-settingsflyout")) { + var settingsFlyout = overlay; + var flyoutControl = _Overlay._getParentControlUsingClassName(active, "win-flyout"); + if (flyoutControl && flyoutControl._previousFocus && settingsFlyout.element.contains(flyoutControl._previousFocus)) { + flyoutControl.element.addEventListener('focusout', function focusOut(event) { + // When the Flyout closes, hide the SetingsFlyout if it didn't regain focus. + _Overlay._hideIfLostFocus(settingsFlyout, event); + flyoutControl.element.removeEventListener('focusout', focusOut, false); + }, false); + return; + } + } + // Do not hide focus if focus moved to a CED. Let the click handler on the CED take care of hiding us. + if (active && + (WinJS.Utilities.hasClass(active, _Overlay._clickEatingFlyoutClass) || + WinJS.Utilities.hasClass(active, _Overlay._clickEatingAppBarClass))) { + return; + } + + overlay._hideOrDismiss(); + }; + + // Want to hide flyouts on blur. + // We get blur if we click off the window, including to an iframe within our window. + // Both blurs call this function, but fortunately document.hasFocus is true if either + // the document window or our iframe window has focus. + _Overlay._checkBlur = function (focusEvent) { + if (!document.hasFocus()) { + // The document doesn't have focus, so they clicked off the app, so light dismiss. + _Overlay._hideAllFlyouts(); + thisWinUI.AppBar._hideLightDismissAppBars(null, false); + } else { + if ((_Overlay._clickEatingFlyoutDiv && + _Overlay._clickEatingFlyoutDiv.style.display === "block") || + (_Overlay._clickEatingAppBarDiv && + _Overlay._clickEatingAppBarDiv.style.display === "block")) { + // We were trying to unfocus the window, but document still has focus, + // so make sure the iframe that took the focus will check for blur next time. + // We don't have to do this if the click eating div is hidden because then + // there would be no flyout or appbar needing light dismiss. + var active = document.activeElement; + if (active && active.tagName === "IFRAME" && !active.msLightDismissBlur) { + // This will go away when the IFRAME goes away, and we only create one + active.msLightDismissBlur = active.addEventListener("blur", _Overlay._checkBlur, false); + } + } + } + }; + + // Try to set us as active + _Overlay._trySetActive = function (element) { + if (!element || !element.setActive || !document.body || !document.body.contains(element)) { + return false; + } + try { + element.setActive(); + } catch (err) { + return false; + } + return (element === document.activeElement); + }; + + // Try to select the text so keyboard can be used. + _Overlay._trySelect = function (element) { + try { + if (element && element.select) { + element.select(); + } + } catch (e) { } + }; + + // Prevent the document.activeElement from showing focus + _Overlay._addHideFocusClass = function (element) { + if (element) { + WinJS.Utilities.addClass(element, hideFocusClass); + element.addEventListener("focusout", _Overlay._removeHideFocusClass, false); + } + }; + + // Allow the event.target (element that is losing focus) to show focus next time it gains focus + _Overlay._removeHideFocusClass = function (event) { + // Make sure we really lost focus and was not just an App switch + var target = event.target; + if (target && target !== document.activeElement) { + WinJS.Utilities.removeClass(target, hideFocusClass); + event.target.removeEventListener("focusout", _Overlay._removeHideFocusClass, false); + } + }; + + _Overlay._getParentControlUsingClassName = function (element, className) { + while (element && element !== document.body) { + if (WinJS.Utilities.hasClass(element, className)) { + return element.winControl; + } + element = element.parentNode; + } + return null; + }; + + // Global keyboard hiding offset + _Overlay._keyboardInfo = { + // Determine if the keyboard is visible or not. + get _visible() { + try { + return (WinJS.Utilities.hasWinRT && Windows.UI.ViewManagement.InputPane.getForCurrentView().occludedRect.height > 0); + } catch (e) { + return false; + } + }, + + // See if we have to reserve extra space for the IHM + get _extraOccluded() { + var occluded; + if (WinJS.Utilities.hasWinRT) { + try { + occluded = Windows.UI.ViewManagement.InputPane.getForCurrentView().occludedRect.height; + } catch (e) { + } + } + + // Nothing occluded if not visible. + if (occluded && !_Overlay._keyboardInfo._isResized) { + // View hasn't been resized, need to return occluded height. + return occluded; + } + + // View already has space for keyboard or there's no keyboard + return 0; + }, + + // See if the view has been resized to fit a keyboard + get _isResized() { + // Compare ratios. Very different includes IHM space. + var heightRatio = document.documentElement.clientHeight / window.innerHeight, + widthRatio = document.documentElement.clientWidth / window.innerWidth; + + // If they're nearly identical, then the view hasn't been resized for the IHM + // Only check one bound because we know the IHM will make it shorter, not skinnier. + return (widthRatio / heightRatio < 0.99); + }, + + // Get the top of our visible area in terms of document.documentElement. + get _visibleDocTop() { + return window.pageYOffset - document.documentElement.scrollTop; + }, + + // Get the bottom of our visible area. + get _visibleDocBottom() { + return _Overlay._keyboardInfo._visibleDocTop + _Overlay._keyboardInfo._visibleDocHeight; + }, + + // Get the height of the visible document, e.g. the height of the visual viewport minus any IHM occlusion. + get _visibleDocHeight() { + return _Overlay._keyboardInfo._visualViewportHeight -_Overlay._keyboardInfo._extraOccluded; + }, + + // Get the visual viewport height. window.innerHeight doesn't return floating point values which are present with high DPI. + get _visualViewportHeight() { + var boundingRect = _Overlay._keyboardInfo._visualViewportSpace; + return boundingRect.bottom - boundingRect.top; + }, + + // Get the visual viewport width. window.innerHeight doesn't return floating point values which are present with high DPI. + get _visualViewportWidth() { + var boundingRect = _Overlay._keyboardInfo._visualViewportSpace; + return boundingRect.right - boundingRect.left; + }, + + get _visualViewportSpace() { + var className = "win-visualviewport-space" + var visualViewportSpace = document.body.querySelector("." + className); + if (!visualViewportSpace) { + visualViewportSpace = document.createElement("DIV"); + visualViewportSpace.className = className; + document.body.appendChild(visualViewportSpace); + } + + return visualViewportSpace.getBoundingClientRect(); + }, + + // Get offset of visible window from bottom. + get _visibleDocBottomOffset() { + // If the view resizes we can return 0 and rely on appbar's -ms-device-fixed css positioning. + return (_Overlay._keyboardInfo._isResized) ? 0 : _Overlay._keyboardInfo._extraOccluded; + }, + + // Get total length of the IHM showPanel animation + get _animationShowLength() { + if (!WinJS.Utilities.hasWinRT) { + return 0; + } + + var a = Windows.UI.Core.AnimationMetrics, + animationDescription = new a.AnimationDescription(a.AnimationEffect.showPanel, a.AnimationEffectTarget.primary); + var animations = animationDescription.animations; + var max = 0; + for (var i = 0; i < animations.size; i++) { + var animation = animations[i]; + max = Math.max(max, animation.delay + animation.duration); + } + return max; + } + }; + + // Classes other objects use + _Overlay._clickEatingAppBarClass = "win-appbarclickeater"; + _Overlay._clickEatingFlyoutClass = "win-flyoutmenuclickeater"; + + // Padding for IHM timer to allow for first scroll event + _Overlay._scrollTimeout = 150; + + // Events + _Overlay.beforeShow = BEFORESHOW; + _Overlay.beforeHide = BEFOREHIDE; + _Overlay.afterShow = AFTERSHOW; + _Overlay.afterHide = AFTERHIDE; + + _Overlay.commonstrings = { + get cannotChangeCommandsWhenVisible() { return WinJS.Resources._getWinJSString("ui/cannotChangeCommandsWhenVisible").value; }, + get cannotChangeHiddenProperty() { return WinJS.Resources._getWinJSString("ui/cannotChangeHiddenProperty").value; } + }; + + var strings = { + get duplicateConstruction() { return WinJS.Resources._getWinJSString("ui/duplicateConstruction").value; }, + get mustContainCommands() { return WinJS.Resources._getWinJSString("ui/mustContainCommands").value; }, + get closeOverlay() { return WinJS.Resources._getWinJSString("ui/closeOverlay").value; }, + }; + + WinJS.Class.mix(_Overlay, WinJS.UI.DOMEventMixin); + + return _Overlay; + }) + }); + +})(WinJS); + + +// Glyph Enumeration +/// Segoe +(function appBarIconInit(WinJS) { + "use strict"; + + var glyphs = ["previous", + "next", + "play", + "pause", + "edit", + "save", + "clear", + "delete", + "remove", + "add", + "cancel", + "accept", + "more", + "redo", + "undo", + "home", + "up", + "forward", + "right", + "back", + "left", + "favorite", + "camera", + "settings", + "video", + "sync", + "download", + "mail", + "find", + "help", + "upload", + "emoji", + "twopage", + "leavechat", + "mailforward", + "clock", + "send", + "crop", + "rotatecamera", + "people", + "closepane", + "openpane", + "world", + "flag", + "previewlink", + "globe", + "trim", + "attachcamera", + "zoomin", + "bookmarks", + "document", + "protecteddocument", + "page", + "bullets", + "comment", + "mail2", + "contactinfo", + "hangup", + "viewall", + "mappin", + "phone", + "videochat", + "switch", + "contact", + "rename", + "pin", + "musicinfo", + "go", + "keyboard", + "dockleft", + "dockright", + "dockbottom", + "remote", + "refresh", + "rotate", + "shuffle", + "list", + "shop", + "selectall", + "orientation", + "import", + "importall", + "browsephotos", + "webcam", + "pictures", + "savelocal", + "caption", + "stop", + "showresults", + "volume", + "repair", + "message", + "page2", + "calendarday", + "calendarweek", + "calendar", + "characters", + "mailreplyall", + "read", + "link", + "accounts", + "showbcc", + "hidebcc", + "cut", + "attach", + "paste", + "filter", + "copy", + "emoji2", + "important", + "mailreply", + "slideshow", + "sort", + "manage", + "allapps", + "disconnectdrive", + "mapdrive", + "newwindow", + "openwith", + "contactpresence", + "priority", + "uploadskydrive", + "gototoday", + "font", + "fontcolor", + "contact2", + "folder", + "audio", + "placeholder", + "view", + "setlockscreen", + "settile", + "cc", + "stopslideshow", + "permissions", + "highlight", + "disableupdates", + "unfavorite", + "unpin", + "openlocal", + "mute", + "italic", + "underline", + "bold", + "movetofolder", + "likedislike", + "dislike", + "like", + "alignright", + "aligncenter", + "alignleft", + "zoom", + "zoomout", + "openfile", + "otheruser", + "admin", + "street", + "map", + "clearselection", + "fontdecrease", + "fontincrease", + "fontsize", + "cellphone", + "reshare", + "tag", + "repeatone", + "repeatall", + "outlinestar", + "solidstar", + "calculator", + "directions", + "target", + "library", + "phonebook", + "memo", + "microphone", + "postupdate", + "backtowindow", + "fullscreen", + "newfolder", + "calendarreply", + "unsyncfolder", + "reporthacked", + "syncfolder", + "blockcontact", + "switchapps", + "addfriend", + "touchpointer", + "gotostart", + "zerobars", + "onebar", + "twobars", + "threebars", + "fourbars", + "scan", + "preview"]; + + // Provide properties to grab resources for each of the icons + /// + /// The AppBarIcon enumeration provides a set of glyphs for use with the AppBarCommand icon property. + /// + WinJS.Namespace.define("WinJS.UI.AppBarIcon", + glyphs.reduce(function (fixedIcons, item) { + fixedIcons[item] = { get: function () { return WinJS.Resources._getWinJSString("ui/appBarIcons/" + item).value; } }; + return fixedIcons; + }, {})); +})(WinJS); +// AppBarCommand +/// appbar,appbars,Flyout,Flyouts,onclick,Statics +(function appBarCommandInit(WinJS) { + "use strict"; + + WinJS.Namespace.define("WinJS.UI", { + /// + /// Represents a command to display in an AppBar. + /// + /// + /// + /// ]]> + /// The AppBarCommand control itself. + /// The AppBarCommand's icon box. + /// The AppBarCommand's icon's image formatting. + /// The AppBarCommand's icon's ring. + /// The AppBarCommand's label + /// + /// + /// + AppBarCommand: WinJS.Namespace._lazy(function () { + var thisWinUI = WinJS.UI; + + // Class Names + var appBarCommandClass = "win-command", + appBarCommandGlobalClass = "win-global", + appBarCommandSelectionClass = "win-selection", + reducedClass = "win-reduced", + typeSeparator = "separator", + typeButton = "button", + typeToggle = "toggle", + typeFlyout = "flyout", + typeContent = "content", + sectionSelection = "selection", + sectionGlobal = "global"; + + function _handleClick(event) { + var command = this.winControl; + if (command) { + if (command._type === typeToggle) { + command.selected = !command.selected; + } else if (command._type === typeFlyout && command._flyout) { + var parentAppBar = thisWinUI._Overlay._getParentControlUsingClassName(this, "win-appbar"); + var placement = "top"; + if (parentAppBar && parentAppBar.placement === "top") { + placement = "bottom"; + } + var flyout = command._flyout; + // Flyout may not have processAll'd, so this may be a DOM object + if (typeof flyout === "string") { + flyout = document.getElementById(flyout); + } + if (!flyout.show) { + flyout = flyout.winControl; + } + if (flyout && flyout.show) { + flyout.show(this, placement); + } + } + if (command.onclick) { + command.onclick(event); + } + } + } + + var strings = { + get ariaLabel() { return WinJS.Resources._getWinJSString("ui/appBarCommandAriaLabel").value; }, + get duplicateConstruction() { return WinJS.Resources._getWinJSString("ui/duplicateConstruction").value; }, + get badClick() { return WinJS.Resources._getWinJSString("ui/badClick").value; }, + get badDivElement() { return WinJS.Resources._getWinJSString("ui/badDivElement").value; }, + get badHrElement() { return WinJS.Resources._getWinJSString("ui/badHrElement").value; }, + get badButtonElement() { return WinJS.Resources._getWinJSString("ui/badButtonElement").value; } + }; + + return WinJS.Class.define(function AppBarCommand_ctor(element, options) { + /// + /// + /// Creates a new AppBarCommand control. + /// + /// + /// The DOM element that will host the control. AppBarCommand will create one if null. + /// + /// + /// The set of properties and values to apply to the new AppBarCommand. + /// + /// + /// The new AppBarCommand control. + /// + /// + + // Check to make sure we weren't duplicated + if (element && element.winControl) { + throw new WinJS.ErrorFromName("WinJS.UI.AppBarCommand.DuplicateConstruction", strings.duplicateConstruction); + } + + this._disposed = false; + + // Don't blow up if they didn't pass options + if (!options) { + options = {}; + } + + // Need a type before we can create our element + if (!options.type) { + this._type = typeButton; + } + + // Go ahead and create it, separator and content types look different than buttons + // Don't forget to use passed in element if one was provided. + this._element = element; + + if (options.type === typeContent) { + this._createContent(); + } + else if (options.type === typeSeparator) { + this._createSeparator(); + } else { + // This will also set the icon & label + this._createButton(); + } + WinJS.Utilities.addClass(this._element, "win-disposable"); + + // Remember ourselves + this._element.winControl = this; + + // Attach our css class + WinJS.Utilities.addClass(this._element, appBarCommandClass); + + if (options.onclick) { + this.onclick = options.onclick; + } + // We want to handle some clicks + options.onclick = _handleClick; + + WinJS.UI.setOptions(this, options); + + if (!options.section) { + this._setSection(options.section); + } + + if (this._type === typeToggle && !options.selected) { + this.selected = false; + } + + // Set up pointerdown handler and clean up ARIA if needed + if (this._type !== typeSeparator) { + + // Hide the modern focus rect on click or touch + var that = this; + this._element.addEventListener("pointerdown", function () { thisWinUI._Overlay._addHideFocusClass(that._element); }, false); + + // Make sure we have an ARIA role + var role = this._element.getAttribute("role"); + if (role === null || role === "" || role === undefined) { + if (this._type === typeToggle) { + role = "menuitemcheckbox"; + } else if (this._type === typeContent) { + role = "group"; + } else { + role = "menuitem"; + } + this._element.setAttribute("role", role); + if (this._type === typeFlyout) { + this._element.setAttribute("aria-haspopup", true); + } + } + // Label should've been set by label, but if it was missed for some reason: + var label = this._element.getAttribute("aria-label"); + if (label === null || label === "" || label === undefined) { + this._element.setAttribute("aria-label", strings.ariaLabel); + } + } + }, { + /// + /// Gets or sets the ID of the AppBarCommand. + /// + id: { + get: function () { + return this._element.id; + }, + + set: function (value) { + // we allow setting first time only. otherwise we ignore it. + if (value && !this._element.id) { + this._element.id = value; + } + } + }, + + /// + /// Gets or sets the type of the AppBarCommand. Possible values are "button", "toggle", "flyout", or "separator". + /// + type: { + get: function () { + return this._type; + }, + set: function (value) { + // we allow setting first time only. otherwise we ignore it. + if (!this._type) { + if (value !== typeContent && value !== typeFlyout && value !== typeToggle && value !== typeSeparator) { + this._type = typeButton; + } else { + this._type = value; + } + } + } + }, + + /// + /// Gets or sets the label of the AppBarCommand. + /// + label: { + get: function () { + return this._label; + }, + set: function (value) { + this._label = value; + if (this._labelSpan) { + this._labelSpan.innerText = this.label; + } + + // Ensure that we have a tooltip, by updating already-constructed tooltips. Separators won't have these: + if (!this.tooltip && this._tooltipControl) { + this._tooltip = this.label; + this._tooltipControl.innerHTML = this.label; + } + + // Update aria-label + this._element.setAttribute("aria-label", this.label); + + // Check if we need to suppress the tooltip + this._testIdenticalTooltip(); + } + }, + + /// + /// Gets or sets the icon of the AppBarCommand. + /// + icon: { + get: function () { + return this._icon; + }, + set: function (value) { + if (value) { + this._icon = (WinJS.UI.AppBarIcon[value] || value); + } else { + this._icon = value; + } + + if (this._imageSpan) { + // If the icon's a single character, presume a glyph + if (this._icon && this._icon.length === 1) { + // Set the glyph + this._imageSpan.innerText = this._icon; + this._imageSpan.style.backgroundImage = ""; + this._imageSpan.style.msHighContrastAdjust = ""; + } else { + // Must be an image, set that + this._imageSpan.innerText = ""; + this._imageSpan.style.backgroundImage = this._icon; + this._imageSpan.style.msHighContrastAdjust = "none"; + } + } + } + }, + + /// + /// Gets or sets the function to invoke when the command is clicked. + /// + onclick: { + get: function () { + return this._onclick; + }, + set: function (value) { + if (value && typeof value !== "function") { + throw new WinJS.ErrorFromName("WinJS.UI.AppBarCommand.BadClick", WinJS.Resources._formatString(strings.badClick, "AppBarCommand")); + } + this._onclick = value; + } + }, + + /// + /// For flyout-type AppBarCommands, this property returns the WinJS.UI.Flyout that this command invokes. + /// When setting this property, you may also use the String ID of the flyout to invoke, the DOM object + /// for the flyout, or the WinJS.UI.Flayout object itself. + /// + flyout: { + get: function () { + // Resolve it to the flyout + var flyout = this._flyout; + if (typeof flyout === "string") { + flyout = document.getElementById(flyout); + } + // If it doesn't have a .element, then we need to getControl on it + if (flyout && !flyout.element) { + flyout = flyout.winControl; + } + + return flyout; + }, + set: function (value) { + // Need to update aria-owns with the new ID. + var id = value; + if (id && typeof id !== "string") { + // Our controls have .element properties + if (id.element) { + id = id.element; + } + // Hope it's a DOM element, get ID from DOM element + if (id) { + if (id.id) { + id = id.id; + } else { + // No id, have to fake one + id.id = id.uniqueID; + id = id.id; + } + } + } + if (typeof id === "string") { + this._element.setAttribute("aria-owns", id); + } + + // Remember it + this._flyout = value; + } + }, + + /// + /// Gets or sets the section that the AppBarCommand is in. Possible values are "selection" and "global". + /// + section: { + get: function () { + return this._section; + }, + set: function (value) { + // we allow settings section only one time + if (!this._section || (window.Windows && Windows.ApplicationModel && Windows.ApplicationModel.DesignMode && Windows.ApplicationModel.DesignMode.designModeEnabled)) { + this._setSection(value); + } + } + }, + + /// Gets or sets the tooltip text of the AppBarCommand. + tooltip: { + get: function () { + return this._tooltip; + }, + set: function (value) { + this._tooltip = value; + + // Update already-constructed tooltips. Separators and content commands won't have these: + if (this._tooltipControl) { + this._tooltipControl.innerHTML = this._tooltip; + } + + // Check if we need to suppress the tooltip + this._testIdenticalTooltip(); + } + }, + + /// Set or get the selected state of a toggle button. + selected: { + get: function () { + // Ensure it's a boolean because we're using the DOM element to keep in-sync + return this._element.getAttribute("aria-checked") === "true"; + }, + set: function (value) { + this._element.setAttribute("aria-checked", value); + } + }, + + /// + element: { + get: function () { + return this._element; + } + }, + + /// + /// Gets or sets a value that indicates whether the AppBarCommand is disabled. A value of true disables the AppBarCommand, and a value of false enables it. + /// + disabled: { + get: function () { + // Ensure it's a boolean because we're using the DOM element to keep in-sync + return !!this._element.disabled; + }, + set: function (value) { + this._element.disabled = value; + } + }, + + /// + hidden: { + get: function () { + // Ensure it's a boolean because we're using the DOM element to keep in-sync + return this._element.style.visibility === "hidden"; + }, + set: function (value) { + var appbarControl = thisWinUI._Overlay._getParentControlUsingClassName(this._element, "win-appbar"); + if (appbarControl && !appbarControl.hidden) { + throw new WinJS.ErrorFromName("WinJS.UI.AppBarCommand.CannotChangeHiddenProperty", WinJS.Resources._formatString(thisWinUI._Overlay.commonstrings.cannotChangeHiddenProperty, "AppBar")); + } + + if (value === this.hidden) { + // No changes to make. + return; + } + + var style = this._element.style; + + if (value) { + style.visibility = "hidden"; + style.display = "none"; + } else { + style.visibility = ""; + style.display = "inline-block"; + } + if (appbarControl) { + appbarControl._contentChanged(); + } + } + }, + + /// + /// Gets or sets the HTMLElement within a "content" type AppBarCommand that should receive focus whenever focus moves via Home or the arrow keys, + /// from the previous AppBarCommand to the this AppBarCommand. Returns the AppBarCommand object's host element by default. + /// + firstElementFocus: { + get: function () { + return this._firstElementFocus || this._lastElementFocus || this._element; + }, + set: function (element) { + // Arguments of null and this.element should treated the same to ensure that this.element is never a tabstop when either focus property has been set. + this._firstElementFocus = (element === this.element) ? null : element; + this._updateTabStop(); + } + }, + + /// + /// Gets or sets the HTMLElement within a "content" type AppBarCommand that should receive focus whenever focus would move, via End or arrow keys, + /// from the next AppBarCommand to this AppBarCommand. Returns this AppBarCommand object's host element by default. + /// + lastElementFocus: { + get: function () { + return this._lastElementFocus || this._firstElementFocus || this._element; + }, + set: function (element) { + // Arguments of null and this.element should treated the same to ensure that this.element is never a tabstop when either focus property has been set. + this._lastElementFocus = (element === this.element) ? null : element; + this._updateTabStop(); + } + }, + + dispose: function () { + /// + /// + /// Disposes this control. + /// + /// + if (this._disposed) { + return; + } + this._disposed = true; + + if (this._tooltipControl) { + this._tooltipControl.dispose(); + } + + if (this._type === typeContent) { + WinJS.Utilities.disposeSubTree(this.element); + } + }, + + addEventListener: function (type, listener, useCapture) { + /// + /// + /// Registers an event handler for the specified event. + /// + /// + /// Required. The name of the event to register. It must be "beforeshow", "beforehide", "aftershow", or "afterhide". + /// + /// Required. The event handler function to associate with this event. + /// + /// Optional. Set to true to register the event handler for the capturing phase; otherwise, set to false to register the event handler for the bubbling phase. + /// + /// + return this._element.addEventListener(type, listener, useCapture); + }, + + removeEventListener: function (type, listener, useCapture) { + /// + /// + /// Removes an event handler that the addEventListener method registered. + /// + /// Required. The name of the event to remove. + /// Required. The event handler function to remove. + /// + /// Optional. Set to true to remove the capturing phase event handler; otherwise, set to false to remove the bubbling phase event handler. + /// + /// + return this._element.removeEventListener(type, listener, useCapture); + }, + + /// Adds an extra CSS class during construction. + extraClass: { + get: function () { + return this._extraClass; + }, + set: function (value) { + if (this._extraClass) { + WinJS.Utilities.removeClass(this._element, this._extraClass); + } + this._extraClass = value; + WinJS.Utilities.addClass(this._element, this._extraClass); + } + }, + + // Private + _testIdenticalTooltip: function AppBarCommand_testIdenticalToolTip() { + this._hideIfFullSize = (this._label === this._tooltip); + }, + + _createContent: function AppBarCommand_createContent() { + // Make sure there's an element + if (!this._element) { + this._element = document.createElement("div"); + } else { + // Verify the element was a div + if (this._element.tagName !== "DIV") { + throw new WinJS.ErrorFromName("WinJS.UI.AppBarCommand.BadDivElement", strings.badDivElement); + } + } + + // If a tabIndex isnt set, default to 0; + if (parseInt(this._element.getAttribute("tabIndex"), 10) !== this._element.tabIndex) { + this._element.tabIndex = 0; + } + }, + + _createSeparator: function AppBarCommand_createSeparator() { + // Make sure there's an element + if (!this._element) { + this._element = document.createElement("hr"); + } else { + // Verify the element was an hr + if (this._element.tagName !== "HR") { + throw new WinJS.ErrorFromName("WinJS.UI.AppBarCommand.BadHrElement", strings.badHrElement); + } + } + }, + + _createButton: function AppBarCommand_createButton() { + // Make sure there's an element + if (!this._element) { + this._element = document.createElement("button"); + } else { + // Verify the element was a button + if (this._element.tagName !== "BUTTON") { + throw new WinJS.ErrorFromName("WinJS.UI.AppBarCommand.BadButtonElement", strings.badButtonElement); + } + // Make sure it has a type="button" + var type = this._element.getAttribute("type"); + if (type === null || type === "" || type === undefined) { + this._element.setAttribute("type", "button"); + } + this._element.innerHTML = ""; + } + + // AppBarCommand buttons need to look like this: + //// + this._element.type = "button"; + this._iconSpan = document.createElement("span"); + this._iconSpan.setAttribute("aria-hidden", "true"); + this._iconSpan.className = "win-commandicon win-commandring"; + this._iconSpan.tabIndex = -1; + this._element.appendChild(this._iconSpan); + this._imageSpan = document.createElement("span"); + this._imageSpan.setAttribute("aria-hidden", "true"); + this._imageSpan.className = "win-commandimage"; + this._imageSpan.tabIndex = -1; + this._iconSpan.appendChild(this._imageSpan); + this._labelSpan = document.createElement("span"); + this._labelSpan.setAttribute("aria-hidden", "true"); + this._labelSpan.className = "win-label"; + this._labelSpan.tabIndex = -1; + this._element.appendChild(this._labelSpan); + // 'win-global' or 'win-selection' are added later by caller. + // Label and icon are added later by caller. + + // Attach a tooltip - Note: we're going to stomp on it's setControl so we don't have to make another DOM element to hang it off of. + // This private _tooltipControl attribute is used by other pieces, changing the name could break them. + this._tooltipControl = new WinJS.UI.Tooltip(this._element); + var that = this; + this._tooltipControl.addEventListener("beforeopen", function () { + if (that._hideIfFullSize && !thisWinUI._Overlay._getParentControlUsingClassName(that._element.parentElement, reducedClass)) { + that._tooltipControl.close(); + } + }, false); + }, + + _setSection: function AppBarCommand_setSection(section) { + if (!section) { + section = sectionGlobal; + } + if (this._section) { + // Remove the old section class + if (this._section === sectionGlobal) { + WinJS.Utilities.removeClass(this._element, appBarCommandGlobalClass); + } else if (this.section === sectionSelection) { + WinJS.Utilities.removeClass(this._element, appBarCommandSelectionClass); + } + } + // Add the new section class + this._section = section; + if (section === sectionGlobal) { + WinJS.Utilities.addClass(this._element, appBarCommandGlobalClass); + } else if (section === sectionSelection) { + WinJS.Utilities.addClass(this._element, appBarCommandSelectionClass); + } + }, + + _updateTabStop: function AppBarCommand_updateTabStop() { + // Whenever the firstElementFocus or lastElementFocus properties are set for content type AppBarCommands, + // the containing command element is no longer a tabstop. + + if (this._firstElementFocus || this._lastElementFocus ) { + this.element.tabIndex = -1; + } else { + this.element.tabIndex = 0; + } + }, + + _isFocusable: function AppBarCommand_isFocusable() { + return (!this.hidden && this._type !== typeSeparator && !this.element.disabled && + (this.firstElementFocus.tabIndex >= 0 || this.lastElementFocus.tabIndex >= 0)) + }, + }); + }) + }); + +})(WinJS); + +// AppBar +/// appbar,appBars,Flyout,Flyouts,iframe,Statics,unfocus,WinJS +(function appBarInit(WinJS) { + "use strict"; + + WinJS.Namespace.define("WinJS.UI", { + /// + /// Represents an application toolbar for display commands. + /// + /// + /// + /// + /// + /// ]]> + /// Raised just before showing the AppBar. + /// Raised immediately after the AppBar is fully shown. + /// Raised just before hiding the AppBar. + /// Raised immediately after the AppBar is fully hidden. + /// The AppBar control itself. + /// Style for a custom layout AppBar. + /// + /// + /// + AppBar: WinJS.Namespace._lazy(function () { + var thisWinUI = WinJS.UI; + + // Class Names + var commandClass = "win-commandlayout", + appBarClass = "win-appbar", + reducedClass = "win-reduced", + settingsFlyoutClass = "win-settingsflyout", + topClass = "win-top", + bottomClass = "win-bottom", + appBarCommandClass = "win-command"; + + var firstDivClass = "win-firstdiv", + finalDivClass = "win-finaldiv"; + + // Constants for placement + var appBarPlacementTop = "top", + appBarPlacementBottom = "bottom"; + + // Constants for layout + var appBarLayoutCustom = "custom", + appBarLayoutCommands = "commands"; + + // Constants for AppBarCommands + var typeSeparator = "separator", + typeContent = "content", + separatorWidth = 60, + buttonWidth = 100; + + // Hook into event + var appBarCommandEvent = false; + var edgyHappening = null; + + // Handler for the edgy starting/completed/cancelled events + function _completedEdgy(e) { + // If we had a right click on a flyout, ignore it. + if (thisWinUI._Overlay._rightMouseMightEdgy && + e.kind === Windows.UI.Input.EdgeGestureKind.mouse) { + return; + } + if (edgyHappening) { + // Edgy was happening, just skip it + edgyHappening = null; + } else { + // Edgy wasn't happening, so toggle + var keyboardInvoked = e.kind === Windows.UI.Input.EdgeGestureKind.keyboard; + WinJS.UI.AppBar._toggleAppBarEdgy(keyboardInvoked); + } + } + + function _startingEdgy() { + if (!edgyHappening) { + // Edgy wasn't happening, so toggle & start it + edgyHappening = WinJS.UI.AppBar._toggleAppBarEdgy(false); + } + } + + function _canceledEdgy() { + // Shouldn't get here unless edgy was happening. + // Undo whatever we were doing. + var bars = _getDynamicBarsForEdgy(); + if (edgyHappening === "showing") { + _hideAllBars(bars, false); + } else if (edgyHappening === "hiding") { + _showAllBars(bars, false); + } + edgyHappening = null; + } + + function _allManipulationChanged(event) { + var elements = document.querySelectorAll("." + appBarClass); + if (elements) { + var len = elements.length; + for (var i = 0; i < len; i++) { + var element = elements[i]; + var appbar = element.winControl; + if (appbar && !element.disabled) { + appbar._manipulationChanged(event); + } + } + } + } + + // Get all the non-sticky bars and return them. + // Returns array of AppBar objects. + // The array also has _hidden and/or _visible set if ANY are hidden or visible. + function _getDynamicBarsForEdgy() { + var elements = document.querySelectorAll("." + appBarClass); + var len = elements.length; + var AppBars = []; + AppBars._visible = false; + AppBars._hidden = false; + for (var i = 0; i < len; i++) { + var element = elements[i]; + if (element.disabled) { + // Skip disabled AppBars + continue; + } + var AppBar = element.winControl; + if (AppBar) { + AppBars.push(AppBar); + // Middle of animation is different than animated + if (AppBar._element.winAnimating) { + // If animating, look at showing/hiding + if (AppBar._element.winAnimating === "hiding") { + AppBars._hidden = true; + } else { + AppBars._visible = true; + } + } else { + // Not animating, so check visibility + if (AppBar._element.style.visibility === "hidden") { + AppBars._hidden = true; + } else { + AppBars._visible = true; + } + } + } + } + + return AppBars; + } + + // Show or hide all bars + function _hideAllBars(bars, keyboardInvoked) { + var len = bars.length; + var allBarsAnimationPromises = new Array(len); + for (var i = 0; i < len; i++) { + bars[i]._keyboardInvoked = keyboardInvoked; + bars[i].hide(); + allBarsAnimationPromises[i] = bars[i]._animationPromise; + } + return WinJS.Promise.join(allBarsAnimationPromises); + } + + function _showAllBars(bars, keyboardInvoked) { + var len = bars.length; + var allBarsAnimationPromises = new Array(len); + for (var i = 0; i < len; i++) { + bars[i]._keyboardInvoked = keyboardInvoked; + bars[i]._doNotFocus = false; + bars[i]._show(); + allBarsAnimationPromises[i] = bars[i]._animationPromise; + } + return WinJS.Promise.join(allBarsAnimationPromises); + } + + // Sets focus to the last AppBar in the provided appBars array with given placement. + // Returns true if focus was set. False otherwise. + function _setFocusToPreviousAppBarHelper(startIndex, appBarPlacement, appBars) { + for (var i = startIndex; i >= 0; i--) { + if (appBars[i].winControl + && appBars[i].winControl.placement === appBarPlacement + && !appBars[i].winControl.hidden + && appBars[i].winControl._focusOnLastFocusableElement + && appBars[i].winControl._focusOnLastFocusableElement()) { + return true; + } + } + return false; + } + + // Sets focus to the last AppBar in the provided appBars array with other placement. + // Returns true if focus was set. False otherwise. + function _setFocusToPreviousAppBarHelperNeither(startIndex, appBars) { + for (var i = startIndex; i >= 0; i--) { + if (appBars[i].winControl + && appBars[i].winControl.placement != appBarPlacementBottom + && appBars[i].winControl.placement != appBarPlacementTop + && !appBars[i].winControl.hidden + && appBars[i].winControl._focusOnLastFocusableElement + && appBars[i].winControl._focusOnLastFocusableElement()) { + return true; + } + } + return false; + } + + // Sets focus to the last tab stop of the previous AppBar + // AppBar tabbing order: + // 1) Bottom AppBars + // 2) Top AppBars + // 3) Other AppBars + // DOM order is respected, because an AppBar should not have a defined tabIndex + function _setFocusToPreviousAppBar() { + var appBars = document.querySelectorAll("." + appBarClass); + if (!appBars.length) { + return; + } + + var thisAppBarIndex = 0; + for (var i = 0; i < appBars.length; i++) { + if (appBars[i] === this.parentElement) { + thisAppBarIndex = i; + break; + } + } + + var appBarControl = this.parentElement.winControl; + if (appBarControl.placement === appBarPlacementBottom) { + // Bottom appBar: Focus order: (1)previous bottom appBars (2)other appBars (3)top appBars (4)bottom appBars + if (thisAppBarIndex && _setFocusToPreviousAppBarHelper(thisAppBarIndex - 1, appBarPlacementBottom, appBars)) { return; } + if (_setFocusToPreviousAppBarHelperNeither(appBars.length - 1, appBars)) { return; } + if (_setFocusToPreviousAppBarHelper(appBars.length - 1, appBarPlacementTop, appBars)) { return; } + if (_setFocusToPreviousAppBarHelper(appBars.length - 1, appBarPlacementBottom, appBars)) { return; } + } else if (appBarControl.placement === appBarPlacementTop) { + // Top appBar: Focus order: (1)previous top appBars (2)bottom appBars (3)other appBars (4)top appBars + if (thisAppBarIndex && _setFocusToPreviousAppBarHelper(thisAppBarIndex - 1, appBarPlacementTop, appBars)) { return; } + if (_setFocusToPreviousAppBarHelper(appBars.length - 1, appBarPlacementBottom, appBars)) { return; } + if (_setFocusToPreviousAppBarHelperNeither(appBars.length - 1, appBars)) { return; } + if (_setFocusToPreviousAppBarHelper(appBars.length - 1, appBarPlacementTop, appBars)) { return; } + } else { + // Other appBar: Focus order: (1)previous other appBars (2)top appBars (3)bottom appBars (4)other appBars + if (thisAppBarIndex && _setFocusToPreviousAppBarHelperNeither(thisAppBarIndex - 1, appBars)) { return; } + if (_setFocusToPreviousAppBarHelper(appBars.length - 1, appBarPlacementTop, appBars)) { return; } + if (_setFocusToPreviousAppBarHelper(appBars.length - 1, appBarPlacementBottom, appBars)) { return; } + if (_setFocusToPreviousAppBarHelperNeither(appBars.length - 1, appBars)) { return; } + } + } + + // Sets focus to the first AppBar in the provided appBars array with given placement. + // Returns true if focus was set. False otherwise. + function _setFocusToNextAppBarHelper(startIndex, appBarPlacement, appBars) { + for (var i = startIndex; i < appBars.length; i++) { + if (appBars[i].winControl + && appBars[i].winControl.placement === appBarPlacement + && !appBars[i].winControl.hidden + && appBars[i].winControl._focusOnFirstFocusableElement + && appBars[i].winControl._focusOnFirstFocusableElement()) { + return true; + } + } + return false; + } + + // Sets focus to the first AppBar in the provided appBars array with other placement. + // Returns true if focus was set. False otherwise. + function _setFocusToNextAppBarHelperNeither(startIndex, appBars) { + for (var i = startIndex; i < appBars.length; i++) { + if (appBars[i].winControl + && appBars[i].winControl.placement != appBarPlacementBottom + && appBars[i].winControl.placement != appBarPlacementTop + && !appBars[i].winControl.hidden + && appBars[i].winControl._focusOnFirstFocusableElement + && appBars[i].winControl._focusOnFirstFocusableElement()) { + return true; + } + } + return false; + } + + // Sets focus to the first tab stop of the next AppBar + // AppBar tabbing order: + // 1) Bottom AppBars + // 2) Top AppBars + // 3) Other AppBars + // DOM order is respected, because an AppBar should not have a defined tabIndex + function _setFocusToNextAppBar() { + var appBars = document.querySelectorAll("." + appBarClass); + + var thisAppBarIndex = 0; + for (var i = 0; i < appBars.length; i++) { + if (appBars[i] === this.parentElement) { + thisAppBarIndex = i; + break; + } + } + + var appBarControl = this.parentElement.winControl; + if (this.parentElement.winControl.placement === appBarPlacementBottom) { + // Bottom appBar: Focus order: (1)next bottom appBars (2)top appBars (3)other appBars (4)bottom appBars + if (_setFocusToNextAppBarHelper(thisAppBarIndex + 1, appBarPlacementBottom, appBars)) { return; } + if (_setFocusToNextAppBarHelper(0, appBarPlacementTop, appBars)) { return; } + if (_setFocusToNextAppBarHelperNeither(0, appBars)) { return; } + if (_setFocusToNextAppBarHelper(0, appBarPlacementBottom, appBars)) { return; } + } else if (this.parentElement.winControl.placement === appBarPlacementTop) { + // Top appBar: Focus order: (1)next top appBars (2)other appBars (3)bottom appBars (4)top appBars + if (_setFocusToNextAppBarHelper(thisAppBarIndex + 1, appBarPlacementTop, appBars)) { return; } + if (_setFocusToNextAppBarHelperNeither(0, appBars)) { return; } + if (_setFocusToNextAppBarHelper(0, appBarPlacementBottom, appBars)) { return; } + if (_setFocusToNextAppBarHelper(0, appBarPlacementTop, appBars)) { return; } + } else { + // Other appBar: Focus order: (1)next other appBars (2)bottom appBars (3)top appBars (4)other appBars + if (_setFocusToNextAppBarHelperNeither(thisAppBarIndex + 1, appBars)) { return; } + if (_setFocusToNextAppBarHelper(0, appBarPlacementBottom, appBars)) { return; } + if (_setFocusToNextAppBarHelper(0, appBarPlacementTop, appBars)) { return; } + if (_setFocusToNextAppBarHelperNeither(0, appBars)) { return; } + } + } + + // Updates the firstDiv & finalDiv of all visible AppBars + function _updateAllAppBarsFirstAndFinalDiv() { + var appBars = document.querySelectorAll("." + appBarClass); + + for (var i = 0; i < appBars.length; i++) { + if (appBars[i].winControl + && !appBars[i].winControl.hidden + && appBars[i].winControl._updateFirstAndFinalDiv) { + appBars[i].winControl._updateFirstAndFinalDiv(); + } + } + } + + // Returns true if a visible non-sticky (light dismiss) AppBar is found in the document + function _isThereVisibleNonStickyBar() { + var appBars = document.querySelectorAll("." + appBarClass); + for (var i = 0; i < appBars.length; i++) { + var appBarControl = appBars[i].winControl; + if (appBarControl && !appBarControl.sticky && + (!appBarControl.hidden || appBarControl._element.winAnimating === "showing")) { + return true; + } + } + + return false; + } + + // Hide all light dismiss AppBars if what has focus is not part of a AppBar or flyout. + function _hideIfAllAppBarsLostFocus() { + if (!thisWinUI.AppBar._isAppBarOrChild(document.activeElement)) { + thisWinUI.AppBar._hideLightDismissAppBars(null, false); + // Ensure that sticky appbars clear cached focus after light dismiss are dismissed, which moved focus. + thisWinUI.AppBar._ElementWithFocusPreviousToAppBar = null; + } + } + + // If the previous focus was not a AppBar or CED, store it in the cache + // (_isAppBarOrChild tests CED for us). + function _checkStorePreviousFocus(focusEvent) { + if (focusEvent.relatedTarget + && focusEvent.relatedTarget.focus + && !thisWinUI.AppBar._isAppBarOrChild(focusEvent.relatedTarget)) { + _storePreviousFocus(focusEvent.relatedTarget); + } + } + + // Cache the previous focus information + function _storePreviousFocus(element) { + if (element) { + thisWinUI.AppBar._ElementWithFocusPreviousToAppBar = element; + } + } + + // Try to return focus to what had focus before. + // If successfully return focus to a textbox, restore the selection too. + function _restorePreviousFocus() { + thisWinUI._Overlay._trySetActive(thisWinUI.AppBar._ElementWithFocusPreviousToAppBar); + } + + var strings = { + get ariaLabel() { return WinJS.Resources._getWinJSString("ui/appBarAriaLabel").value; }, + get requiresCommands() { return WinJS.Resources._getWinJSString("ui/requiresCommands").value; }, + get nullCommand() { return WinJS.Resources._getWinJSString("ui/nullCommand").value; }, + get cannotChangePlacementWhenVisible() { return WinJS.Resources._getWinJSString("ui/cannotChangePlacementWhenVisible").value; }, + get badLayout() { return WinJS.Resources._getWinJSString("ui/badLayout").value; }, + get cannotChangeLayoutWhenVisible() { return WinJS.Resources._getWinJSString("ui/cannotChangeLayoutWhenVisible").value; } + }; + + var AppBar = WinJS.Class.derive(WinJS.UI._Overlay, function AppBar_ctor(element, options) { + /// + /// + /// Creates a new AppBar control. + /// + /// + /// The DOM element that will host the control. + /// + /// + /// The set of properties and values to apply to the new AppBar control. + /// + /// + /// The new AppBar control. + /// + /// + + this._initializing = true; + + // Simplify checking later + options = options || {}; + + // Make sure there's an input element + this._element = element || document.createElement("div"); + this._id = this._element.id || this._element.uniqueID; + this._writeProfilerMark("constructor,StartTM"); + + // validate that if they didn't set commands, but want command + // layout that the HTML only contains commands. Do this first + // so that we don't leave partial AppBars in the DOM. + if (options.layout !== appBarLayoutCustom && !options.commands && this._element) { + // Shallow copy object so we can modify it. + options = WinJS.Utilities._shallowCopy(options); + options.commands = this._verifyCommandsOnly(this._element, "WinJS.UI.AppBarCommand"); + } + + // Call the base overlay constructor helper + this._baseOverlayConstructor(this._element, options); + + this._initializing = false; + + // Make a click eating div + thisWinUI._Overlay._createClickEatingDivAppBar(); + + // Attach our css class, + WinJS.Utilities.addClass(this._element, appBarClass); + // Also may need a command class if not a custom layout appbar + if (options.layout !== appBarLayoutCustom) { + WinJS.Utilities.addClass(this._element, commandClass); + } + + if (!options.placement) { + // Make sure we have default placement + this.placement = appBarPlacementBottom; + } + + // Make sure we have an ARIA role + var role = this._element.getAttribute("role"); + if (!role) { + this._element.setAttribute("role", "menubar"); + } + var label = this._element.getAttribute("aria-label"); + if (!label) { + this._element.setAttribute("aria-label", strings.ariaLabel); + } + + // Handle key down (esc) and key pressed (left & right) + this._element.addEventListener("keydown", this._handleKeyDown.bind(this), false); + + // Attach event handler + if (!appBarCommandEvent) { + // We'll trigger on invoking. Could also have invoked or canceled + // Eventually we may want click up on invoking and drop back on invoked. + // Check for namespace so it'll behave in the designer. + if (WinJS.Utilities.hasWinRT) { + var commandUI = Windows.UI.Input.EdgeGesture.getForCurrentView(); + commandUI.addEventListener("starting", _startingEdgy); + commandUI.addEventListener("completed", _completedEdgy); + commandUI.addEventListener("canceled", _canceledEdgy); + } + + // Need to know if the IHM is done scrolling + document.addEventListener("MSManipulationStateChanged", _allManipulationChanged, false); + + appBarCommandEvent = true; + } + + // Make sure _Overlay event handlers are hooked up (this aids light dismiss) + this._addOverlayEventHandlers(false); + + // Need to store what had focus before + this._element.addEventListener("focusin", function (event) { _checkStorePreviousFocus(event); }, false); + + // Need to hide ourselves if we lose focus + this._element.addEventListener("focusout", function (event) { _hideIfAllAppBarsLostFocus(); }, false); + + // Commands layout AppBar measures and caches its content synchronously in setOptions through the .commands property setter. + // Remove the commands layout AppBar from the layout tree at this point so we don't cause unnecessary layout costs whenever + // the window resizes or when CSS changes are applied to the commands layout AppBar's parent element. + if (this.layout === appBarLayoutCommands) { + this._element.style.display = "none"; + } + + this._writeProfilerMark("constructor,StopTM"); + + return this; + }, { + // Public Properties + + /// The placement of the AppBar on the display. Values are "top" or "bottom". + placement: { + get: function () { + return this._placement; + }, + set: function (value) { + // In designer we may have to move it + var wasShown = false; + if (window.Windows && Windows.ApplicationModel && Windows.ApplicationModel.DesignMode && Windows.ApplicationModel.DesignMode.designModeEnabled && !this.hidden) { + this._hide(); + wasShown = true; + } + + if (!this.hidden) { + throw new WinJS.ErrorFromName("WinJS.UI.AppBar.CannotChangePlacementWhenVisible", strings.cannotChangePlacementWhenVisible); + } + + // Set placement + this._placement = value; + + // Clean up win-top, win-bottom styles + if (this._placement === appBarPlacementTop) { + WinJS.Utilities.addClass(this._element, topClass); + WinJS.Utilities.removeClass(this._element, bottomClass); + } else if (this._placement === appBarPlacementBottom) { + WinJS.Utilities.removeClass(this._element, topClass); + WinJS.Utilities.addClass(this._element, bottomClass); + } else { + WinJS.Utilities.removeClass(this._element, topClass); + WinJS.Utilities.removeClass(this._element, bottomClass); + } + + // Make sure our animations are correct + this._assignAnimations(); + + // Show again if we hid ourselves for the designer + if (wasShown) { + this._show(); + } + } + }, + + /// + /// Gets or sets the layout of the AppBar contents to either "commands" or "custom". + /// + layout: { + get: function () { + // Defaults to commands if not set + return this._layout ? this._layout : appBarLayoutCommands; + }, + set: function (value) { + if (value !== appBarLayoutCommands && value !== appBarLayoutCustom) { + throw new WinJS.ErrorFromName("WinJS.UI.AppBar.BadLayout", strings.badLayout); + } + + // In designer we may have to redraw it + var wasShown = false; + if (window.Windows && Windows.ApplicationModel && Windows.ApplicationModel.DesignMode && Windows.ApplicationModel.DesignMode.designModeEnabled && !this.hidden) { + this._hide(); + wasShown = true; + } + + if (!this.hidden) { + throw new WinJS.ErrorFromName("WinJS.UI.AppBar.CannotChangeLayoutWhenVisible", strings.cannotChangeLayoutWhenVisible); + } + + // Set layout + this._layout = value; + + // Update our classes + if (this._layout === appBarLayoutCommands) { + // Add the appbar css class back + WinJS.Utilities.addClass(this._element, commandClass); + } else { + // Remove the appbar css class + WinJS.Utilities.removeClass(this._element, commandClass); + } + + // Show again if we hid ourselves for the designer + if (wasShown) { + this._show(); + } + }, + configurable: true + }, + + /// + /// Gets or sets value that indicates whether the AppBar is sticky. + /// This value is true if the AppBar is sticky; otherwise, it's false. + /// + sticky: { + get: function () { + return this._sticky; + }, + set: function (value) { + // If it doesn't change, do nothing + if (this._sticky === !!value) { + return; + } + + this._sticky = !!value; + + // Note: caller has to call .show() if they also want it visible + + // Show or hide the click eating div based on sticky value + if (!this.hidden && this._element.style.visibility === "visible") { + // May have changed sticky state for keyboard navigation + _updateAllAppBarsFirstAndFinalDiv(); + + // Ensure that the click eating div is in the correct state + if (this._sticky) { + if (!_isThereVisibleNonStickyBar()) { + thisWinUI._Overlay._hideClickEatingDivAppBar(); + } + } else { + thisWinUI._Overlay._showClickEatingDivAppBar(); + + if (this._shouldStealFocus()) { + _storePreviousFocus(document.activeElement); + this._setFocusToAppBar(); + } + } + } + } + }, + + /// + /// Sets the commands for the AppBar. This property accepts an array of AppBarCommand objects. + /// + commands: { + set: function (value) { + // Fail if trying to set when visible + if (!this.hidden) { + throw new WinJS.ErrorFromName("WinJS.UI.AppBar.CannotChangeCommandsWhenVisible", WinJS.Resources._formatString(thisWinUI._Overlay.commonstrings.cannotChangeCommandsWhenVisible, "AppBar")); + } + + // Start from scratch + if (!this._initializing) { + // AppBarCommands defined in markup don't want to be disposed during initialization. + this._disposeChildren(); + } + WinJS.Utilities.empty(this._element); + + // In case they had only one... + if (!Array.isArray(value)) { + value = [value]; + } + + // Add commands + var len = value.length; + for (var i = 0; i < len; i++) { + this._addCommand(value[i]); + } + + // Need to measure all content commands after they have been added to the AppBar to make sure we allow + // user defined CSS rules based on the ancestor of the content command to take affect. + this._needToMeasure = true; + + // In case this is called from the constructor we wait for the AppBar to get added to the DOM. + // It should be added in the synchronous block in which the constructor was called. + WinJS.Utilities.Scheduler.schedule(this._layoutCommands, WinJS.Utilities.Scheduler.Priority.idle, this, "WinJS.AppBar._layoutCommands"); + } + }, + + getCommandById: function (id) { + /// + /// + /// Retrieves the command with the specified ID from this AppBar. + /// If more than one command is found, this method returns them all. + /// + /// Id of the command to return. + /// + /// The command found, an array of commands if more than one have the same ID, or null if no command is found. + /// + /// + var commands = this.element.querySelectorAll("#" + id); + for (var count = 0; count < commands.length; count++) { + // Any elements we generated this should succeed for, + // but remove missing ones just to be safe. + commands[count] = commands[count].winControl; + if (!commands[count]) { + commands.splice(count, 1); + } + } + + if (commands.length === 1) { + return commands[0]; + } else if (commands.length === 0) { + return null; + } + + return commands; + }, + + + showCommands: function (commands) { + /// + /// + /// Show the specified commands of the AppBar. + /// + /// + /// An array of the commands to show. The array elements may be AppBarCommand objects, or the string identifiers (IDs) of commands. + /// + /// + if (!commands) { + throw new WinJS.ErrorFromName("WinJS.UI.AppBar.RequiresCommands", strings.requiresCommands); + } + + this._showCommands(commands); + }, + + + hideCommands: function (commands) { + /// + /// + /// Hides the specified commands of the AppBar. + /// + /// Required. Command or Commands to hide, either String, DOM elements, or WinJS objects. + /// + if (!commands) { + throw new WinJS.ErrorFromName("WinJS.UI.AppBar.RequiresCommands", strings.requiresCommands); + } + + this._hideCommands(commands); + }, + + + showOnlyCommands: function (commands) { + /// + /// + /// Show the specified commands, hiding all of the others in the AppBar. + /// + /// + /// An array of the commands to show. The array elements may be AppBarCommand objects, or the string identifiers (IDs) of commands. + /// + /// + if (!commands) { + throw new WinJS.ErrorFromName("WinJS.UI.AppBar.RequiresCommands", strings.requiresCommands); + } + + this._showOnlyCommands(commands); + }, + + + show: function () { + /// + /// + /// Shows the AppBar, if hidden, regardless of other state + /// + /// + // Just wrap the private one, turning off keyboard invoked flag + this._writeProfilerMark("show,StartTM"); // The corresponding "stop" profiler mark is handled in _Overlay._baseEndShow(). + this._keyboardInvoked = false; + this._doNotFocus = !!this.sticky; + this._show(); + }, + + _show: function AppBar_show() { + // Don't do anything if disabled + if (this.disabled) { + return; + } + + // Make sure everything fits before showing + this._layoutCommands(); + + this._scaleAppBar(); + + // If we're covered by a keyboard we look hidden, so we may have to jump up + if (this._keyboardObscured) { + // just make us look hidden so that show() gets called. + this._fakeHide = true; + this._keyboardObscured = false; + } + + // Regardless we're going to be in a CED state + if (!this.sticky) { + // Need click-eating div to be visible ASAP. + thisWinUI._Overlay._showClickEatingDivAppBar(); + } + + // If we are already animating, just remember this for later + if (this._element.winAnimating) { + this._doNext = "show"; + return false; + } + + // We call our base _baseShow because AppBar may need to override show + // "hiding" would need to cancel. + this._baseShow(); + + // Clean up tabbing behavior by making sure first and final divs are correct after showing. + if (!this.sticky && _isThereVisibleNonStickyBar()) { + _updateAllAppBarsFirstAndFinalDiv(); + } else { + this._updateFirstAndFinalDiv(); + } + + // Check if we should steal focus + if (!this._doNotFocus && this._shouldStealFocus()) { + // Store what had focus if nothing currently is stored + if (!thisWinUI.AppBar._ElementWithFocusPreviousToAppBar) { + _storePreviousFocus(document.activeElement); + } + + this._setFocusToAppBar(); + } + }, + + hide: function () { + /// + /// + /// Hides the AppBar. + /// + /// + // Just wrap the private one + this._writeProfilerMark("hide,StartTM"); // The corresponding "stop" profiler mark is handled in _Overlay._baseEndHide(). + this._hide(); + }, + + _hide: function AppBar_hide() { + // If we're covered by a keyboard we already look hidden + if (this._keyboardObscured && !this._animating) { + this._keyboardObscured = false; + this._baseEndHide(); + } else { + // We call our base "_baseHide" because AppBar may need to override hide + this._baseHide(); + } + + // Determine if there are any AppBars that are visible. + // Set the focus to the next visible AppBar. + // If there are none, set the focus to the control stored in the cache, which + // is what had focus before the AppBars were given focus. + var appBars = document.querySelectorAll("." + appBarClass); + var areOtherAppBars = false; + var areOtherNonStickyAppBars = false; + var i; + for (i = 0; i < appBars.length; i++) { + var appBarControl = appBars[i].winControl; + if (appBarControl && !appBarControl.hidden && (appBarControl !== this)) { + areOtherAppBars = true; + + if (!appBarControl.sticky) { + areOtherNonStickyAppBars = true; + break; + } + } + } + + var settingsFlyouts = document.querySelectorAll("." + settingsFlyoutClass); + var areVisibleSettingsFlyouts = false; + for (i = 0; i < settingsFlyouts.length; i++) { + var settingsFlyoutControl = settingsFlyouts[i].winControl; + if (settingsFlyoutControl && !settingsFlyoutControl.hidden) { + areVisibleSettingsFlyouts = true; + break; + } + } + + if (!areOtherNonStickyAppBars && !areVisibleSettingsFlyouts) { + // Hide the click eating div because there are no other AppBars showing + thisWinUI._Overlay._hideClickEatingDivAppBar(); + } + + var that = this; + if (!areOtherAppBars) { + // Set focus to what had focus before showing the AppBar + if (thisWinUI.AppBar._ElementWithFocusPreviousToAppBar && + (!document.activeElement || thisWinUI.AppBar._isAppBarOrChild(document.activeElement))) { + _restorePreviousFocus(); + } + // Always clear the previous focus (to prevent temporary leaking of element) + thisWinUI.AppBar._ElementWithFocusPreviousToAppBar = null; + } else if (thisWinUI.AppBar._isWithinAppBarOrChild(document.activeElement, that.element)) { + // Set focus to next visible AppBar in DOM + + var foundCurrentAppBar = false; + for (i = 0; i <= appBars.length; i++) { + if (i === appBars.length) { + i = 0; + } + + var appBar = appBars[i]; + if (appBar === this.element) { + foundCurrentAppBar = true; + } else if (foundCurrentAppBar && !appBar.winControl.hidden) { + appBar.winControl._keyboardInvoked = !!this._keyboardInvoked; + appBar.winControl._setFocusToAppBar(); + break; + } + } + } + + // If we are hiding the last lightDismiss AppBar, + // then we need to update the tabStops of the other AppBars + if (!this.sticky && !_isThereVisibleNonStickyBar()) { + _updateAllAppBarsFirstAndFinalDiv(); + } + + // Reset these values + this._keyboardInvoked = false; + this._doNotFocus = false; + }, + + _dispose: function AppBar_dispose() { + WinJS.Utilities.disposeSubTree(this.element); + this._hide(); + }, + + _disposeChildren: function AppBar_disposeChildren() { + var appBarFirstDiv = this._element.querySelectorAll("." + firstDivClass); + appBarFirstDiv = appBarFirstDiv.length >= 1 ? appBarFirstDiv[0] : null; + var appBarFinalDiv = this._element.querySelectorAll("." + finalDivClass); + appBarFinalDiv = appBarFinalDiv.length >= 1 ? appBarFinalDiv[0] : null; + + var children = this.element.children; + var length = children.length; + for (var i = 0; i < length; i++) { + var element = children[i]; + if (element === appBarFirstDiv || element === appBarFinalDiv) { + continue; + } + if (this.layout === appBarLayoutCommands) { + element.winControl.dispose(); + } else { + WinJS.Utilities.disposeSubTree(element); + } + } + }, + + _handleKeyDown: function AppBar_handleKeyDown(event) { + // On Left/Right arrow keys, moves focus to previous/next AppbarCommand element. + // On "Esc" key press hide all flyouts and light dismiss AppBars. + + // Esc closes light-dismiss AppBars in all layouts but if the user has a text box with an IME + // candidate window open, we want to skip the ESC key event since it is handled by the IME. + // When the IME handles a key it sets event.keyCode to 229 for an easy check. + if (event.key === "Esc" && event.keyCode !== 229) { + event.preventDefault(); + event.stopPropagation(); + thisWinUI._Overlay._hideAllFlyouts(); + thisWinUI.AppBar._hideLightDismissAppBars(null, true); + } + + // Commands layout only. + if (this.layout === appBarLayoutCommands && !event.altKey) { + if (event.srcElement.msMatchesSelector(".win-interactive, .win-interactive *")) { + return; //ignore left, right, home & end keys if focused element has win-interactive class. + } + var rtl = getComputedStyle(this._element).direction === "rtl"; + var leftKey = rtl ? "Right" : "Left"; + var rightKey = rtl ? "Left" : "Right"; + + if (event.key === leftKey || event.key == rightKey || event.key === "Home" || event.key === "End") { + + var focusableCommands = this._getFocusableCommandsInLogicalOrder(); + var targetCommand; + + if (focusableCommands.length) { + switch (event.key) { + case leftKey: + // Arrowing past the last command wraps back around to the first command. + var index = Math.max(-1, focusableCommands.focusedIndex - 1) + focusableCommands.length; + targetCommand = focusableCommands[index % focusableCommands.length].winControl.lastElementFocus; + break; + + case rightKey: + // Arrowing previous to the first command wraps back around to the last command. + var index = focusableCommands.focusedIndex + 1 + focusableCommands.length; + targetCommand = focusableCommands[index % focusableCommands.length].winControl.firstElementFocus; + break; + + case "Home": + var index = 0; + targetCommand = focusableCommands[index].winControl.firstElementFocus; + break; + + case "End": + var index = focusableCommands.length - 1; + targetCommand = focusableCommands[index].winControl.lastElementFocus; + break; + } + } + + if (targetCommand) { + targetCommand.focus(); + // Prevent default so that Trident doesn't resolve the keydown event on the newly focused element. + event.preventDefault(); + } + } + } + }, + + _getFocusableCommandsInLogicalOrder: function AppBar_getCommandsInLogicalOrder() { + // Function returns an array of all the contained AppBarCommands which are reachable by left/right arrows. + // + if (this.layout === appBarLayoutCommands) { + var selectionCommands = [], + globalCommands = [], + children = this._element.children, + globalCommandHasFocus = false, + focusedIndex = -1; + + var categorizeCommand = function (element, isGlobalCommand, containsFocus) { + // Helper function to categorize the element by AppBarCommand's section property. The passed in element could be the + // AppBarCommand, or the element referenced by a content AppBarCommands's firstElementFocus/lastElementFocus property. + // + if (isGlobalCommand) { + globalCommands.push(element); + if (containsFocus) { + focusedIndex = globalCommands.length - 1; + globalCommandHasFocus = true; + } + } else { + selectionCommands.push(element); + if (containsFocus) { + focusedIndex = selectionCommands.length - 1; + } + } + } + + // Separate commands into global and selection arrays. Find the current command with focus. + // Skip the first and last indices to avoid "firstDiv" and "finalDiv". + for (var i = 1, len = children.length; i < len - 1; i++) { + var element = children[i]; + if (element && element.winControl) { + var containsFocus = element.contains(document.activeElement); + // With the inclusion of content type commands, it may be possible to tab to elements in AppBarCommands that are not reachable by arrow keys. + // Regardless, when an AppBarCommand contains the element with focus, we just include the whole command so that we can determine which + // Commands are adjacent to it when looking for the next focus destination. + if (element.winControl._isFocusable() || containsFocus) { + var isGlobalCommand = (element.winControl.section === "global"); + categorizeCommand(element, isGlobalCommand, containsFocus); + } + } + } + + var focusableCommands = selectionCommands.concat(globalCommands); + focusableCommands.focusedIndex = globalCommandHasFocus ? focusedIndex + selectionCommands.length : focusedIndex; + return focusableCommands; + } + }, + + _assignAnimations: function AppBar_assignAnimations() { + // Make sure the animations are correct for our current placement + if (this._placement === appBarPlacementTop || this._placement === appBarPlacementBottom) { + // Top or Bottom + this._currentAnimateIn = this._animateSlideIn; + this._currentAnimateOut = this._animateSlideOut; + } else { + // Default for in the middle of nowhere + this._currentAnimateIn = this._baseAnimateIn; + this._currentAnimateOut = this._baseAnimateOut; + } + }, + + // AppBar animations + _animateSlideIn: function AppBar_animateSlideIn() { + var where, + height = this._element.offsetHeight; + // Get top/bottoms + this._checkPosition(); + // Get animation direction and clear other value + if (this._placement === appBarPlacementTop) { + // Top Bar + where = { top: "-" + height + "px", left: "0px" }; + this._element.style.bottom = "auto"; + } else { + // Bottom Bar + where = { top: height + "px", left: "0px" }; + this._element.style.top = "auto"; + } + + this._element.style.opacity = 1; + this._element.style.visibility = "visible"; + return WinJS.UI.Animation.showEdgeUI(this._element, where, { mechanism: "transition" }); + }, + + _animateSlideOut: function AppBar_animateSlideOut() { + var where, + height = this._element.offsetHeight; + if (this._placement === appBarPlacementTop) { + // Top Bar + where = { top: "-" + height + "px", left: "0px" }; + // Adjust for scrolling or soft keyboard positioning + this._element.style.top = (this._getTopOfVisualViewport()) + "px"; + } else { + // Bottom Bar + where = { top: height + "px", left: "0px" }; + // Adjust for scrolling or soft keyboard positioning + this._element.style.bottom = (this._getAdjustedBottom()) + "px"; + } + + return WinJS.UI.Animation.hideEdgeUI(this._element, where, { mechanism: "transition" }); + }, + + _isABottomAppBarInTheProcessOfShowing: function AppBar_isABottomAppBarInTheProcessOfShowing() { + var appbars = document.querySelectorAll("." + appBarClass + "." + bottomClass); + for (var i = 0; i < appbars.length; i++) { + if (appbars[i].winAnimating === "showing") { + return true; + } + } + + return false; + }, + + // Returns true if + // 1) This is a bottom appbar + // 2) No appbar has focus and a bottom appbar is not in the process of showing + // 3) What currently has focus is neither a bottom appbar nor a top appbar + // AND a bottom appbar is not in the process of showing. + // Otherwise Returns false + _shouldStealFocus: function AppBar_shouldStealFocus() { + var activeElementAppBar = thisWinUI.AppBar._isAppBarOrChild(document.activeElement); + if (this._element === activeElementAppBar) { + // This appbar already has focus and we don't want to move focus + // from where it currently is in this appbar. + return false; + } + if (this._placement === appBarPlacementBottom) { + // This is a bottom appbar + return true; + } + + var isBottomAppBarShowing = this._isABottomAppBarInTheProcessOfShowing(); + if (!activeElementAppBar) { + // Currently no appbar has focus. + // Return true if a bottom appbar is not in the process of showing. + return !isBottomAppBarShowing; + } + if (!activeElementAppBar.winControl) { + // This should not happen, but if it does we want to make sure + // that an AppBar ends up with focus. + return true; + } + if ((activeElementAppBar.winControl._placement !== appBarPlacementBottom) + && (activeElementAppBar.winControl._placement !== appBarPlacementTop) + && !isBottomAppBarShowing) { + // What currently has focus is neither a bottom appbar nor a top appbar + // -and- + // a bottom appbar is not in the process of showing. + return true; + } + return false + }, + + // Set focus to the passed in AppBar + _setFocusToAppBar: function AppBar_setFocusToAppBar() { + if (this._focusOnFirstFocusableElement()) { + // Prevent what is gaining focus from showing that it has focus, + // but only in the non-keyboard scenario. + if (!this._keyboardInvoked) { + thisWinUI._Overlay._addHideFocusClass(document.activeElement); + } + } else { + // No first element, set it to appbar itself + thisWinUI._Overlay._trySetActive(this._element); + } + }, + + _contentChanged: function AppBar_contentChanged() { + this._updateCommandsWidth(); + this._scaleAppBar(); + }, + + _scaleAppBar: function AppBar_scaleAppBar() { + // For commands layout AppBars only. If the total width of all AppBarCommands is greater than the + // width of the AppBar, add the win-reduced class to the AppBar element. + + if (this.layout === appBarLayoutCommands) { + // Measure AppBar's contents width, AppBar offsetWidth and AppBar padding: + var widthOfVisibleContent = this._getCommandsWidth(); + if (this._appBarTotalKnownWidth !== +this._appBarTotalKnownWidth) { + this._appBarTotalKnownWidth = this._scaleAppBarHelper(); + } + + if (widthOfVisibleContent <= this._appBarTotalKnownWidth) { + // Full size commands + WinJS.Utilities.removeClass(this._element, reducedClass); + } + else { + // Reduced size commands + WinJS.Utilities.addClass(this._element, reducedClass); + } + } + }, + + _scaleAppBarHelper: function AppBar_scaleAppBarHelper() { + // This exists as a single line function so that unit tests can + // overwrite it since they can't resize the WWA window. + return document.documentElement.clientWidth; + }, + + _updateCommandsWidth: function AppBar_updateCommandsWidth(commandSubSet) { + // Whenever Commands are hidden/shown in the Commands layout AppBar, this function is called + // to update the cached width measurement of all visible AppBarCommands in the AppBar. + if (this.layout === appBarLayoutCommands) { + var buttonsCount = 0; + var separatorsCount = 0; + var command; + var commands = commandSubSet; + + this._widthOfAllCommands = 0; + if (!commands) { + // Crawl the AppBar's inner HTML for the commands. + commands = this._getVisibleCommands(); + } + this._widthOfAllCommands = this._getCommandsWidth(commands); + } + }, + + _getCommandsWidth: function AppBar_getCommandsWidth(commandSubSet) { + if (!commandSubSet) { + // Return the cached width of all previously visible commands in the AppBar. + return this._widthOfAllCommands; + } else { + // Return the width of the specified subset. + var separatorsCount = 0; + var buttonsCount = 0; + var widthOfCommandSubSet = 0; + var command; + for (var i = 0, len = commandSubSet.length; i < len; i++) { + command = commandSubSet[i].winControl || commandSubSet[i]; + if (command._type === typeSeparator) { + separatorsCount++ + } else if (command._type !== typeContent) { + // button, toggle, and flyout types all have the same width. + buttonsCount++; + } else { + widthOfCommandSubSet += command._fullSizeWidth; + } + } + } + return widthOfCommandSubSet += (separatorsCount * separatorWidth) + (buttonsCount * buttonWidth); + }, + + _beginAnimateCommands: function AppBar_beginAnimateCommands(showCommands, hideCommands, otherVisibleCommands) { + // The parameters are 3 mutually exclusive arrays of win-command elements contained in this Overlay. + // 1) showCommands[]: All of the HIDDEN win-command elements that ARE scheduled to show. + // 2) hideCommands[]: All of the VISIBLE win-command elements that ARE scheduled to hide. + // 3) otherVisibleCommands[]: All VISIBLE win-command elements that ARE NOT scheduled to hide. + if (this.layout === appBarLayoutCommands) { + this._scaleCommandsAfterAnimations = false; + // Update our command counts now, to what they will be after we complete the animations. + var visibleCommandsAfterAnimations = otherVisibleCommands.concat(showCommands); + this._updateCommandsWidth(visibleCommandsAfterAnimations) + var changeInWidth = this._getCommandsWidth(showCommands) - this._getCommandsWidth(hideCommands); + if (changeInWidth > 0) { + // Width of contents is going to increase. If there won't be enough room to fit them all on a single row, + // reduce size of commands before the new content appears. + this._scaleAppBar(); + } else if (changeInWidth < 0) { + // Width of contents is going to decrease. Once animations are complete, check if + // there is enough available space to make the remaining commands full size. + this._scaleCommandsAfterAnimations = true; + } + } + }, + + _endAnimateCommands: function AppBar_endAnimateCommands() { + if (this._scaleCommandsAfterAnimations) { + this._scaleAppBar(); + } + }, + + _addCommand: function AppBar_addCommand(command) { + if (!command) { + throw new WinJS.ErrorFromName("WinJS.UI.AppBar.NullCommand", strings.nullCommand); + } + // See if it's a command already + if (!command._element) { + // Not a command, so assume it is options for the command's constructor. + command = new WinJS.UI.AppBarCommand(null, command); + } + // If we were attached somewhere else, detach us + if (command._element.parentElement) { + command._element.parentElement.removeChild(command._element); + } + // Reattach us + this._element.appendChild(command._element); + }, + + _measureContentCommands: function AppBar_measureContentCommands() { + // AppBar measures the width of content commands when they are first added + // and then caches that value to avoid additional layouts in the future. + + // Can't measure unless We're in the document body + if (document.body.contains(this.element)) { + this._needToMeasure = false; + + // Remove the reducedClass from AppBar to ensure fullsize measurements + var hadReducedClass = WinJS.Utilities.hasClass(this.element, reducedClass); + WinJS.Utilities.removeClass(this._element, reducedClass); + + // Make sure AppBar and children have width dimensions. + var prevAppBarDisplay = this.element.style.display; + var prevCommandDisplay; + this.element.style.display = ""; + + var commandElements = this._element.children; + var element; + for (var i = 0, len = commandElements.length; i < len; i++) { + element = commandElements[i]; + if (element.winControl && element.winControl._type === typeContent) { + // Make sure command has width dimensions before we measure. + prevCommandDisplay = element.style.display; + element.style.display = ""; + element.winControl._fullSizeWidth = WinJS.Utilities.getTotalWidth(element) || 0; + element.style.display = prevCommandDisplay; + } + } + + // Restore state to AppBar. + this.element.display = prevAppBarDisplay; + if (hadReducedClass) { + WinJS.Utilities.addClass(this._element, reducedClass); + } + } + }, + + // Performs any pending measurements on "content" type AppBarCommands and scales the AppBar to fit all AppBarCommand types accordingly. + _layoutCommands: function AppBar_layoutCommands() { + if (this._needToMeasure && this.layout === appBarLayoutCommands) { + this._measureContentCommands(); + this._contentChanged(); + } + }, + + // Get the top of the top appbars, this is always 0 because appbar uses + // -ms-device-fixed positioning. + _getTopOfVisualViewport: function AppBar_getTopOfVisualViewPort() { + return 0; + }, + + // Get the bottom of the bottom appbars, Bottom is just 0, if there's no IHM. + // When the IHM appears, the default behavior is to resize the view. If a resize + // happens, we can rely on -ms-device-fixed positioning and leave the bottom + // at 0. However if resize doesn't happen, then the keyboard obscures the appbar + // and we will need to adjust the bottom of the appbar by distance of the keyboard. + _getAdjustedBottom: function AppBar_getAdjustedBottom() { + // Need the distance the IHM moved as well. + return thisWinUI._Overlay._keyboardInfo._visibleDocBottomOffset; + }, + + _showingKeyboard: function AppBar_showingKeyboard(event) { + // Remember keyboard showing state. + this._keyboardObscured = false; + this._keyboardHiding = false; + + // If we're already moved, then ignore the whole thing + if (thisWinUI._Overlay._keyboardInfo._visible && this._alreadyInPlace()) { + return; + } + + this._keyboardShowing = true; + // If focus is in the appbar, don't cause scrolling. + if (!this.hidden && this._element.contains(document.activeElement)) { + event.ensuredFocusedElementInView = true; + } + + // Check if appbar moves or is obscured + if (!this.hidden && this._placement !== appBarPlacementTop && thisWinUI._Overlay._isFlyoutVisible()) { + // Remember that we're obscured + this._keyboardObscured = true; + } else { + // If not obscured, tag as showing and set timeout to restore us. + this._scrollHappened = false; + } + + // Also set timeout regardless, so we can clean up our _keyboardShowing flag. + var that = this; + setTimeout(function (e) { that._checkKeyboardTimer(e); }, thisWinUI._Overlay._keyboardInfo._animationShowLength + thisWinUI._Overlay._scrollTimeout); + }, + + _hidingKeyboard: function AppBar_hidingKeyboard(event) { + // We won't be obscured + this._keyboardObscured = false; + this._keyboardShowing = false; + this._keyboardHiding = true; + + // We'll either just reveal the current space or resize the window + if (!thisWinUI._Overlay._keyboardInfo._isResized) { + // If we're visible or only fake hiding under keyboard, or already animating, + // then snap us to our final position. + if (!this.hidden || this._fakeHide || this._animating) { + // Not resized, update our final position immediately + this._checkScrollPosition(); + this._element.style.display = ""; + this._fakeHide = false; + } + this._keyboardHiding = false; + } + // Else resize should clear keyboardHiding. + }, + + _resize: function AppBar_resize(event) { + // If we're hidden by the keyboard, then hide bottom appbar so it doesn't pop up twice when it scrolls + if (this._keyboardShowing) { + // Top is allowed to scroll off the top, but we don't want bottom to peek up when + // scrolled into view since we'll show it ourselves and don't want a stutter effect. + if (!this.hidden) { + if (this._placement !== appBarPlacementTop && !this._keyboardObscured) { + // If viewport doesn't match window, need to vanish momentarily so it doesn't scroll into view, + // however we don't want to toggle the visibility="hidden" hidden flag. + this._element.style.display = "none"; + } + } + // else if we're top we stay, and if there's a flyout, stay obscured by the keyboard. + } else if (this._keyboardHiding) { + this._keyboardHiding = false; + if (!this.hidden || this._animating) { + // Snap to final position + this._checkScrollPosition(); + this._element.style.display = ""; + this._fakeHide = false; + } + } + + // Check for horizontal window resizes. + this._appBarTotalKnownWidth = null; + if (!this.hidden) { + this._scaleAppBar(); + } + }, + + _checkKeyboardTimer: function AppBar_checkKeyboardTimer() { + if (!this._scrollHappened) { + this._mayEdgeBackIn(); + } + }, + + _manipulationChanged: function AppBar_manipulationChanged(event) { + // See if we're at the not manipulating state, and we had a scroll happen, + // which is implicitly after the keyboard animated. + if (event.currentState === 0 && this._scrollHappened) { + this._mayEdgeBackIn(); + } + }, + + _mayEdgeBackIn: function AppBar_mayEdgeBackIn(event) { + // May need to react to IHM being resized event + if (this._keyboardShowing) { + // If not top appbar or viewport isn't still at top, then need to show again + this._keyboardShowing = false; + // If obscured (flyout showing), don't change. + // If hidden, may be because _fakeHide was set in _resize. + // If bottom we have to move, or if top scrolled off screen. + if (!this._keyboardObscured && (!this.hidden || this._fakeHide) && + (this._placement !== appBarPlacementTop || thisWinUI._Overlay._keyboardInfo._visibleDocTop !== 0)) { + this._doNotFocus = true; + this._fakeHide = true; + this._show(); + } else { + // Ensure any animation dropped during the showing keyboard are caught up. + this._checkDoNext(); + } + } + this._scrollHappened = false; + }, + + // _checkPosition repositions the AppBar when the soft keyboard shows up + _checkPosition: function AppBar_checkPosition() { + // Bottom's the only one needing movement + if (this._placement === appBarPlacementBottom) { + this._element.style.bottom = this._getAdjustedBottom() + "px"; + } else if (this._placement === appBarPlacementTop) { + this._element.style.top = this._getTopOfVisualViewport() + "px"; + } + // else we don't touch custom positions + }, + + _checkScrollPosition: function AppBar_checkScrollPosition(event) { + // If keyboard's animating, then remember we may come in + if (this._keyboardShowing) { + // Tag that it's OK to edge back in. + this._scrollHappened = true; + return; + } + + // We only need to update if we're visible + if (!this.hidden || this._animating) { + this._checkPosition(); + // Ensure any animation dropped during the showing keyboard are caught up. + this._checkDoNext(); + } + }, + + _alreadyInPlace: function AppBar_alreadyInPlace() { + // See if we're already where we're supposed to be. + if (this._placement === appBarPlacementBottom) { + if (parseInt(this._element.style.bottom) === this._getAdjustedBottom()) { + return true; + } + } else if (this._placement === appBarPlacementTop) { + if (parseInt(this._element.style.top) === this._getTopOfVisualViewport()) { + return true; + } + } + // else we don't understand custom positioning + return false; + }, + + // If there is a visible non-sticky AppBar then it sets the firstDiv tabIndex to + // the minimum tabIndex found in the AppBars and finalDiv to the max found. + // Otherwise sets their tabIndex to -1 so they are not tab stops. + _updateFirstAndFinalDiv: function AppBar_updateFirstAndFinalDiv() { + var appBarFirstDiv = this._element.querySelectorAll("." + firstDivClass); + appBarFirstDiv = appBarFirstDiv.length >= 1 ? appBarFirstDiv[0] : null; + + var appBarFinalDiv = this._element.querySelectorAll("." + finalDivClass); + appBarFinalDiv = appBarFinalDiv.length >= 1 ? appBarFinalDiv[0] : null; + + // Remove the firstDiv & finalDiv if they are not at the appropriate locations + if (appBarFirstDiv && (this._element.children[0] != appBarFirstDiv)) { + appBarFirstDiv.parentNode.removeChild(appBarFirstDiv); + appBarFirstDiv = null; + } + if (appBarFinalDiv && (this._element.children[this._element.children.length - 1] != appBarFinalDiv)) { + appBarFinalDiv.parentNode.removeChild(appBarFinalDiv); + appBarFinalDiv = null; + } + + // Create and add the firstDiv & finalDiv if they don't already exist + if (!appBarFirstDiv) { + // Add a firstDiv that will be the first child of the appBar. + // On focus set focus to the previous appBar. + // The div should only be focusable if there are visible non-sticky AppBars. + appBarFirstDiv = document.createElement("div"); + // display: inline is needed so that the div doesn't take up space and cause the page to scroll on focus + appBarFirstDiv.style.display = "inline"; + appBarFirstDiv.className = firstDivClass; + appBarFirstDiv.tabIndex = -1; + appBarFirstDiv.setAttribute("aria-hidden", "true"); + appBarFirstDiv.addEventListener("focus", _setFocusToPreviousAppBar, true); + this._element.insertAdjacentElement("AfterBegin", appBarFirstDiv); + } + if (!appBarFinalDiv) { + // Add a finalDiv that will be the last child of the appBar. + // On focus set focus to the next appBar. + // The div should only be focusable if there are visible non-sticky AppBars. + appBarFinalDiv = document.createElement("div"); + // display: inline is needed so that the div doesn't take up space and cause the page to scroll on focus + appBarFinalDiv.style.display = "inline"; + appBarFinalDiv.className = finalDivClass; + appBarFinalDiv.tabIndex = -1; + appBarFinalDiv.setAttribute("aria-hidden", "true"); + appBarFinalDiv.addEventListener("focus", _setFocusToNextAppBar, true); + this._element.appendChild(appBarFinalDiv); + } + + // Update the tabIndex of the firstDiv & finalDiv + if (_isThereVisibleNonStickyBar()) { + var elms = this._element.getElementsByTagName("*"); + + if (appBarFirstDiv) { + appBarFirstDiv.tabIndex = WinJS.Utilities._getLowestTabIndexInList(elms); + } + if (appBarFinalDiv) { + appBarFinalDiv.tabIndex = WinJS.Utilities._getHighestTabIndexInList(elms); + } + } else { + if (appBarFirstDiv) { + appBarFirstDiv.tabIndex = -1; + } + if (appBarFinalDiv) { + appBarFinalDiv.tabIndex = -1; + } + } + }, + + _writeProfilerMark: function AppBar_writeProfilerMark(text) { + msWriteProfilerMark("WinJS.UI.AppBar:" + this._id + ":" + text); + } + }); + + // Statics + AppBar._ElementWithFocusPreviousToAppBar = null; + + // Returns appbar element (or CED/sentinal) if the element or what had focus before the element (if a Flyout) is either: + // 1) an AppBar, + // 2) OR in the subtree of an AppBar, + // 3) OR an AppBar click eating div. + // Returns null otherwise. + AppBar._isAppBarOrChild = function (element) { + // If it's null, we can't do this + if (!element) { + return null; + } + + // click eating divs and sentinals should not have children + if (WinJS.Utilities.hasClass(element, thisWinUI._Overlay._clickEatingAppBarClass) || + WinJS.Utilities.hasClass(element, thisWinUI._Overlay._clickEatingFlyoutClass) || + WinJS.Utilities.hasClass(element, firstDivClass) || + WinJS.Utilities.hasClass(element, finalDivClass)) { + return element; + } + + while (element && element !== document) { + if (WinJS.Utilities.hasClass(element, appBarClass)) { + return element; + } + if (WinJS.Utilities.hasClass(element, "win-flyout") + && element != element.winControl._previousFocus) { + var flyoutControl = element.winControl; + // If _previousFocus was in a light dismissable AppBar, then this Flyout is considered of an extension of it and that AppBar will not close. + // Hook up a 'focusout' listener to this Flyout element to make sure that light dismiss AppBars close if focus moves anywhere other than back to an AppBar. + var appBarElement = thisWinUI.AppBar._isAppBarOrChild(flyoutControl._previousFocus); + if (appBarElement) { + flyoutControl.element.addEventListener('focusout', function focusOut(event) { + // Hides any open AppBars if the new activeElement is not in an AppBar. + _hideIfAllAppBarsLostFocus(); + flyoutControl.element.removeEventListener('focusout', focusOut, false); + }, false); + } + return appBarElement; + } + + element = element.parentNode; + } + + return null; + }; + + // Returns true if the element or what had focus before the element (if a Flyout) is either: + // 1) the appBar or subtree + // 2) OR in a flyout spawned by the appBar + // Returns false otherwise. + AppBar._isWithinAppBarOrChild = function (element, appBar) { + if (!element || !appBar) { + return false; + } + if (appBar.contains(element)) { + return true; + } + var flyout = thisWinUI._Overlay._getParentControlUsingClassName(element, "win-flyout"); + return (flyout && appBar.contains(flyout._previousFocus)); + }; + + // Overlay class calls this for global light dismiss events + AppBar._hideLightDismissAppBars = function (event, keyboardInvoked) { + var elements = document.querySelectorAll("." + appBarClass); + var len = elements.length; + var AppBars = []; + for (var i = 0; i < len; i++) { + var AppBar = elements[i].winControl; + if (AppBar && !AppBar.sticky && !AppBar.hidden) { + AppBars.push(AppBar); + } + } + + _hideAllBars(AppBars, keyboardInvoked); + }; + + var appBarSynchronizationPromise = WinJS.Promise.as(); + + // Callback for AppBar Edgy Event Command + AppBar._toggleAppBarEdgy = function (keyboardInvoked) { + var bars = _getDynamicBarsForEdgy(); + + // If they're all visible hide them, otherwise show them all + if (bars._visible && !bars._hidden) { + appBarSynchronizationPromise = appBarSynchronizationPromise.then(function () { + return _hideAllBars(bars, keyboardInvoked); + }); + return "hiding"; + } else { + appBarSynchronizationPromise = appBarSynchronizationPromise.then(function () { + return _showAllBars(bars, keyboardInvoked); + }); + return "showing"; + } + }; + + return AppBar; + }) + }); + +})(WinJS); + + +/// appbar,Flyout,Flyouts,Statics +(function flyoutInit(WinJS) { + "use strict"; + + WinJS.Namespace.define("WinJS.UI", { + /// + /// + /// Displays lightweight UI that is either informational, or requires user interaction. + /// Unlike a dialog, a Flyout can be light dismissed by clicking or tapping off of it. + /// + /// + /// + /// Flyout + /// + /// + /// ]]> + /// Raised just before showing a flyout. + /// Raised immediately after a flyout is fully shown. + /// Raised just before hiding a flyout. + /// Raised immediately after a flyout is fully hidden. + /// The Flyout control itself. + /// + /// + /// + Flyout: WinJS.Namespace._lazy(function () { + var thisWinUI = WinJS.UI; + + // Class Names + var appBarCommandClass = "win-command"; + var flyoutClass = "win-flyout"; + var flyoutLightClass = "win-ui-light"; + var menuClass = "win-menu"; + var scrollsClass = "win-scrolls"; + + var finalDivClass = "win-finaldiv"; + var firstDivClass = "win-firstdiv"; + + function getDimension(element, property) { + return parseFloat(element, window.getComputedStyle(element, null)[property]); + } + + var strings = { + get ariaLabel() { return WinJS.Resources._getWinJSString("ui/flyoutAriaLabel").value; }, + get noAnchor() { return WinJS.Resources._getWinJSString("ui/noAnchor").value; }, + get badPlacement() { return WinJS.Resources._getWinJSString("ui/badPlacement").value; }, + get badAlignment() { return WinJS.Resources._getWinJSString("ui/badAlignment").value; } + }; + + var Flyout = WinJS.Class.derive(WinJS.UI._Overlay, function Flyout_ctor(element, options) { + /// + /// + /// Creates a new Flyout control. + /// + /// + /// The DOM element that hosts the control. + /// + /// + /// The set of properties and values to apply to the new Flyout. + /// + /// The new Flyout control. + /// + /// + + // Simplify checking later + options = options || {}; + + // Make sure there's an input element + this._element = element || document.createElement("div"); + this._id = this._element.id || this._element.uniqueID; + this._writeProfilerMark("constructor,StartTM"); + + this._baseFlyoutConstructor(this._element, options); + + var _elms = this._element.getElementsByTagName("*"); + var firstDiv = this._addFirstDiv(); + firstDiv.tabIndex = WinJS.Utilities._getLowestTabIndexInList(_elms); + var finalDiv = this._addFinalDiv(); + finalDiv.tabIndex = WinJS.Utilities._getHighestTabIndexInList(_elms); + + // Handle "esc" & "tab" key presses + this._element.addEventListener("keydown", this._handleKeyDown, true); + + this._writeProfilerMark("constructor,StopTM"); + return this; + }, { + _lastMaxHeight: null, + + _baseFlyoutConstructor: function Flyout_baseFlyoutContstructor(element, options) { + // Flyout constructor + + // We have some options with defaults + this._placement = "auto"; + this._alignment = "center"; + + // Call the base overlay constructor helper + this._baseOverlayConstructor(element, options); + + // Make a click eating div + thisWinUI._Overlay._createClickEatingDivFlyout(); + + // Start flyouts hidden + this._element.style.visibilty = "hidden"; + this._element.style.display = "none"; + + // Attach our css class + WinJS.Utilities.addClass(this._element, flyoutClass); + WinJS.Utilities.addClass(this._element, flyoutLightClass); + + // Make sure we have an ARIA role + var role = this._element.getAttribute("role"); + if (role === null || role === "" || role === undefined) { + if (WinJS.Utilities.hasClass(this._element, menuClass)) { + this._element.setAttribute("role", "menu"); + } else { + this._element.setAttribute("role", "dialog"); + } + } + var label = this._element.getAttribute("aria-label"); + if (label === null || label === "" || label === undefined) { + this._element.setAttribute("aria-label", strings.ariaLabel); + } + + // Base animation is popIn, but our flyout has different arguments + this._currentAnimateIn = this._flyoutAnimateIn; + this._currentAnimateOut = this._flyoutAnimateOut; + + // Make sure _Overlay event handlers are hooked up + this._addOverlayEventHandlers(true); + }, + + /// + /// Gets or sets the Flyout control's anchor. The anchor element is the HTML element which the Flyout originates from and is positioned relative to. + /// (This setting can be overridden when you call the show method.) + /// + /// + anchor: { + get: function () { + return this._anchor; + }, + set: function (value) { + this._anchor = value; + } + }, + + /// + /// Gets or sets the default placement of this Flyout. (This setting can be overridden when you call the show method.) + /// + /// + placement: { + get: function () { + return this._placement; + }, + set: function (value) { + if (value !== "top" && value !== "bottom" && value !== "left" && value !== "right" && value !== "auto") { + // Not a legal placement value + throw new WinJS.ErrorFromName("WinJS.UI.Flyout.BadPlacement", strings.badPlacement); + } + this._placement = value; + } + }, + + /// + /// Gets or sets the default alignment for this Flyout. (This setting can be overridden when you call the show method.) + /// + /// + alignment: { + get: function () { + return this._alignment; + }, + set: function (value) { + if (value !== "right" && value !== "left" && value !== "center") { + // Not a legal alignment value + throw new WinJS.ErrorFromName("WinJS.UI.Flyout.BadAlignment", strings.badAlignment); + } + this._alignment = value; + } + }, + + _dispose: function Flyout_dispose() { + WinJS.Utilities.disposeSubTree(this.element); + this._hide(); + this.anchor = null; + }, + + show: function (anchor, placement, alignment) { + /// + /// + /// Shows the Flyout, if hidden, regardless of other states. + /// + /// + /// The DOM element, or ID of a DOM element to anchor the Flyout, overriding the anchor property for this time only. + /// + /// + /// The placement of the Flyout to the anchor: 'auto' (default), 'top', 'bottom', 'left', or 'right'. This parameter overrides the placement property for this show only. + /// + /// + /// For 'top' or 'bottom' placement, the alignment of the Flyout to the anchor's edge: 'center' (default), 'left', or 'right'. + /// This parameter overrides the alignment property for this show only. + /// + /// + /// + this._writeProfilerMark("show,StartTM"); // The corresponding "stop" profiler mark is handled in _Overlay._baseEndShow(). + // Just call private version to make appbar flags happy + this._show(anchor, placement, alignment); + }, + + _show: function Flyout_show(anchor, placement, alignment) { + this._baseFlyoutShow(anchor, placement, alignment); + }, + + hide: function () { + /// + /// + /// Hides the Flyout, if visible, regardless of other states. + /// + /// + /// + // Just wrap the private one, turning off keyboard invoked flag + this._writeProfilerMark("hide,StartTM"); // The corresponding "stop" profiler mark is handled in _Overlay._baseEndHide(). + this._keyboardInvoked = false; + this._hide(); + }, + + _hide: function Flyout_hide() { + if (this._baseHide()) { + // Return focus if this or the flyout CED has focus + var active = document.activeElement; + if (this._previousFocus + && active + && (this._element.contains(active) + || WinJS.Utilities.hasClass(active, thisWinUI._Overlay._clickEatingFlyoutClass)) + && this._previousFocus.focus !== undefined) { + + // _isAppBarOrChild may return a CED or sentinal + var appBar = thisWinUI.AppBar._isAppBarOrChild(this._previousFocus); + if (!appBar || (appBar.winControl && !appBar.winControl.hidden && !appBar.winAnimating)) { + // Don't move focus back to a appBar that is hidden + // We cannot rely on element.style.visibility because it will be visible while animating + var role = this._previousFocus.getAttribute("role"); + var fHideRole = thisWinUI._Overlay._keyboardInfo._visible && !this._keyboardWasUp; + if (fHideRole) { + // Convince IHM to dismiss because it only came up after the flyout was up. + // Change aria role and back to get IHM to dismiss. + this._previousFocus.setAttribute("role", ""); + } + + if (this._keyboardInvoked) { + this._previousFocus.focus(); + } else { + thisWinUI._Overlay._trySetActive(this._previousFocus); + } + active = document.activeElement; + + if (fHideRole) { + // Restore the role so that css is applied correctly + var previousFocus = this._previousFocus; + if (previousFocus) { + setImmediate(function () { + previousFocus.setAttribute("role", role); + }); + } + } + } + + // If the anchor gained focus we want to hide the focus in the non-keyboarding scenario + if (!this._keyboardInvoked && (this._previousFocus === active) && appBar && active) { + thisWinUI._Overlay._addHideFocusClass(active); + } + } + + this._previousFocus = null; + + // Need click-eating div to be hidden if there are no other visible flyouts + if (!this._isThereVisibleFlyout()) { + thisWinUI._Overlay._hideClickEatingDivFlyout(); + } + } + }, + + _baseFlyoutShow: function Flyout_baseFlyoutShow(anchor, placement, alignment) { + // Don't do anything if disabled + if (this.disabled) { + return; + } + // Pick up defaults + if (!anchor) { + anchor = this._anchor; + } + if (!placement) { + placement = this._placement; + } + if (!alignment) { + alignment = this._alignment; + } + + // Dereference the anchor if necessary + if (typeof anchor === "string") { + anchor = document.getElementById(anchor); + } else if (anchor && anchor.element) { + anchor = anchor.element; + } + + // We expect an anchor + if (!anchor) { + // If we have _nextLeft, etc., then we were continuing an old animation, so that's OK + if (!this._retryLast) { + throw new WinJS.ErrorFromName("WinJS.UI.Flyout.NoAnchor", strings.noAnchor); + } + // Last call was incomplete, so use the previous _current values. + this._retryLast = null; + } else { + // Remember the anchor so that if we lose focus we can go back + this._currentAnchor = anchor; + // Remember current values + this._currentPlacement = placement; + this._currentAlignment = alignment; + } + + // Need click-eating div to be visible, no matter what + if (!this._sticky) { + thisWinUI._Overlay._showClickEatingDivFlyout(); + } + + // If we're animating (eg baseShow is going to fail), then don't mess up our current state. + // Queue us up to wait for current animation to finish first. + if (this._element.winAnimating) { + this._doNext = "show"; + this._retryLast = true; + return; + } + + // We call our base _baseShow to handle the actual animation + if (this._baseShow()) { + // (_baseShow shouldn't ever fail because we tested winAnimating above). + if (!WinJS.Utilities.hasClass(this.element, "win-menu")) { + // Verify that the firstDiv is in the correct location. + // Move it to the correct location or add it if not. + var _elms = this._element.getElementsByTagName("*"); + var firstDiv = this.element.querySelectorAll(".win-first"); + if (this.element.children.length && !WinJS.Utilities.hasClass(this.element.children[0], firstDivClass)) { + if (firstDiv && firstDiv.length > 0) { + firstDiv.item(0).parentNode.removeChild(firstDiv.item(0)); + } + + firstDiv = this._addFirstDiv(); + } + firstDiv.tabIndex = WinJS.Utilities._getLowestTabIndexInList(_elms); + + // Verify that the finalDiv is in the correct location. + // Move it to the correct location or add it if not. + var finalDiv = this.element.querySelectorAll(".win-final"); + if (!WinJS.Utilities.hasClass(this.element.children[this.element.children.length - 1], finalDivClass)) { + if (finalDiv && finalDiv.length > 0) { + finalDiv.item(0).parentNode.removeChild(finalDiv.item(0)); + } + + finalDiv = this._addFinalDiv(); + } + finalDiv.tabIndex = WinJS.Utilities._getHighestTabIndexInList(_elms); + } + + // Hide all other flyouts + this._hideAllOtherFlyouts(this); + + // Store what had focus before showing the Flyout. + // This must happen after we hide all other flyouts so that we store the correct element. + this._previousFocus = document.activeElement; + } + }, + + _endShow: function Flyout_endShow() { + // Remember if the IHM was up since we may need to hide it when the flyout hides. + // This check needs to happen after the IHM has a chance to hide itself after we force hide + // all other visible Flyouts. + this._keyboardWasUp = thisWinUI._Overlay._keyboardInfo._visible; + + if (!WinJS.Utilities.hasClass(this.element, "win-menu")) { + // Put focus on the first child in the Flyout + this._focusOnFirstFocusableElementOrThis(); + + // Prevent what is gaining focus from showing that it has focus + thisWinUI._Overlay._addHideFocusClass(document.activeElement); + } else { + // Make sure the menu has focus, but don't show a focus rect + thisWinUI._Overlay._trySetActive(this._element); + } + }, + + // Find our new flyout position. + _findPosition: function Flyout_findPosition() { + this._nextHeight = null; + this._keyboardMovedUs = false; + this._hasScrolls = false; + this._keyboardSquishedUs = 0; + + // Make sure menu toggles behave + if (this._checkToggle) { + this._checkToggle(); + } + + // Update margins for this alignment and remove old scrolling + this._updateAdjustments(this._currentAlignment); + + // Set up the new position, and prep the offset for showPopup + this._getTopLeft(); + // Panning top offset is calculated top + this._scrollTop = this._nextTop; + + // Adjust position + if (this._nextTop < 0) { + // Need to attach to bottom + this._element.style.bottom = "0px"; + this._element.style.top = "auto"; + } else { + // Normal, attach to top + this._element.style.top = this._nextTop + "px"; + this._element.style.bottom = "auto"; + } + if (this._nextLeft < 0) { + // Overran right, attach to right + this._element.style.right = "0px"; + this._element.style.left = "auto"; + } else { + // Normal, attach to left + this._element.style.left = this._nextLeft + "px"; + this._element.style.right = "auto"; + } + + // Adjust height/scrollbar + if (this._nextHeight !== null) { + WinJS.Utilities.addClass(this._element, scrollsClass); + this._lastMaxHeight = this._element.style.maxHeight; + this._element.style.maxHeight = this._nextHeight + "px"; + this._nextBottom = this._nextTop + this._nextHeight; + this._hasScrolls = true; + } + + // May need to adjust if the IHM is showing. + if (thisWinUI._Overlay._keyboardInfo._visible) { + // Use keyboard logic + this._checkKeyboardFit(); + + if (this._keyboardMovedUs) { + this._adjustForKeyboard(); + } + } + }, + + // This determines our positioning. We have 5 modes, the 1st four are explicit, the last is automatic: + // * top - position explicitly on the top of the anchor, shrinking and adding scrollbar as needed. + // * bottom - position explicitly below the anchor, shrinking and adding scrollbar as needed. + // * left - position left of the anchor, shrinking and adding a vertical scrollbar as needed. + // * right - position right of the anchor, shrinking and adding a vertical scroolbar as needed. + // * auto - Automatic placement. + // Auto tests the height of the anchor and the flyout. For consistency in orientation, we imagine + // that the anchor is placed in the vertical center of the display. If the flyout would fit above + // that centered anchor, then we will place the flyout vertically in relation to the anchor, otherwise + // placement will be horizontal. + // Vertical auto placement will be positioned on top of the anchor if room, otherwise below the anchor. + // - this is because touch users would be more likely to obscure flyouts below the anchor. + // Horizontal auto placement will be positioned to the left of the anchor if room, otherwise to the right. + // - this is because right handed users would be more likely to obscure a flyout on the right of the anchor. + // Auto placement will add a vertical scrollbar if necessary. + _getTopLeft: function Flyout_getTopLeft() { + var anchorRawRectangle = this._currentAnchor.getBoundingClientRect(), + flyout = {}, + anchor = {}; + + // Adjust for the anchor's margins. + anchor.top = anchorRawRectangle.top; + anchor.bottom = anchorRawRectangle.bottom; + anchor.left = anchorRawRectangle.left; + anchor.right = anchorRawRectangle.right; + anchor.height = anchor.bottom - anchor.top; + anchor.width = anchor.right - anchor.left; + + // Get our flyout and margins, note that getDimension calls + // window.getComputedStyle, which ensures layout is updated. + flyout.marginTop = getDimension(this._element, "marginTop"); + flyout.marginBottom = getDimension(this._element, "marginBottom"); + flyout.marginLeft = getDimension(this._element, "marginLeft"); + flyout.marginRight = getDimension(this._element, "marginRight"); + flyout.width = WinJS.Utilities.getTotalWidth(this._element); + flyout.height = WinJS.Utilities.getTotalHeight(this._element); + flyout.innerWidth = WinJS.Utilities.getContentWidth(this._element); + flyout.innerHeight = WinJS.Utilities.getContentHeight(this._element); + this._nextMarginPadding = (flyout.height - flyout.innerHeight); + + // Check fit for requested this._currentPlacement, doing fallback if necessary + switch (this._currentPlacement) { + case "top": + if (!this._fitTop(anchor, flyout)) { + // Didn't fit, needs scrollbar + this._nextTop = 0; + this._nextHeight = anchor.top - this._nextMarginPadding; + } + this._centerHorizontally(anchor, flyout, this._currentAlignment); + break; + case "bottom": + if (!this._fitBottom(anchor, flyout)) { + // Didn't fit, needs scrollbar + this._nextTop = -1; + this._nextHeight = thisWinUI._Overlay._keyboardInfo._visibleDocHeight - anchor.bottom - this._nextMarginPadding; + } + this._centerHorizontally(anchor, flyout, this._currentAlignment); + break; + case "left": + if (!this._fitLeft(anchor, flyout)) { + // Didn't fit, just shove it to edge + this._nextLeft = 0; + } + this._centerVertically(anchor, flyout); + break; + case "right": + if (!this._fitRight(anchor, flyout)) { + // Didn't fit,just shove it to edge + this._nextLeft = -1; + } + this._centerVertically(anchor, flyout); + break; + case "auto": + // Auto, if the anchor was in the vertical center of the display would we fit above it? + if (this._sometimesFitsAbove(anchor, flyout)) { + // It will fit above or below the anchor + if (!this._fitTop(anchor, flyout)) { + // Didn't fit above (preferred), so go below. + this._fitBottom(anchor, flyout); + } + this._centerHorizontally(anchor, flyout, this._currentAlignment); + } else { + // Won't fit above or below, try a side + if (!this._fitLeft(anchor, flyout) && + !this._fitRight(anchor, flyout)) { + // Didn't fit left or right either, is top or bottom bigger? + if (this._topHasMoreRoom(anchor)) { + // Top, won't fit, needs scrollbar + this._nextTop = 0; + this._nextHeight = anchor.top - this._nextMarginPadding; + } else { + // Bottom, won't fit, needs scrollbar + this._nextTop = -1; + this._nextHeight = thisWinUI._Overlay._keyboardInfo._visibleDocHeight - anchor.bottom - this._nextMarginPadding; + } + this._centerHorizontally(anchor, flyout, this._currentAlignment); + } else { + this._centerVertically(anchor, flyout); + } + } + break; + default: + // Not a legal this._currentPlacement value + throw new WinJS.ErrorFromName("WinJS.UI.Flyout.BadPlacement", strings.badPlacement); + } + + // Remember "bottom" in case we need to consider keyboard later, only tested for top-pinned bars + this._nextBottom = this._nextTop + flyout.height; + }, + + // If the anchor is centered vertically, would the flyout fit above it? + _sometimesFitsAbove: function Flyout_sometimesFitsAbove(anchor, flyout) { + return ((thisWinUI._Overlay._keyboardInfo._visibleDocHeight - anchor.height) / 2) >= flyout.height; + }, + + _topHasMoreRoom: function Flyout_topHasMoreRoom(anchor) { + return anchor.top > thisWinUI._Overlay._keyboardInfo._visibleDocHeight - anchor.bottom; + }, + + // See if we can fit in various places, fitting in the main view, + // ignoring viewport changes, like for the IHM. + _fitTop: function Flyout_fitTop(anchor, flyout) { + this._nextTop = anchor.top - flyout.height; + this._nextAnimOffset = { top: "50px", left: "0px", keyframe: "WinJS-showFlyoutTop" }; + return (this._nextTop >= 0 && + this._nextTop + flyout.height <= thisWinUI._Overlay._keyboardInfo._visibleDocBottom); + }, + + _fitBottom: function Flyout_fitBottom(anchor, flyout) { + this._nextTop = anchor.bottom; + this._nextAnimOffset = { top: "-50px", left: "0px", keyframe: "WinJS-showFlyoutBottom" }; + return (this._nextTop >= 0 && + this._nextTop + flyout.height <= thisWinUI._Overlay._keyboardInfo._visibleDocBottom); + }, + + _fitLeft: function Flyout_fitLeft(anchor, flyout) { + this._nextLeft = anchor.left - flyout.width; + this._nextAnimOffset = { top: "0px", left: "50px", keyframe: "WinJS-showFlyoutLeft" }; + return (this._nextLeft >= 0 && this._nextLeft + flyout.width <= thisWinUI._Overlay._keyboardInfo._visualViewportWidth); + }, + + _fitRight: function Flyout_fitRight(anchor, flyout) { + this._nextLeft = anchor.right; + this._nextAnimOffset = { top: "0px", left: "-50px", keyframe: "WinJS-showFlyoutRight" }; + return (this._nextLeft >= 0 && this._nextLeft + flyout.width <= thisWinUI._Overlay._keyboardInfo._visualViewportWidth); + }, + + _centerVertically: function Flyout_centerVertically(anchor, flyout) { + this._nextTop = anchor.top + anchor.height / 2 - flyout.height / 2; + if (this._nextTop < 0) { + this._nextTop = 0; + } else if (this._nextTop + flyout.height >= thisWinUI._Overlay._keyboardInfo._visibleDocBottom) { + // Flag to put on bottom + this._nextTop = -1; + } + }, + + _centerHorizontally: function Flyout_centerHorizontally(anchor, flyout, alignment) { + if (alignment === "center") { + this._nextLeft = anchor.left + anchor.width / 2 - flyout.width / 2; + } else if (alignment === "left") { + this._nextLeft = anchor.left; + } else if (alignment === "right") { + this._nextLeft = anchor.right - flyout.width; + } else { + throw new WinJS.ErrorFromName("WinJS.UI.Flyout.BadAlignment", strings.badAlignment); + } + if (this._nextLeft < 0) { + this._nextLeft = 0; + } else if (this._nextLeft + flyout.width >= document.documentElement.clientWidth) { + // flag to put on right + this._nextLeft = -1; + } + }, + + _updateAdjustments: function Flyout_updateAdjustments(alignment) { + // Move to 0,0 in case it is off screen, so that it lays out at a reasonable size + this._element.style.top = "0px"; + this._element.style.bottom = "auto"; + this._element.style.left = "0px"; + this._element.style.right = "auto"; + + // Scrolling may not be necessary + WinJS.Utilities.removeClass(this._element, scrollsClass); + if (this._lastMaxHeight !== null) { + this._element.style.maxHeight = this._lastMaxHeight; + this._lastMaxHeight = null; + } + // Alignment + if (alignment === "center") { + WinJS.Utilities.removeClass(this._element, "win-leftalign"); + WinJS.Utilities.removeClass(this._element, "win-rightalign"); + } else if (alignment === "left") { + WinJS.Utilities.addClass(this._element, "win-leftalign"); + WinJS.Utilities.removeClass(this._element, "win-rightalign"); + } else if (alignment === "right") { + WinJS.Utilities.addClass(this._element, "win-rightalign"); + WinJS.Utilities.removeClass(this._element, "win-leftalign"); + } + }, + + _showingKeyboard: function Flyout_showingKeyboard(event) { + if (this.hidden) { + return; + } + + // The only way that we can be showing a keyboard when a flyout is up is because the input was + // in the flyout itself, in which case we'll be moving ourselves. There is no practical way + // for the application to override this as the focused element is in our flyout. + event.ensuredFocusedElementInView = true; + + // See if the keyboard is going to force us to move + this._checkKeyboardFit(); + + if (this._keyboardMovedUs) { + // Pop out immediately, then move to new spot + this._element.style.opacity = 0; + var that = this; + setTimeout(function () { that._adjustForKeyboard(); that._baseAnimateIn(); }, thisWinUI._Overlay._keyboardInfo._animationShowLength); + } + }, + + _resize: function Flyout_resize(event) { + // If hidden and not busy animating, then nothing to do + if (this.hidden && !this._animating) { + return; + } + + // This should only happen if the IHM is dismissing, + // the only other way is for viewstate changes, which + // would dismiss any flyout. + if (this._keyboardHiding) { + // Hiding keyboard, update our position, giving the anchor a chance to update first. + var that = this; + setImmediate(function () { that._findPosition(); }); + this._keyboardHiding = false; + } + }, + + _checkKeyboardFit: function Flyout_checkKeyboardFit() { + // Check for moving to fit keyboard: + // - Too Tall, above top, or below bottom. + var height = WinJS.Utilities.getTotalHeight(this._element); + var viewportHeight = thisWinUI._Overlay._keyboardInfo._visibleDocHeight - this._nextMarginPadding; + if (height > viewportHeight) { + // Too Tall, pin to top with max height + this._keyboardMovedUs = true; + this._scrollTop = 0; + this._keyboardSquishedUs = viewportHeight; + } else if (this._nextTop === -1) { + // Pinned to bottom counts as moved + this._keyboardMovedUs = true; + } else if (this._nextTop < 0) { + // Above the top of the viewport + this._scrollTop = 0; + this._keyboardMovedUs = true; + } else if (this._nextBottom > thisWinUI._Overlay._keyboardInfo._visibleDocBottom) { + // Below the bottom of the viewport + this._scrollTop = -1; + this._keyboardMovedUs = true; + } + }, + + _adjustForKeyboard: function Flyout_adjustForKeyboard() { + // Keyboard moved us, update our metrics as needed + if (this._keyboardSquishedUs) { + // Add scrollbar if we didn't already have scrollsClass + if (!this._hasScrolls) { + WinJS.Utilities.addClass(this._element, scrollsClass); + this._lastMaxHeight = this._element.style.maxHeight; + } + // Adjust height + this._element.style.maxHeight = this._keyboardSquishedUs + "px"; + } + + // Update top/bottom + this._checkScrollPosition(true); + }, + + _hidingKeyboard: function Flyout_hidingKeyboard(event) { + // If we aren't visible and not animating, or haven't been repositioned, then nothing to do + // We don't know if the keyboard moved the anchor, so _keyboardMovedUs doesn't help here + if (this.hidden && !this._animating) { + return; + } + + // Snap to the final position + // We'll either just reveal the current space or resize the window + if (thisWinUI._Overlay._keyboardInfo._isResized) { + // Flag resize that we'll need an updated position + this._keyboardHiding = true; + } else { + // Not resized, update our final position, giving the anchor a chance to update first. + var that = this; + setImmediate(function () { that._findPosition(); }); + } + }, + + _checkScrollPosition: function Flyout_checkScrollPosition(showing) { + if (this.hidden && !showing) { + return; + } + + // May need to adjust top by viewport offset + if (this._scrollTop < 0) { + // Need to attach to bottom + this._element.style.bottom = thisWinUI._Overlay._keyboardInfo._visibleDocBottomOffset + "px"; + this._element.style.top = "auto"; + } else { + // Normal, attach to top + this._element.style.top = "0px"; + this._element.style.bottom = "auto"; + } + }, + + // AppBar flyout animations + _flyoutAnimateIn: function Flyout_flyoutAnimateIn() { + if (this._keyboardMovedUs) { + return this._baseAnimateIn(); + } else { + this._element.style.opacity = 1; + this._element.style.visibility = "visible"; + return WinJS.UI.Animation.showPopup(this._element, this._nextAnimOffset); + } + }, + + _flyoutAnimateOut: function Flyout_flyoutAnimateOut() { + if (this._keyboardMovedUs) { + return this._baseAnimateOut(); + } else { + this._element.style.opacity = 0; + return WinJS.UI.Animation.hidePopup(this._element, this._nextAnimOffset); + } + }, + + // Hide all other flyouts besides this one + _hideAllOtherFlyouts: function Flyout_hideAllOtherFlyouts(thisFlyout) { + var flyouts = document.querySelectorAll(".win-flyout"); + for (var i = 0; i < flyouts.length; i++) { + var flyoutControl = flyouts[i].winControl; + if (flyoutControl && !flyoutControl.hidden && (flyoutControl !== thisFlyout)) { + flyoutControl.hide(); + } + } + }, + + // Returns true if there is a flyout in the DOM that is not hidden + _isThereVisibleFlyout: function Flyout_isThereVisibleFlyout() { + var flyouts = document.querySelectorAll(".win-flyout"); + for (var i = 0; i < flyouts.length; i++) { + var flyoutControl = flyouts[i].winControl; + if (flyoutControl && !flyoutControl.hidden) { + return true; + } + } + + return false; + }, + + _handleKeyDown: function Flyout_handleKeyDown(event) { + // Escape closes flyouts but if the user has a text box with an IME candidate + // window open, we want to skip the ESC key event since it is handled by the IME. + // When the IME handles a key it sets event.keyCode to 229 for an easy check. + if (event.key === "Esc" && event.keyCode !== 229) { + // Show a focus rect on what we move focus to + event.preventDefault(); + event.stopPropagation(); + this.winControl._keyboardInvoked = true; + this.winControl._hide(); + } else if ((event.key === "Spacebar" || event.key === "Enter") + && (this === document.activeElement)) { + event.preventDefault(); + event.stopPropagation(); + this.winControl.hide(); + } else if (event.shiftKey && event.key === "Tab" + && this === document.activeElement + && !event.altKey && !event.ctrlKey && !event.metaKey) { + event.preventDefault(); + event.stopPropagation(); + this.winControl._focusOnLastFocusableElementOrThis(); + } + }, + + // Create and add a new first div as the first child + _addFirstDiv: function Flyout_addFirstDiv() { + var firstDiv = document.createElement("div"); + firstDiv.className = firstDivClass; + firstDiv.style.display = "inline"; + firstDiv.setAttribute("role", "menuitem"); + firstDiv.setAttribute("aria-hidden", "true"); + + this._element.insertAdjacentElement("AfterBegin", firstDiv); + var that = this; + firstDiv.addEventListener("focus", function () { that._focusOnLastFocusableElementOrThis(); }, false); + + return firstDiv; + }, + + // Create and add a new final div as the last child + _addFinalDiv: function Flyout_addFinalDiv() { + var finalDiv = document.createElement("div"); + finalDiv.className = finalDivClass; + finalDiv.style.display = "inline"; + finalDiv.setAttribute("role", "menuitem"); + finalDiv.setAttribute("aria-hidden", "true"); + + this._element.appendChild(finalDiv); + var that = this; + finalDiv.addEventListener("focus", function () { that._focusOnFirstFocusableElementOrThis(); }, false); + + return finalDiv; + }, + + _writeProfilerMark: function Flyout_writeProfilerMark(text) { + msWriteProfilerMark("WinJS.UI.Flyout:" + this._id + ":" + text); + } + }); + return Flyout; + }) + }); + +})(WinJS); + +// Menu +/// Menu,Menus,Flyout,Flyouts,Statics +(function menuInit(WinJS) { + "use strict"; + + WinJS.Namespace.define("WinJS.UI", { + /// + /// Represents a menu flyout for displaying commands. + /// + /// + /// Menu + /// + /// + /// + /// + /// ]]> + /// Raised just before showing a menu. + /// Raised immediately after a menu is fully shown. + /// Raised just before hiding a menu. + /// Raised immediately after a menu is fully hidden. + /// The Menu control itself + /// + /// + /// + Menu: WinJS.Namespace._lazy(function () { + var thisWinUI = WinJS.UI; + + // Class Names + var menuClass = "win-menu"; + var menuToggleClass = "win-menu-toggle"; + + var strings = { + get ariaLabel() { return WinJS.Resources._getWinJSString("ui/menuAriaLabel").value; }, + get requiresCommands() { return WinJS.Resources._getWinJSString("ui/requiresCommands").value; }, + get nullCommand() { return WinJS.Resources._getWinJSString("ui/nullCommand").value; }, + }; + + var Menu = WinJS.Class.derive(WinJS.UI.Flyout, function Menu_ctor(element, options) { + /// + /// + /// Creates a new Menu control. + /// + /// + /// The DOM element that will host the control. + /// + /// + /// The set of properties and values to apply to the control. + /// + /// The new Menu control. + /// + /// + + // We need to be built on top of a Flyout, so stomp on the user's input + options = options || {}; + + // Make sure there's an input element + this._element = element || document.createElement("div"); + this._id = this._element.id || this._element.uniqueID; + this._writeProfilerMark("constructor,StartTM"); + + // validate that if they didn't set commands, in which + // case any HTML only contains commands. Do this first + // so that we don't leave partial Menus in the DOM. + if (!options.commands && this._element) { + // Shallow copy object so we can modify it. + options = WinJS.Utilities._shallowCopy(options); + options.commands = this._verifyCommandsOnly(this._element, "WinJS.UI.MenuCommand"); + } + + // Remember aria role in case base constructor changes it + var role = this._element ? this._element.getAttribute("role") : null; + var label = this._element ? this._element.getAttribute("aria-label") : null; + + // Call the base overlay constructor helper + this._baseFlyoutConstructor(this._element, options); + + // Make sure we have an ARIA role + if (role === null || role === "" || role === undefined) { + this._element.setAttribute("role", "menu"); + } + if (label === null || label === "" || label === undefined) { + this._element.setAttribute("aria-label", strings.ariaLabel); + } + + // Handle "esc" & "up/down" key presses + this._element.addEventListener("keydown", this._handleKeyDown, true); + + // Attach our css class + WinJS.Utilities.addClass(this._element, menuClass); + + // Need to set our commands, making sure we're hidden first + this.hide(); + this._writeProfilerMark("constructor,StopTM"); + }, { + // Public Properties + + /// + /// Sets the MenuCommand objects that appear in the Menu. You can set this to a single MenuCommand or an array of MenuCommand objects. + /// + /// + commands: { + set: function (value) { + // Fail if trying to set when visible + if (!this.hidden) { + throw new WinJS.ErrorFromName("WinJS.UI.Menu.CannotChangeCommandsWhenVisible", WinJS.Resources._formatString(thisWinUI._Overlay.commonstrings.cannotChangeCommandsWhenVisible, "Menu")); + } + + // Start from scratch + WinJS.Utilities.empty(this._element); + + // In case they had only one... + if (!Array.isArray(value)) { + value = [value]; + } + + // Add commands + var len = value.length; + for (var i = 0; i < len; i++) { + this._addCommand(value[i]); + } + } + }, + + getCommandById: function (id) { + /// + /// + /// Retrieve the command with the specified ID from this Menu. If more than one command is found, all are returned. + /// + /// The ID of the command to find. + /// + /// The command found, an array of commands if more than one have the same ID, or null if no command is found. + /// + /// + /// + var commands = this.element.querySelectorAll("#" + id); + for (var count = 0; count < commands.length; count++) { + // Any elements we generated this should succeed for, + // but remove missing ones just to be safe. + commands[count] = commands[count].winControl; + if (!commands[count]) { + commands.splice(count, 1); + } + } + + if (commands.length === 1) { + return commands[0]; + } else if (commands.length === 0) { + return null; + } + + return commands; + }, + + + showCommands: function (commands) { + /// + /// + /// Shows the specified commands of the Menu. + /// + /// + /// The commands to show. The array elements may be Menu objects, or the string identifiers (IDs) of commands. + /// + /// + /// + if (!commands) { + throw new WinJS.ErrorFromName("WinJS.UI.Menu.RequiresCommands", strings.requiresCommands); + } + + this._showCommands(commands, true); + }, + + hideCommands: function (commands) { + /// + /// + /// Hides the Menu. + /// + /// + /// Required. Command or Commands to hide, either String, DOM elements, or WinJS objects. + /// + /// + /// + if (!commands) { + throw new WinJS.ErrorFromName("WinJS.UI.Menu.RequiresCommands", strings.requiresCommands); + } + + this._hideCommands(commands, true); + }, + + showOnlyCommands: function (commands) { + /// + /// + /// Shows the specified commands of the Menu while hiding all other commands. + /// + /// + /// The commands to show. The array elements may be MenuCommand objects, or the string identifiers (IDs) of commands. + /// + /// + /// + if (!commands) { + throw new WinJS.ErrorFromName("WinJS.UI.Menu.RequiresCommands", strings.requiresCommands); + } + + this._showOnlyCommands(commands, true); + }, + + show: function (anchor, placement, alignment) { + /// + /// + /// Shows the Menu, if hidden, regardless of other states. + /// + /// + /// The DOM element, or ID of a DOM element, to anchor the Menu. This parameter overrides the anchor property for this method call only. + /// + /// + /// The placement of the Menu to the anchor: 'auto' (default), 'top', 'bottom', 'left', or 'right'. This parameter overrides the placement + /// property for this method call only. + /// + /// + /// For 'top' or 'bottom' placement, the alignment of the Menu to the anchor's edge: 'center' (default), 'left', or 'right'. This parameter + /// overrides the alignment property for this method call only. + /// + /// + /// + // Just call private version to make appbar flags happy + this._writeProfilerMark("show,StartTM"); // The corresponding "stop" profiler mark is handled in _Overlay._baseEndShow(). + this._show(anchor, placement, alignment); + }, + + _show: function Menu_show(anchor, placement, alignment) { + // Before we show, we also need to check for children flyouts needing anchors + this._checkForFlyoutCommands(); + + // Call flyout show + this._baseFlyoutShow(anchor, placement, alignment); + + // We need to check for toggles after we send the beforeshow event, + // so the developer has a chance to show or hide more commands. + // Flyout's _findPosition will make that call. + }, + + _addCommand: function Menu_addCommand(command) { + if (!command) { + throw new WinJS.ErrorFromName("WinJS.UI.Menu.NullCommand", strings.nullCommand); + } + // See if it's a command already + if (!command._element) { + // Not a command, so assume it's options for a command + command = new WinJS.UI.MenuCommand(null, command); + } + // If we were attached somewhere else, detach us + if (command._element.parentElement) { + command._element.parentElement.removeChild(command._element); + } + + // Reattach us + this._element.appendChild(command._element); + }, + + // Called by flyout's _findPosition so that application can update it status + // we do the test and we can then fix this last-minute before showing. + _checkToggle: function Menu_checkToggle() { + var toggles = this._element.querySelectorAll(".win-command[aria-checked]"); + var hasToggle = false; + if (toggles) { + for (var i = 0; i < toggles.length; i++) { + if (toggles[i] && toggles[i].winControl && !toggles[i].winControl.hidden) { + // Found a visible toggle control + hasToggle = true; + break; + } + } + } + if (hasToggle) { + WinJS.Utilities.addClass(this._element, menuToggleClass); + } else { + WinJS.Utilities.removeClass(this._element, menuToggleClass); + } + }, + + _checkForFlyoutCommands: function Menu_checkForFlyoutCommands() { + var commands = this._element.querySelectorAll(".win-command"); + for (var count = 0; count < commands.length; count++) { + if (commands[count].winControl) { + // Remember our anchor in case it's a flyout + commands[count].winControl._parentFlyout = this; + } + } + }, + + _handleKeyDown: function Menu_handleKeyDown(event) { + if (event.key === "Esc") { + // Show a focus rect on what we move focus to + this.winControl._keyboardInvoked = true; + this.winControl._hide(); + } else if ((event.key === "Spacebar" || event.key === "Enter") + && (this === document.activeElement)) { + event.preventDefault(); + this.winControl.hide(); + } else if (event.key === "Up") { + var that = this; + thisWinUI.Menu._focusOnPreviousElement(that); + + // Prevent the page from scrolling + event.preventDefault(); + } else if (event.key === "Down") { + that = this; + thisWinUI.Menu._focusOnNextElement(that); + + // Prevent the page from scrolling + event.preventDefault(); + } else if (event.key === "Tab") { + event.preventDefault(); + } + }, + + _writeProfilerMark: function Menu_writeProfilerMark(text) { + msWriteProfilerMark("WinJS.UI.Menu:" + this._id + ":" + text); + } + }); + + // Statics + + // Set focus to next focusable element in the menu (loop if necessary). + // Note: The loop works by first setting focus to the menu itself. If the menu is + // what had focus before, then we break. Otherwise we try the first child next. + // Focus remains on the menu if nothing is focusable. + Menu._focusOnNextElement = function (menu) { + var _currentElement = document.activeElement; + + do { + if (_currentElement === menu) { + _currentElement = _currentElement.firstElementChild; + } else { + _currentElement = _currentElement.nextElementSibling; + } + + if (_currentElement) { + _currentElement.focus(); + } else { + _currentElement = menu; + } + + } while (_currentElement !== document.activeElement) + }; + + // Set focus to previous focusable element in the menu (loop if necessary). + // Note: The loop works by first setting focus to the menu itself. If the menu is + // what had focus before, then we break. Otherwise we try the last child next. + // Focus remains on the menu if nothing is focusable. + Menu._focusOnPreviousElement = function (menu) { + var _currentElement = document.activeElement; + + do { + if (_currentElement === menu) { + _currentElement = _currentElement.lastElementChild; + } else { + _currentElement = _currentElement.previousElementSibling; + } + + if (_currentElement) { + _currentElement.focus(); + } else { + _currentElement = menu; + } + + } while (_currentElement !== document.activeElement) + }; + + return Menu; + }) + }); + +})(WinJS); + + +// Menu Command +/// appbar,appbars,Flyout,Flyouts,onclick,Statics +(function menuCommandInit(WinJS) { + "use strict"; + + WinJS.Namespace.define("WinJS.UI", { + /// + /// + /// Represents a command to be displayed in a Menu. MenuCommand objects provide button, toggle button, flyout button, + /// or separator functionality for Menu controls. + /// + /// + /// + /// + /// + /// ]]> + /// The MenuCommand control itself + /// + /// + /// + MenuCommand: WinJS.Namespace._lazy(function () { + var thisWinUI = WinJS.UI; + + // Class Names + var menuCommandClass = "win-command"; + var typeSeparator = "separator"; + var typeButton = "button"; + var typeToggle = "toggle"; + var typeFlyout = "flyout"; + + function _handleMenuClick(event) { + var command = this.winControl; + if (command) { + var hideParent = true; + if (command._type === typeToggle) { + command.selected = !command.selected; + } else if (command._type === typeFlyout && command._flyout) { + var flyout = command._flyout; + // Flyout may not have processAll'd, so this may be a DOM object + if (typeof flyout === "string") { + flyout = document.getElementById(flyout); + } + if (!flyout.show) { + flyout = flyout.winControl; + } + if (flyout && flyout.show) { + if (command._parentFlyout) { + hideParent = false; + flyout.show(command._parentFlyout._currentAnchor, command._parentFlyout._currentPlacement, command._parentFlyout._currentAlignment); + } else { + flyout.show(this); + } + } + } + if (command.onclick) { + command.onclick(event); + } + // Dismiss parent flyout + if (hideParent && command._parentFlyout) { + command._parentFlyout.hide(); + } + } + } + + function _handleMouseOver(event) { + if (this && this.focus) { + this.focus(); + + this.addEventListener("mousemove", _handleMouseMove, false); + } + } + + function _handleMouseMove(event) { + if (this && this.focus && this !== document.activeElement) { + this.focus(); + } + } + + function _handleMouseOut(event) { + var that = this; + var parentFlyout = _getParentFlyout(that); + if (parentFlyout + && this === document.activeElement + && WinJS.Utilities.hasClass(parentFlyout, "win-menu") + && parentFlyout.focus) { + // Menu gives focus to the menu itself + parentFlyout.focus(); + } else if (parentFlyout + && this === document.activeElement + && parentFlyout.children + && parentFlyout.children.length > 0 + && parentFlyout.children[0] + && WinJS.Utilities.hasClass(parentFlyout.children[0], "win-firstdiv") + && parentFlyout.children[0].focus) { + // Flyout gives focus to firstDiv + parentFlyout.children[0].focus(); + } + + this.removeEventListener("mousemove", _handleMouseMove, false); + } + + function _getParentFlyout(element) { + while (element && !WinJS.Utilities.hasClass(element, "win-flyout")) { + element = element.parentElement; + } + + return element; + } + + var strings = { + get ariaLabel() { return WinJS.Resources._getWinJSString("ui/menuCommandAriaLabel").value; }, + get duplicateConstruction() { return WinJS.Resources._getWinJSString("ui/duplicateConstruction").value; }, + get badClick() { return WinJS.Resources._getWinJSString("ui/badClick").value; }, + get badHrElement() { return WinJS.Resources._getWinJSString("ui/badHrElement").value; }, + get badButtonElement() { return WinJS.Resources._getWinJSString("ui/badButtonElement").value; } + }; + + return WinJS.Class.define(function MenuCommand_ctor(element, options) { + /// + /// + /// Creates a new MenuCommand object. + /// + /// + /// The DOM element that will host the control. + /// + /// + /// The set of properties and values to apply to the new MenuCommand. + /// + /// + /// A MenuCommand control. + /// + /// + /// + + // Check to make sure we weren't duplicated + if (element && element.winControl) { + throw new WinJS.ErrorFromName("WinJS.UI.MenuCommand.DuplicateConstruction", strings.duplicateConstruction); + } + + this._disposed = false; + + // Don't blow up if they didn't pass options + if (!options) { + options = {}; + } + + // Need a type before we can create our element + if (!options.type) { + this._type = typeButton; + } + + // Go ahead and create it, separator types look different than buttons + // Don't forget to use passed in element if one was provided. + this._element = element; + if (options.type === typeSeparator) { + this._createSeparator(); + } else { + // This will also set the icon & label + this._createButton(); + } + WinJS.Utilities.addClass(this._element, "win-disposable"); + + // Remember ourselves + this._element.winControl = this; + + // Attach our css class + WinJS.Utilities.addClass(this._element, menuCommandClass); + + if (!options.selected && options.type === typeToggle) { + // Make sure toggle's have selected false for CSS + this.selected = false; + } + if (options.onclick) { + this.onclick = options.onclick; + } + options.onclick = _handleMenuClick; + + WinJS.UI.setOptions(this, options); + + // Set our options + if (this._type !== typeSeparator) { + // Make sure we have an ARIA role + var role = this._element.getAttribute("role"); + if (role === null || role === "" || role === undefined) { + role = "menuitem"; + if (this._type === typeToggle) { + role = "menuitemcheckbox"; + } + this._element.setAttribute("role", role); + if (this._type === typeFlyout) { + this._element.setAttribute("aria-haspopup", true); + } + } + var label = this._element.getAttribute("aria-label"); + if (label === null || label === "" || label === undefined) { + this._element.setAttribute("aria-label", strings.ariaLabel); + } + } + + this._element.addEventListener("mouseover", _handleMouseOver, false); + this._element.addEventListener("mouseout", _handleMouseOut, false); + }, { + /// + /// Gets the ID of the MenuCommand. + /// + /// + id: { + get: function () { + return this._element.id; + }, + set: function (value) { + // we allow setting first time only. otherwise we ignore it. + if (!this._element.id) { + this._element.id = value; + } + } + }, + + /// + /// Gets the type of the MenuCommand. Possible values are "button", "toggle", "flyout", or "separator". + /// + /// + type: { + get: function () { + return this._type; + }, + set: function (value) { + // we allow setting first time only. otherwise we ignore it. + if (!this._type) { + if (value !== typeButton && value !== typeFlyout && value !== typeToggle && value !== typeSeparator) { + this._type = typeButton; + } else { + this._type = value; + } + } + } + }, + + /// + /// The label of the MenuCommand + /// + /// + label: { + get: function () { + return this._label; + }, + set: function (value) { + this._label = value; + this._element.innerText = this.label; + + // Update aria-label + this._element.setAttribute("aria-label", this.label); + } + }, + + /// + /// Gets or sets the function to invoke when the command is clicked. + /// + /// + onclick: { + get: function () { + return this._onclick; + }, + set: function (value) { + if (value && typeof value !== "function") { + throw new WinJS.ErrorFromName("WinJS.UI.MenuCommand.BadClick", WinJS.Resources._formatString(strings.badClick, "MenuCommand")); + } + this._onclick = value; + } + }, + + /// + /// For flyout type MenuCommands, this property returns the WinJS.UI.Flyout that this command invokes. When setting this property, you can set + /// it to the string ID of the Flyout, the DOM object that hosts the Flyout, or the Flyout object itself. + /// + /// + flyout: { + get: function () { + // Resolve it to the flyout + var flyout = this._flyout; + if (typeof flyout === "string") { + flyout = document.getElementById(flyout); + } + // If it doesn't have a .element, then we need to getControl on it + if (flyout && !flyout.element) { + flyout = flyout.winControl; + } + + return flyout; + }, + set: function (value) { + // Need to update aria-owns with the new ID. + var id = value; + if (id && typeof id !== "string") { + // Our controls have .element properties + if (id.element) { + id = id.element; + } + // Hope it's a DOM element, get ID from DOM element + if (id) { + if (id.id) { + id = id.id; + } else { + // No id, have to fake one + id.id = id.uniqueID; + id = id.id; + } + } + } + if (typeof id === "string") { + this._element.setAttribute("aria-owns", id); + } + + // Remember it + this._flyout = value; + } + }, + + /// + /// Gets or sets the selected state of a toggle button. This property is true if the toggle button is selected; otherwise, it's false. + /// + /// + selected: { + get: function () { + // Ensure it's a boolean because we're using the DOM element to keep in-sync + return this._element.getAttribute("aria-checked") === "true"; + }, + set: function (value) { + this._element.setAttribute("aria-checked", !!value); + } + }, + + /// + element: { + get: function () { + return this._element; + } + }, + + /// + /// Gets or sets a value that indicates whether the MenuCommand is disabled. This value is true if the MenuCommand is disabled; otherwise, false. + /// + /// + disabled: { + get: function () { + // Ensure it's a boolean because we're using the DOM element to keep in-sync + return !!this._element.disabled; + }, + set: function (value) { + this._element.disabled = !!value; + } + }, + + /// + hidden: { + get: function () { + // Ensure it's a boolean because we're using the DOM element to keep in-sync + return this._element.style.visibility === "hidden"; + }, + set: function (value) { + var menuControl = thisWinUI._Overlay._getParentControlUsingClassName(this._element, "win-menu"); + if (menuControl && !menuControl.hidden) { + throw new WinJS.ErrorFromName("WinJS.UI.MenuCommand.CannotChangeHiddenProperty", WinJS.Resources._formatString(thisWinUI._Overlay.commonstrings.cannotChangeHiddenProperty, "Menu")); + } + + var style = this._element.style; + if (value) { + style.visibility = "hidden"; + style.display = "none"; + } else { + style.visibility = ""; + style.display = "block"; + } + } + }, + + /// + /// Gets or sets the extra CSS class that is applied to the host DOM element. + /// + /// + extraClass: { + get: function () { + return this._extraClass; + }, + set: function (value) { + if (this._extraClass) { + WinJS.Utilities.removeClass(this._element, this._extraClass); + } + this._extraClass = value; + WinJS.Utilities.addClass(this._element, this._extraClass); + } + }, + + + dispose: function () { + /// + /// + /// Disposes this control. + /// + /// + /// + if (this._disposed) { + return; + } + this._disposed = true; + + if (this._flyout) { + this._flyout.dispose(); + } + }, + + addEventListener: function (type, listener, useCapture) { + /// + /// + /// Registers an event handler for the specified event. + /// + /// The name of the event to register. + /// The function that handles the event. + /// + /// Set to true to register the event handler for the capturing phase; otherwise, set to false to register the event handler for the bubbling phase. + /// + /// + /// + return this._element.addEventListener(type, listener, useCapture); + }, + + removeEventListener: function (type, listener, useCapture) { + /// + /// + /// Removes the specified event handler that the addEventListener method registered. + /// + /// The name of the event to remove. + /// The event handler function to remove. + /// + /// Set to true to remove the capturing phase event handler; set to false to remove the bubbling phase event handler. + /// + /// + /// + return this._element.removeEventListener(type, listener, useCapture); + }, + + // Private properties + _createSeparator: function MenuCommand_createSeparator() { + // Make sure there's an input element + if (!this._element) { + this._element = document.createElement("hr"); + } else { + // Verify the input was an hr + if (this._element.tagName !== "HR") { + throw new WinJS.ErrorFromName("WinJS.UI.MenuCommand.BadHrElement", strings.badHrElement); + } + } + }, + + _createButton: function MenuCommand_createButton() { + // Make sure there's an input element + if (!this._element) { + this._element = document.createElement("button"); + } else { + // Verify the input was a button + if (this._element.tagName !== "BUTTON") { + throw new WinJS.ErrorFromName("WinJS.UI.MenuCommand.BadButtonElement", strings.badButtonElement); + } + this._element.innerHTML = ""; + } + + // MenuCommand buttons need to look like this: + //// + this._element.type = "button"; + + // 'innertext' label is added later by caller + } + }); + }) + }); + +})(WinJS); + + +(function searchboxInit(global) { + "use strict"; + + WinJS.Namespace.define("WinJS.UI", { + /// + /// + /// Enables the user to perform search queries and select suggestions. + /// + /// + /// + /// + /// + /// ]]> + /// Raised when user or app changes the query text. + /// Raised when user clicks on search glyph or presses Enter. + /// Raised when user clicks one of the displayed suggestions. + /// Raised when the system requests search suggestions from this app. + /// + /// Raised when the app automatically redirects focus to the search box. This event can only be raised when the focusOnKeyboardInput property is set to true. + /// + /// Styles the entire Search box control. + /// Styles the query input box. + /// Styles the search button. + /// Styles the result suggestions flyout. + /// Styles the result type suggestion. + /// Styles the query type suggestion. + /// + /// Styles the separator type suggestion. + /// + /// + /// Styles the currently selected suggestion. + /// + /// + /// + /// + SearchBox: WinJS.Namespace._lazy(function () { + var utilities = WinJS.Utilities; + var createEvent = WinJS.Utilities._createEventProperty; + + // Enums + var ClassName = { + searchBox: "win-searchbox", + searchBoxInput: "win-searchbox-input", + searchBoxButton: "win-searchbox-button", + searchBoxFlyout: "win-searchbox-flyout", + searchBoxSuggestionResult: "win-searchbox-suggestion-result", + searchBoxSuggestionQuery: "win-searchbox-suggestion-query", + searchBoxSuggestionSeparator: "win-searchbox-suggestion-separator", + searchBoxSuggestionSelected: "win-searchbox-suggestion-selected", + searchBoxFlyoutHighlightText: "win-searchbox-flyout-highlighttext", + searchBoxButtonInputFocus: "win-searchbox-button-input-focus", + searchBoxInputFocus: "win-searchbox-input-focus", + searchBoxSuggestionResultText: "win-searchbox-suggestion-result-text", + searchBoxSuggestionResultDetailedText: "win-searchbox-suggestion-result-detailed-text", + searchboxDisabled: "win-searchbox-disabled", + searchboxHitHighlightSpan: "win-searchbox-hithighlight-span", + }; + + var EventName = { + querychanged: "querychanged", + querysubmitted: "querysubmitted", + resultsuggestionchosen: "resultsuggestionchosen", + suggestionsrequested: "suggestionsrequested", + receivingfocusonkeyboardinput: "receivingfocusonkeyboardinput" + }; + + var SearchSuggestionKind = { + Query: 0, + Result: 1, + Separator: 2 + }; + + var strings = { + get duplicateConstruction() { return WinJS.Resources._getWinJSString("ui/duplicateConstruction").value; }, + get invalidSearchBoxSuggestionKind() { return WinJS.Resources._getWinJSString("ui/invalidSearchBoxSuggestionKind").value; }, + get ariaLabel() { return WinJS.Resources._getWinJSString("ui/searchBoxAriaLabel").value; }, + get ariaLabelInputNoPlaceHolder() { return WinJS.Resources._getWinJSString("ui/searchBoxAriaLabelInputNoPlaceHolder").value; }, + get ariaLabelInputPlaceHolder() { return WinJS.Resources._getWinJSString("ui/searchBoxAriaLabelInputPlaceHolder").value; }, + get ariaLabelButton() { return WinJS.Resources._getWinJSString("ui/searchBoxAriaLabelButton").value; }, + get ariaLabelQuery() { return WinJS.Resources._getWinJSString("ui/searchBoxAriaLabelQuery").value; }, + get ariaLabelSeparator() { return WinJS.Resources._getWinJSString("ui/searchBoxAriaLabelSeparator").value; }, + get ariaLabelResult() { return WinJS.Resources._getWinJSString("ui/searchBoxAriaLabelResult").value; } + }; + + var SearchBox = WinJS.Class.define(function SearchBox_ctor(element, options) { + /// + /// + /// Creates a new SearchBox. + /// + /// + /// The DOM element that hosts the SearchBox. + /// + /// + /// An object that contains one or more property/value pairs to apply to the new control. + /// Each property of the options object corresponds to one of the control's properties or events. + /// Event names must begin with "on". For example, to provide a handler for the querychanged event, + /// add a property named "onquerychanged" to the options object and set its value to the event handler. + /// This parameter is optional. + /// + /// + /// The new SearchBox. + /// + /// + /// + + element = element || document.createElement("div"); + + if (element.winControl) { + throw new WinJS.ErrorFromName("WinJS.UI.SearchBox.DuplicateConstruction", strings.duplicateConstruction); + } + element.winControl = this; + + // Elements + this._domElement = null; + this._inputElement = null; + this._buttonElement = null; + this._flyout = null; + this._flyoutDivElement = null; + this._repeaterDivElement = null; + this._repeater = null; + + // Variables + this._disposed = false; + this._focusOnKeyboardInput = false; + this._chooseSuggestionOnEnter = false; + this._lastKeyPressLanguage = ""; + + // These are used to eliminate redundant query submitted events + this._prevQueryText = ""; + this._prevLinguisticDetails = this._createSearchQueryLinguisticDetails([], 0, 0, "", ""); + this._prevCompositionStart = 0; + this._prevCompositionLength = 0; + this._isProcessingDownKey = false; + this._isProcessingUpKey = false; + this._isProcessingTabKey = false; + this._isProcessingEnterKey = false; + this._isFlyoutPointerDown = false; + this._reflowImeOnPointerRelease = false; + + // Focus and selection related variables + this._currentFocusedIndex = -1; + this._currentSelectedIndex = -1; + + this._suggestionRendererBind = this._suggestionRenderer.bind(this); + this._requestingFocusOnKeyboardInputHandlerBind = this._requestingFocusOnKeyboardInputHandler.bind(this); + this._suggestionsRequestedHandlerBind = this._suggestionsRequestedHandler.bind(this); + this._suggestionsChangedHandlerBind = this._suggestionsChangedHandler.bind(this); + + // Find out if we are in local compartment and if search APIs are available. + this._searchSuggestionManager = null; + this._searchSuggestions = null; + + // Get the search suggestion provider if it is available + if ((window.Windows) && (Windows.ApplicationModel) && (Windows.ApplicationModel.Search) && (Windows.ApplicationModel.Search.Core) && (Windows.ApplicationModel.Search.Core.SearchSuggestionManager)) { + this._searchSuggestionManager = new Windows.ApplicationModel.Search.Core.SearchSuggestionManager(); + this._searchSuggestions = this._searchSuggestionManager.suggestions; + } + + this._hitFinder = null; + this._setElement(element); + WinJS.UI.setOptions(this, options); + this._setAccessibilityProperties(); + WinJS.Utilities.addClass(element, "win-disposable"); + }, { + + /// + element: { + get: function () { + return this._domElement; + } + }, + + /// + /// Gets or sets the placeholder text for the SearchBox. This text is displayed if there is no + /// other text in the input box. + /// + /// + placeholderText: { + get: function () { + return this._inputElement.placeholder; + }, + set: function (value) { + this._inputElement.placeholder = value; + this._updateInputElementAriaLabel(); + } + }, + + /// + /// Gets or sets the query text for the SearchBox. + /// + /// + queryText: { + get: function () { + return this._inputElement.value; + }, + set: function (value) { + this._inputElement.value = value; + } + }, + + /// + /// Gets or sets a value that specifies whether search history is disabled for the SearchBox. The default value is false. + /// + /// + searchHistoryDisabled: { + get: function () { + if (this._searchSuggestionManager) { + return !this._searchSuggestionManager.searchHistoryEnabled; + } else { + return true; + } + }, + set: function (value) { + if (this._searchSuggestionManager) { + this._searchSuggestionManager.searchHistoryEnabled = !value; + } + } + }, + + /// + /// Gets or sets the search history context for the SearchBox. The search history context string is used as a secondary key for storing search history. + /// (The primary key is the AppId.) An app can use the search history context string to store different search histories based on the context of the application. + /// If you don't set this property, the system assumes that all searches in your app occur in the same context. + /// If you update this property while the search pane is open with suggestions showing, the changes won't take effect until the user enters the next character. + /// + /// + searchHistoryContext: { + get: function () { + if (this._searchSuggestionManager) { + return this._searchSuggestionManager.searchHistoryContext; + } else { + return ""; + } + }, + set: function (value) { + if (this._searchSuggestionManager) { + this._searchSuggestionManager.searchHistoryContext = value; + } + } + }, + + /// + /// Enable automatically focusing the search box when the user types into the app window (off by default) While this is enabled, + /// input on the current thread will be intercepted and redirected to the search box. Only textual input will trigger the search box to focus. + /// The caller will continue to receive non-text keys (such as arrows, tab, etc + /// This will also not affect WIN/CTRL/ALT key combinations (except for Ctrl-V for paste). + /// If the client needs more to happen than just set focus in the box (make control visible, etc.), they will need to handle the event. + /// If enabled, the app must be sure to disable this if the user puts focus in some other edit field. + /// + /// + focusOnKeyboardInput: { + get: function () { + return this._focusOnKeyboardInput; + }, + set: function (value) { + if (this._focusOnKeyboardInput && !value) { + if (this._searchSuggestionManager) { + this._searchSuggestionManager.removeEventListener("requestingfocusonkeyboardinput", this._requestingFocusOnKeyboardInputHandlerBind); + } + } else if (!this._focusOnKeyboardInput && !!value) { + if (this._searchSuggestionManager) { + this._searchSuggestionManager.addEventListener("requestingfocusonkeyboardinput", this._requestingFocusOnKeyboardInputHandlerBind); + } + } + this._focusOnKeyboardInput = !!value; + } + }, + + /// + /// Gets or sets whether the first suggestion is chosen when the user presses Enter. + /// When set to true, as the user types in the search box, a focus rectangle is drawn on the first search suggestion + /// (if present and no IME composition in progress). Pressing enter will behave the same as if clicked on the focused suggestion, + /// and the down arrow key press will put real focus to the second suggestion and the up arrow key will remove focus. + /// + /// + chooseSuggestionOnEnter: { + get: function () { + return this._chooseSuggestionOnEnter; + }, + set: function (value) { + this._chooseSuggestionOnEnter = !!value; + this._updateSearchButtonClass(); + } + }, + + /// + /// Gets or sets a value that specifies whether the SearchBox is disabled. + /// + /// + disabled: { + get: function () { + return this._inputElement.disabled; + }, + set: function (value) { + if (this._inputElement.disabled === !!value) { + return; + } + + if (!value) { + // Enable control + this._inputElement.disabled = false; + this._buttonElement.disabled = false; + this._domElement.disabled = false; + utilities.removeClass(this.element, ClassName.searchboxDisabled); + if (document.activeElement === this.element) { + try { + this._inputElement.setActive(); + } catch (e) { + } + } + } else { + // Disable control + if (this._isFlyoutShown) { + this._hideFlyout(); + } + utilities.addClass(this.element, ClassName.searchboxDisabled); + this._inputElement.disabled = true; + this._buttonElement.disabled = true; + this._domElement.disabled = true; + } + } + }, + + // Methods + setLocalContentSuggestionSettings: function SearchBox_setLocalContentSuggestionSettings(settings) { + /// + /// + /// Specifies whether suggestions based on local files are automatically displayed in the search pane, and defines the criteria that + /// the system uses to locate and filter these suggestions. + /// + /// + /// The new settings for local content suggestions. + /// + /// + /// + if (this._searchSuggestionManager) { + this._searchSuggestionManager.setLocalContentSuggestionSettings(settings); + } + }, + + dispose: function SearchBox() { + /// + /// + /// Disposes this control. + /// + /// + /// + if (this._disposed) { + return; + } + + // Cancel pending promises. + if (this._flyoutOpenPromise) { + this._flyoutOpenPromise.cancel(); + } + + // Detach winrt events. + if (this._focusOnKeyboardInput) { + if (this._searchSuggestionManager) { + this._searchSuggestionManager.removeEventListener("requestingfocusonkeyboardinput", this._requestingFocusOnKeyboardInputHandlerBind); + } + } + + if (this._searchSuggestions) { + this._searchSuggestions.removeEventListener("vectorchanged", this._suggestionsChangedHandlerBind); + } + + if (this._searchSuggestionManager) { + this._searchSuggestionManager.removeEventListener("suggestionsrequested", this._suggestionsRequestedHandlerBind); + } + + this._searchSuggestionManager = null; + this._searchSuggestions = null; + this._hitFinder = null; + + this._disposed = true; + + }, + + /// + /// Raised when user or app changes the query text. + /// + /// + onquerychanged: createEvent(EventName.querychanged), + + /// + /// Raised when user clicks on search glyph or presses enter button. + /// + /// + onquerysubmitted: createEvent(EventName.querysubmitted), + + /// + /// Raised when user clicks on one of the suggestions displayed. + /// + /// + onresultsuggestionchosen: createEvent(EventName.resultsuggestionchosen), + + /// + /// Raised when Windows requests search suggestions from the app. + /// + /// + onsuggestionsrequested: createEvent(EventName.suggestionsrequested), + + // Private methods + _isFlyoutShown: function SearchBox_isFlyoutShown() { + return (this._flyoutDivElement.style.display !== "none"); + }, + + _isFlyoutBelow: function SearchBox_isFlyoutBelow() { + if (this._flyoutDivElement.getBoundingClientRect().top > this._inputElement.getBoundingClientRect().top) { + return true; + } + return false; + }, + + _getFlyoutTop: function SearchBox_getFlyoutTop() { + if (this._isFlyoutBelow()) { + return this._inputElement.getBoundingClientRect().bottom; + } + var popupHeight = this._flyoutDivElement.getBoundingClientRect().bottom - this._flyoutDivElement.getBoundingClientRect().top; + return this._inputElement.getBoundingClientRect().top - popupHeight; + }, + + _getFlyoutBottom: function SearchBox_getFlyoutBottom() { + if (this._isFlyoutBelow()) { + var popupHeight = this._flyoutDivElement.getBoundingClientRect().bottom - this._flyoutDivElement.getBoundingClientRect().top; + return this._inputElement.getBoundingClientRect().bottom + popupHeight; + } + return this._inputElement.getBoundingClientRect().top; + }, + + _updateFlyoutTopAndTouchAction: function SearchBox_updateFlyoutTopAndTouchAction() { + var popupHeight = this._flyoutDivElement.getBoundingClientRect().bottom - this._flyoutDivElement.getBoundingClientRect().top; + if (!this._isFlyoutBelow()) { + this._flyoutDivElement.style.top = "-" + popupHeight + "px"; + } + + // ms-scroll-chaining:none will still chain scroll parent element if child div does + // not have a scroll bar. Prevent this by setting and updating touch action + if (this._flyoutDivElement.scrollHeight > popupHeight) { + this._flyoutDivElement.style.touchAction = "pan-y"; + } else { + this._flyoutDivElement.style.touchAction = "none"; + } + }, + + _showFlyout: function SearchBox_showFlyout() { + + if (this._isFlyoutShown()) { + return; + } + + if (this._suggestionsData.length === 0) { + return; + } + + this._flyoutDivElement.style.display = "block"; + + // Display above vs below + var minPopupHeight = this._flyoutDivElement.clientHeight; + if (minPopupHeight < WinJS.UI.SearchBox._Constants.MIN_POPUP_HEIGHT) { + minPopupHeight = WinJS.UI.SearchBox._Constants.MIN_POPUP_HEIGHT; + } + var flyoutRect = this._flyoutDivElement.getBoundingClientRect(); + var searchBoxRect = this.element.getBoundingClientRect(); + var popupHeight = flyoutRect.bottom - flyoutRect.top; + var popupWidth = flyoutRect.right - flyoutRect.left; + var searchBoxWidth = searchBoxRect.right - searchBoxRect.left; + var documentClientHeight = document.documentElement.clientHeight; + var documentClientWidth = document.documentElement.clientWidth; + var searchBoxClientHeight = this.element.clientHeight; + var searchBoxClientLeft = this.element.clientLeft; + + var flyoutBelowSearchBox = true; + if ((searchBoxRect.bottom + minPopupHeight) <= documentClientHeight) { + // There is enough space below. Show below + this._flyoutDivElement.style.top = searchBoxClientHeight + "px"; + } else if ((searchBoxRect.top - minPopupHeight) >= 0) { + // There is enough space above. Show above + this._flyoutDivElement.style.top = "-" + popupHeight + "px"; + flyoutBelowSearchBox = false; + } else { + // Not enough space above or below. Show below. + this._flyoutDivElement.style.top = searchBoxClientHeight + "px"; + } + + // Align left vs right edge + var alignRight; + if (window.getComputedStyle(this._flyoutDivElement).direction === "rtl") { + // RTL: Align to the right edge if there is enough space to the left of the search box's + // right edge, or if there is not enough space to fit the flyout aligned to either edge. + alignRight = ((searchBoxRect.right - popupWidth) >= 0) || ((searchBoxRect.left + popupWidth) > documentClientWidth); + + } else { + // LTR: Align to the right edge if there isn't enough space to the right of the search box's + // left edge, but there is enough space to the left of the search box's right edge. + alignRight = ((searchBoxRect.left + popupWidth) > documentClientWidth) && ((searchBoxRect.right - popupWidth) >= 0); + } + + if (alignRight) { + this._flyoutDivElement.style.left = (searchBoxWidth - popupWidth - searchBoxClientLeft) + "px"; + } else { + this._flyoutDivElement.style.left = "-" + searchBoxClientLeft + "px"; + } + + // ms-scroll-chaining:none will still chain scroll parent element if child div does + // not have a scroll bar. Prevent this by setting and updating touch action + if (this._flyoutDivElement.scrollHeight > popupHeight) { + this._flyoutDivElement.style.touchAction = "pan-y"; + } else { + this._flyoutDivElement.style.touchAction = "none"; + } + + this._addFlyoutIMEPaddingIfRequired(); + + if (this._flyoutOpenPromise) { + this._flyoutOpenPromise.cancel(); + this._flyoutOpenPromise = null; + } + var animationKeyframe = flyoutBelowSearchBox ? "WinJS-flyoutBelowSearchBox-showPopup" : "WinJS-flyoutAboveSearchBox-showPopup"; + this._flyoutOpenPromise = WinJS.UI.Animation.showPopup(this._flyoutDivElement, { top: "0px", left: "0px", keyframe: animationKeyframe }); + }, + + _hideFlyout: function SearchBox_hideFlyout() { + if (this._isFlyoutShown()) { + this._flyoutDivElement.style.display = "none"; + this._updateSearchButtonClass(); + } + }, + + _addNewSpan: function SearchBox_addNewSpan(element, innerText, insertBefore) { + // Adds new span element with specified inner text as child to element, placed before insertBefore + var spanElement = document.createElement("span"); + spanElement.innerText = innerText; + spanElement.setAttribute("aria-hidden", "true"); + utilities.addClass(spanElement, ClassName.searchboxHitHighlightSpan); + element.insertBefore(spanElement, insertBefore) + return spanElement; + }, + + _addHitHighlightedText: function SearchBox_addHitHighlightedText(element, item, text) { + if (text) { + // Remove any existing hit highlighted text spans + utilities.query("." + ClassName.searchboxHitHighlightSpan, element).forEach(function (childElement) { + childElement.parentNode.removeChild(childElement); + }); + + // Insert spans at the front of element + var firstChild = element.firstChild; + + var hitsProvided = item.hits; + if ((!hitsProvided) && (this._hitFinder !== null) && (item.kind !== SearchSuggestionKind.Separator)) { + hitsProvided = this._hitFinder.find(text); + } + + var hits = WinJS.UI.SearchBox._sortAndMergeHits(hitsProvided); + + var lastPosition = 0; + for (var i = 0; i < hits.length; i++) { + var hit = hits[i]; + + // Add previous normal text + this._addNewSpan(element, text.substring(lastPosition, hit.startPosition), firstChild); + + lastPosition = hit.startPosition + hit.length; + + // Add hit highlighted text + var spanHitHighlightedText = this._addNewSpan(element, text.substring(hit.startPosition, lastPosition), firstChild); + utilities.addClass(spanHitHighlightedText, ClassName.searchBoxFlyoutHighlightText); + } + + // Add final normal text + if (lastPosition < text.length) { + this._addNewSpan(element, text.substring(lastPosition), firstChild); + } + } + }, + + _findSuggestionElementIndex: function SearchBox_findSuggestionElementIndex(curElement) { + if (curElement) { + for (var i = 0; i < this._suggestionsData.length; i++) { + if (this._repeater.elementFromIndex(i) === curElement) { + return i; + } + } + } + return -1; + }, + + _isSuggestionSelectable: function SearchBox_isSuggestionSelectable(suggestion) { + return ((suggestion.kind === SearchSuggestionKind.Query) || + (suggestion.kind === SearchSuggestionKind.Result)); + }, + + _findNextSuggestionElementIndex: function SearchBox_findNextSuggestionElementIndex(curIndex) { + // Returns -1 if there are no focusable elements after curIndex + // Returns first element if curIndex < 0 + var startIndex = curIndex + 1; + if (startIndex < 0) { + startIndex = 0; + } + + for (var i = startIndex; i < this._suggestionsData.length; i++) { + if ((this._repeater.elementFromIndex(i)) && (this._isSuggestionSelectable(this._suggestionsData.getAt(i)))) { + return i; + } + } + return -1; + }, + + _findPreviousSuggestionElementIndex: function SearchBox_findPreviousSuggestionElementIndex(curIndex) { + // Returns -1 if there are no focusable elements before curIndex + // Returns last element if curIndex >= suggestionsdata.length + var startIndex = curIndex - 1; + if (startIndex >= this._suggestionsData.length) { + startIndex = this._suggestionsData.length - 1; + } + + for (var i = startIndex; i >= 0; i--) { + if ((this._repeater.elementFromIndex(i)) && (this._isSuggestionSelectable(this._suggestionsData.getAt(i)))) { + return i; + } + } + return -1; + }, + + _trySetFocusOnSuggestionIndex: function SearchBox_trySetFocusOnSuggestionIndex(index) { + try { + this._repeater.elementFromIndex(index).focus(); + } catch (e) { + } + }, + + _updateFakeFocus: function SearchBox_updateFakeFocus() { + var firstElementIndex; + if (this._isFlyoutShown() && (this._chooseSuggestionOnEnter)) { + firstElementIndex = this._findNextSuggestionElementIndex(-1); + } else { + // This will clear the fake focus. + firstElementIndex = -1; + } + + this._selectSuggestionAtIndex(firstElementIndex); + }, + + _updateSearchButtonClass: function SearchBox_updateSearchButtonClass() { + if ((this._currentSelectedIndex !== -1) || (document.activeElement !== this._inputElement)) { + // Focus is not in input. remove class + utilities.removeClass(this._buttonElement, ClassName.searchBoxButtonInputFocus); + } else if (document.activeElement === this._inputElement) { + utilities.addClass(this._buttonElement, ClassName.searchBoxButtonInputFocus); + } + }, + + _selectSuggestionAtIndex: function SearchBox_selectSuggestionAtIndex(indexToSelect) { + // Sets focus on the specified element and removes focus from others. + // Clears selection if index is outside of suggestiondata index range. + var curElement = null; + for (var i = 0; i < this._suggestionsData.length; i++) { + curElement = this._repeater.elementFromIndex(i); + if (i !== indexToSelect) { + utilities.removeClass(curElement, ClassName.searchBoxSuggestionSelected); + curElement.setAttribute("aria-selected", "false"); + } else { + utilities.addClass(curElement, ClassName.searchBoxSuggestionSelected); + this._scrollToView(curElement); + curElement.setAttribute("aria-selected", "true"); + } + } + this._updateSearchButtonClass(); + this._currentSelectedIndex = indexToSelect; + if (curElement) { + this._inputElement.setAttribute("aria-activedescendant", this._repeaterDivElement.id + indexToSelect); + } else if (this._inputElement.hasAttribute("aria-activedescendant")) { + this._inputElement.removeAttribute("aria-activedescendant"); + } + }, + + _scrollToView: function SearchBox_scrollToView(targetElement) { + var popupHeight = this._flyoutDivElement.getBoundingClientRect().bottom - this._flyoutDivElement.getBoundingClientRect().top; + if ((targetElement.offsetTop + targetElement.offsetHeight) > (this._flyoutDivElement.scrollTop + popupHeight)) { + // Element to scroll is below popup visible area + var scrollDifference = (targetElement.offsetTop + targetElement.offsetHeight) - (this._flyoutDivElement.scrollTop + popupHeight); + this._flyoutDivElement.msZoomTo({ contentX: 0, contentY: (this._flyoutDivElement.scrollTop + scrollDifference), viewportX: 0, viewportY: 0 }); + } else if (targetElement.offsetTop < this._flyoutDivElement.scrollTop) { + // Element to scroll is above popup visible area + this._flyoutDivElement.msZoomTo({ contentX: 0, contentY: targetElement.offsetTop, viewportX: 0, viewportY: 0 }); + } + }, + + _querySuggestionRenderer: function SearchBox_querySuggestionRenderer(item) { + var root = document.createElement("div"); + + this._addHitHighlightedText(root, item, item.text); + root.title = item.text; + + utilities.addClass(root, ClassName.searchBoxSuggestionQuery); + + var that = this; + root.addEventListener('click', function (ev) { + that._inputElement.focus(); + that._processSuggestionChosen(item, ev); + }); + + root.setAttribute("role", "option"); + var ariaLabel = WinJS.Resources._formatString(strings.ariaLabelQuery, item.text); + root.setAttribute("aria-label", ariaLabel); + return root; + }, + + _separatorSuggestionRenderer: function SearchBox_separatorSuggestionRenderer(item) { + var root = document.createElement("div"); + if (item.text.length > 0) { + var textElement = document.createElement("div"); + textElement.innerText = item.text; + textElement.title = item.text; + textElement.setAttribute("aria-hidden", "true"); + root.appendChild(textElement); + } + root.insertAdjacentHTML("beforeend", "
"); + utilities.addClass(root, ClassName.searchBoxSuggestionSeparator); + root.setAttribute("role", "separator"); + var ariaLabel = WinJS.Resources._formatString(strings.ariaLabelSeparator, item.text); + root.setAttribute("aria-label", ariaLabel); + return root; + }, + + _resultSuggestionRenderer: function SearchBox_resultSuggestionRenderer(item) { + var root = document.createElement("div"); + var image = new Image; + image.style.opacity = 0; + var loadImage = function (url) { + function onload() { + image.removeEventListener("load", onload, false); + WinJS.UI.Animation.fadeIn(image); + } + image.addEventListener("load", onload, false); + image.src = url; + }; + + if (item.image !== null) { + item.image.openReadAsync().then(function (streamWithContentType) { + if (streamWithContentType !== null) { + loadImage(URL.createObjectURL(streamWithContentType, { oneTimeOnly: true })); + } + }); + } else if (item.imageUrl != null) { + loadImage(item.imageUrl); + } + image.setAttribute("aria-hidden", "true"); + root.appendChild(image); + + var divElement = document.createElement("div"); + utilities.addClass(divElement, ClassName.searchBoxSuggestionResultText); + this._addHitHighlightedText(divElement, item, item.text); + divElement.title = item.text; + divElement.setAttribute("aria-hidden", "true"); + root.appendChild(divElement); + + var brElement = document.createElement("br"); + divElement.appendChild(brElement); + + var divDetailElement = document.createElement("span"); + utilities.addClass(divDetailElement, ClassName.searchBoxSuggestionResultDetailedText); + this._addHitHighlightedText(divDetailElement, item, item.detailText); + divDetailElement.title = item.detailText; + divDetailElement.setAttribute("aria-hidden", "true"); + divElement.appendChild(divDetailElement); + + utilities.addClass(root, ClassName.searchBoxSuggestionResult); + + var that = this; + root.addEventListener('click', function (ev) { + that._inputElement.focus(); + that._processSuggestionChosen(item, ev); + }); + + root.setAttribute("role", "option"); + var ariaLabel = WinJS.Resources._formatString(strings.ariaLabelResult, item.text, item.detailText); + root.setAttribute("aria-label", ariaLabel); + return root; + }, + + _suggestionRenderer: function SearchBox_suggestionRenderer(item) { + var root = null; + if (!item) { + return root; + } + if (item.kind === SearchSuggestionKind.Query) { + root = this._querySuggestionRenderer(item); + } else if (item.kind === SearchSuggestionKind.Separator) { + root = this._separatorSuggestionRenderer(item); + } else if (item.kind === SearchSuggestionKind.Result) { + root = this._resultSuggestionRenderer(item); + } else { + throw new WinJS.ErrorFromName("WinJS.UI.SearchBox.invalidSearchBoxSuggestionKind", strings.invalidSearchBoxSuggestionKind); + } + + return root; + }, + + _setElement: function SearchBox_setElement(element) { + this._domElement = element; + utilities.addClass(this._domElement, ClassName.searchBox); + + this._inputElement = document.createElement("input"); + this._inputElement.type = "search"; + utilities.addClass(this._inputElement, ClassName.searchBoxInput); + + this._buttonElement = document.createElement("div"); + this._buttonElement.tabIndex = -1; + utilities.addClass(this._buttonElement, ClassName.searchBoxButton); + + this._flyoutDivElement = document.createElement('div'); + utilities.addClass(this._flyoutDivElement, ClassName.searchBoxFlyout); + + this._repeaterDivElement = document.createElement('div'); + this._suggestionsData = new WinJS.Binding.List(); + this._repeater = new WinJS.UI.Repeater(this._repeaterDivElement, { data: this._suggestionsData, template: this._suggestionRendererBind }); + + this._domElement.appendChild(this._inputElement); + this._domElement.appendChild(this._buttonElement); + this._domElement.appendChild(this._flyoutDivElement); + this._flyoutDivElement.appendChild(this._repeaterDivElement); + this._hideFlyout(); + + this._wireupUserEvents(); + this._wireupWinRTEvents(); + this._wireupRepeaterEvents(); + }, + + _setAccessibilityProperties: function Searchbox_setAccessibilityProperties() { + // Set up accessibility properties + var label = this._domElement.getAttribute("aria-label"); + if (!label) { + this._domElement.setAttribute("aria-label", strings.ariaLabel); + } + this._domElement.setAttribute("role", "group"); + this._updateInputElementAriaLabel(); + this._inputElement.setAttribute("role", "textbox"); + this._buttonElement.setAttribute("role", "button"); + this._buttonElement.setAttribute("aria-label", strings.ariaLabelButton); + this._repeaterDivElement.setAttribute("role", "listbox"); + WinJS.UI._ensureId(this._repeaterDivElement); + this._inputElement.setAttribute("aria-controls", this._repeaterDivElement.id); + this._repeaterDivElement.setAttribute("aria-live", "polite"); + }, + + _updateInputElementAriaLabel: function Searchbox_updateInputElementAriaLabel() { + var ariaLabel = strings.ariaLabelInputNoPlaceHolder; + if (this._inputElement.placeholder && this._inputElement.placeholder) { + ariaLabel = WinJS.Resources._formatString(strings.ariaLabelInputPlaceHolder, this._inputElement.placeholder); + } + this._inputElement.setAttribute("aria-label", ariaLabel); + }, + + _submitQuery: function Searchbox_submitQuery(queryText, fillLinguisticDetails, event) { + if (this._disposed) { + return; + } + + // get the most up to date value of the input langauge from WinRT if available + if ((window.Windows) && (Windows.Globalization) && (Windows.Globalization.Language)) { + this._lastKeyPressLanguage = Windows.Globalization.Language.currentInputMethodLanguageTag; + } + + this._fireEvent(WinJS.UI.SearchBox._EventName.querysubmitted, { + language: this._lastKeyPressLanguage, + linguisticDetails: this._getLinguisticDetails(true /*useCache*/, fillLinguisticDetails), // allow caching, but generate empty linguistic details if suggestion is used + queryText: queryText, + keyModifiers: WinJS.UI.SearchBox._getKeyModifiers(event) + }); + + if (this._searchSuggestionManager) { + this._searchSuggestionManager.addToHistory( + this._inputElement.value, + this._lastKeyPressLanguage + ); + } + }, + + _processSuggestionChosen: function Searchbox_processSuggestionChosen(item, event) { + this.queryText = item.text; + if (item.kind === SearchSuggestionKind.Query) { + this._submitQuery(item.text, false /*fillLinguisticDetails*/, event); // force empty linguistic details since explicitly chosen suggestion from list + } else if (item.kind === SearchSuggestionKind.Result) { + this._fireEvent(WinJS.UI.SearchBox._EventName.resultsuggestionchosen, { + tag: item.tag, + keyModifiers: WinJS.UI.SearchBox._getKeyModifiers(event), + storageFile: null + }); + } + this._hideFlyout(); + }, + + _buttonClickHandler: function SearchBox_buttonClickHandler(event) { + this._inputElement.focus(); + this._submitQuery(this._inputElement.value, true /*fillLinguisticDetails*/, event); + this._hideFlyout(); + }, + + _inputOrImeChangeHandler: function SearchBox_inputImeChangeHandler(event) { + var isButtonDown = this._buttonElement.msMatchesSelector(":active"); + // swallow the IME change event that gets fired when composition is ended due to keyboarding down to the suggestion list & mouse down on the button + if (!this._isProcessingImeFocusLossKey() && !isButtonDown && !this._isFlyoutPointerDown) { + var linguisticDetails = this._getLinguisticDetails(false /*useCache*/, true /*createFilled*/); // never cache on explicit user changes + var hasLinguisticDetailsChanged = this._hasLinguisticDetailsChanged(linguisticDetails); // updates this._prevLinguisticDetails + + // Keep the previous composition cache up to date, execpt when composition ended with no text change and alternatives are kept. + // In that case, we need to use the cached values to correctly generate the query prefix/suffix for substituting alternatives, but still report to the client that the composition has ended (via start & length of composition of 0) + if ((this._inputElement.value !== this._prevQueryText) || (this._prevCompositionLength == 0) || (linguisticDetails.queryTextCompositionLength > 0)) { + this._prevCompositionStart = linguisticDetails.queryTextCompositionStart; + this._prevCompositionLength = linguisticDetails.queryTextCompositionLength; + } + + if ((this._prevQueryText === this._inputElement.value) && !hasLinguisticDetailsChanged) { + // Sometimes the input change is fired even if there is no change in input. + // Swallow event in those cases. + return; + } + this._prevQueryText = this._inputElement.value; + + // get the most up to date value of the input langauge from WinRT if available + if ((window.Windows) && (Windows.Globalization) && (Windows.Globalization.Language)) { + this._lastKeyPressLanguage = Windows.Globalization.Language.currentInputMethodLanguageTag; + } + + if ((window.Windows) && (Windows.Data) && (Windows.Data.Text) && (Windows.Data.Text.SemanticTextQuery)) { + if (this._inputElement.value !== "") { + this._hitFinder = new Windows.Data.Text.SemanticTextQuery(this._inputElement.value, this._lastKeyPressLanguage); + } else { + this._hitFinder = null; + } + } + + this._fireEvent(WinJS.UI.SearchBox._EventName.querychanged, { + language: this._lastKeyPressLanguage, + queryText: this._inputElement.value, + linguisticDetails: linguisticDetails + }); + var queryTextCompositionStart = null; + var queryTextCompositionLength = null; + var queryTextAlternatives = null; + if (this._searchSuggestionManager) { + this._searchSuggestionManager.setQuery( + this._inputElement.value, + this._lastKeyPressLanguage, + linguisticDetails + ); + } + } + }, + + _createSearchQueryLinguisticDetails: function SearchBox_createSearchQueryLinguisticDetails(compositionAlternatives, compositionStartOffset, compositionLength, queryTextPrefix, queryTextSuffix) { + var linguisticDetails = null; + + // The linguistic alternatives we receive are only for the composition string being composed. We need to provide the linguistic alternatives + // in the form of the full query text with alternatives embedded. + var fullCompositionAlternatives = []; + for (var i = 0; i < compositionAlternatives.length; i++) { + fullCompositionAlternatives[i] = queryTextPrefix + compositionAlternatives[i] + queryTextSuffix; + } + + if ((window.Windows) && (Windows.ApplicationModel) && (Windows.ApplicationModel.Search) && (Windows.ApplicationModel.Search.SearchQueryLinguisticDetails)) { + linguisticDetails = new Windows.ApplicationModel.Search.SearchQueryLinguisticDetails(fullCompositionAlternatives, compositionStartOffset, compositionLength); + } + else { + // If we're in web compartment, create a script version of the WinRT SearchQueryLinguisticDetails object + linguisticDetails = { + queryTextAlternatives: fullCompositionAlternatives, + queryTextCompositionStart: compositionStartOffset, + queryTextCompositionLength: compositionLength + }; + } + return linguisticDetails; + }, + + _getLinguisticDetails: function SearchBox_getLinguisticDetails(useCache, createFilled) { // createFilled=false always creates an empty linguistic details object, otherwise generate it or use the cache + var linguisticDetails = null; + if ((this._inputElement.value === this._prevQueryText) && useCache && this._prevLinguisticDetails && createFilled) { + linguisticDetails = this._prevLinguisticDetails; + } + else { + var compositionAlternatives = []; + var compositionStartOffset = 0; + var compositionLength = 0; + var queryTextPrefix = ""; + var queryTextSuffix = ""; + if (createFilled && this._inputElement.msGetInputContext && this._inputElement.msGetInputContext().getCompositionAlternatives) { + var context = this._inputElement.msGetInputContext(); + compositionAlternatives = context.getCompositionAlternatives(); + compositionStartOffset = context.compositionStartOffset; + compositionLength = context.compositionEndOffset - context.compositionStartOffset; + + if ((this._inputElement.value !== this._prevQueryText) || (this._prevCompositionLength == 0) || (compositionLength > 0)) { + queryTextPrefix = this._inputElement.value.substring(0, compositionStartOffset); + queryTextSuffix = this._inputElement.value.substring(compositionStartOffset + compositionLength); + } + else { + // composition ended, but alternatives have been kept, need to reuse the previous query prefix/suffix, but still report to the client that the composition has ended (start & length of composition of 0) + queryTextPrefix = this._inputElement.value.substring(0, this._prevCompositionStart); + queryTextSuffix = this._inputElement.value.substring(this._prevCompositionStart + this._prevCompositionLength); + } + } + linguisticDetails = this._createSearchQueryLinguisticDetails(compositionAlternatives, compositionStartOffset, compositionLength, queryTextPrefix, queryTextSuffix); + } + return linguisticDetails; + }, + + _handleTabKeyDown: function SearchBox_handleTabKeyDown(event) { + var closeFlyout = true; + if (event.shiftKey) { + // If focus is not in input + if (this._currentFocusedIndex !== -1) { + // Remove selection. + this._currentFocusedIndex = -1; + this._selectSuggestionAtIndex(this._currentFocusedIndex); + this._updateSearchButtonClass(); + event.preventDefault(); + event.stopPropagation(); + closeFlyout = false; + } + } else if (this._currentFocusedIndex === -1) { + if (this._isFlyoutBelow()) { + // Move to first element + this._currentFocusedIndex = this._findNextSuggestionElementIndex(this._currentFocusedIndex); + } else { + // Move to last element + this._currentFocusedIndex = this._findPreviousSuggestionElementIndex(this._suggestionsData.length); + } + if (this._currentFocusedIndex != -1) { + this._selectSuggestionAtIndex(this._currentFocusedIndex); + this._updateQueryTextWithSuggestionText(this._currentFocusedIndex); + this._updateSearchButtonClass(); + event.preventDefault(); + event.stopPropagation(); + closeFlyout = false; + } + } + + if (closeFlyout) { + this._hideFlyout(); + } + }, + + _keyDownHandler: function SearchBox_keyDownHandler(event) { + this._lastKeyPressLanguage = event.locale; + if (event.key === "Tab") { + this._isProcessingTabKey = true; + } + else if (event.key === "Up") { + this._isProcessingUpKey = true; + } + else if (event.key === "Down") { + this._isProcessingDownKey = true; + } + else if ((event.key === "Enter") && (event.locale === "ko")) { + this._isProcessingEnterKey = true; + } + // Ignore keys handled by ime. + if (event.keyCode !== WinJS.UI.SearchBox._Constants.IME_HANDLED_KEYCODE) { + if (event.key === "Tab") { + this._handleTabKeyDown(event); + } else if (event.key === "Esc") { + // If focus is not in input + if (this._currentFocusedIndex !== -1) { + this.queryText = this._prevQueryText; + this._currentFocusedIndex = -1; + this._selectSuggestionAtIndex(this._currentFocusedIndex); + this._updateSearchButtonClass(); + event.preventDefault(); + event.stopPropagation(); + } else if (this.queryText !== "") { + this.queryText = ""; + this._inputOrImeChangeHandler(null); + this._updateSearchButtonClass(); + event.preventDefault(); + event.stopPropagation(); + } + } else if (event.key === "Up") { + var prevIndex; + if (this._currentSelectedIndex !== -1) { + prevIndex = this._findPreviousSuggestionElementIndex(this._currentSelectedIndex); + // Restore user entered query when user navigates back to input. + if (prevIndex === -1) { + this.queryText = this._prevQueryText; + } + } else { + prevIndex = this._findPreviousSuggestionElementIndex(this._suggestionsData.length); + } + this._currentFocusedIndex = prevIndex; + this._selectSuggestionAtIndex(prevIndex); + this._updateQueryTextWithSuggestionText(this._currentFocusedIndex); + this._updateSearchButtonClass(); + event.preventDefault(); + event.stopPropagation(); + } else if (event.key === "Down") { + var nextIndex = this._findNextSuggestionElementIndex(this._currentSelectedIndex); + // Restore user entered query when user navigates back to input. + if ((this._currentSelectedIndex !== -1) && (nextIndex === -1)) { + this.queryText = this._prevQueryText; + } + this._currentFocusedIndex = nextIndex; + this._selectSuggestionAtIndex(nextIndex); + this._updateQueryTextWithSuggestionText(this._currentFocusedIndex); + this._updateSearchButtonClass(); + event.preventDefault(); + event.stopPropagation(); + } else if (event.key === "Enter") { + if (this._currentSelectedIndex === -1) { + this._submitQuery(this._inputElement.value, true /*fillLinguisticDetails*/, event); + } else { + this._processSuggestionChosen(this._suggestionsData.getAt(this._currentSelectedIndex), event); + } + this._hideFlyout(); + } else if (WinJS.UI.SearchBox._isTypeToSearchKey(event)) { + // Type to search on suggestions scenario. + if (this._currentFocusedIndex !== -1) { + this._currentFocusedIndex = -1; + this._selectSuggestionAtIndex(-1); + this._updateFakeFocus(); + } + } + } + }, + + _keyPressHandler: function SearchBox_keyPressHandler(event) { + this._lastKeyPressLanguage = event.locale; + }, + + _keyUpHandler: function SearchBox_keyUpHandler(event) { + if (event.key === "Tab") { + this._isProcessingTabKey = false; + } + else if (event.key === "Up") { + this._isProcessingUpKey = false; + } + else if (event.key === "Down") { + this._isProcessingDownKey = false; + } + else if (event.key === "Enter") { + this._isProcessingEnterKey = false; + } + }, + + _searchBoxFocusInHandler: function SearchBox__searchBoxFocusInHandler(event) { + // Refresh hit highlighting if text has changed since focus was present + // This can happen if the user committed a suggestion previously. + if (this._inputElement.value !== this._prevQueryText) { + if ((window.Windows) && (Windows.Data) && (Windows.Data.Text) && (Windows.Data.Text.SemanticTextQuery)) { + if (this._inputElement.value !== "") { + this._hitFinder = new Windows.Data.Text.SemanticTextQuery(this._inputElement.value, this._inputElement.lang); + } else { + this._hitFinder = null; + } + } + } + + // If focus is returning to the input box from outside the search control, show the flyout and refresh the suggestions + if ((event.target === this._inputElement) && !this._isElementInSearchControl(event.relatedTarget)) { + this._showFlyout(); + // If focus is not in input + if (this._currentFocusedIndex !== -1) { + this._selectSuggestionAtIndex(this._currentFocusedIndex); + } else { + this._updateFakeFocus(); + } + + if (this._searchSuggestionManager) { + this._searchSuggestionManager.setQuery( + this._inputElement.value, + this._lastKeyPressLanguage, + this._getLinguisticDetails(true /*useCache*/, true /*createFilled*/) + ); + } + } + + utilities.addClass(this.element, ClassName.searchBoxInputFocus); + this._updateSearchButtonClass(); + }, + + _searchBoxFocusOutHandler: function SearchBox_searchBoxFocusOutHandler(event) { + this._hideFlyoutIfLeavingSearchControl(event.relatedTarget); + utilities.removeClass(this.element, ClassName.searchBoxInputFocus); + this._updateSearchButtonClass(); + this._isProcessingDownKey = false; + this._isProcessingUpKey = false; + this._isProcessingTabKey = false; + this._isProcessingEnterKey = false; + }, + + _isIMEOccludingFlyout: function SearchBox_isIMEOccludingFlyout(imeRect) { + var flyoutTop = this._getFlyoutTop(); + var flyoutBottom = this._getFlyoutBottom(); + if (((imeRect.top >= flyoutTop) && (imeRect.top <= flyoutBottom)) || + ((imeRect.bottom >= flyoutTop) && (imeRect.bottom <= flyoutBottom))) { + return true; + } + return false; + }, + + _addFlyoutIMEPaddingIfRequired: function SearchBox_addFlyoutIMEPaddingIfRequired() { + if (this._isFlyoutShown() && this._isFlyoutBelow() && this._inputElement.msGetInputContext && this._inputElement.msGetInputContext()) { + var context = this._inputElement.msGetInputContext(); + var rect = context.getCandidateWindowClientRect(); + if (this._isIMEOccludingFlyout(rect)) { + var animation = WinJS.UI.Animation.createRepositionAnimation(this._flyoutDivElement.children); + this._flyoutDivElement.style.paddingTop = (rect.bottom - rect.top) + "px"; + animation.execute(); + } + } + }, + + _msCandidateWindowShowHandler: function SearchBox_msCandidateWindowShowHandler(event) { + this._addFlyoutIMEPaddingIfRequired(); + this._reflowImeOnPointerRelease = false; + }, + + _msCandidateWindowHideHandler: function SearchBox_msCandidateWindowHideHandler(event) { + if (!this._isFlyoutPointerDown) { + var animation = WinJS.UI.Animation.createRepositionAnimation(this._flyoutDivElement.children); + this._flyoutDivElement.style.paddingTop = ""; + animation.execute(); + } + else { + this._reflowImeOnPointerRelease = true; + } + }, + + _wireupUserEvents: function SearchBox_wireupUserEvents() { + var inputOrImeChangeHandler = this._inputOrImeChangeHandler.bind(this); + this._buttonElement.addEventListener("click", this._buttonClickHandler.bind(this)); + this._inputElement.addEventListener("input", inputOrImeChangeHandler); + this._inputElement.addEventListener("keydown", this._keyDownHandler.bind(this)); + this._inputElement.addEventListener("keypress", this._keyPressHandler.bind(this)); + this._inputElement.addEventListener("keyup", this._keyUpHandler.bind(this)); + this._inputElement.addEventListener("pointerdown", this._inputPointerDownHandler.bind(this)); + this._flyoutDivElement.addEventListener("pointerdown", this._flyoutPointerDownHandler.bind(this)); + this._flyoutDivElement.addEventListener("pointerup", this._flyoutPointerReleasedHandler.bind(this)); + this._flyoutDivElement.addEventListener("pointercancel", this._flyoutPointerReleasedHandler.bind(this)); + this._flyoutDivElement.addEventListener("pointerout", this._flyoutPointerReleasedHandler.bind(this)); + this.element.addEventListener("focusin", this._searchBoxFocusInHandler.bind(this)); + this.element.addEventListener("focusout", this._searchBoxFocusOutHandler.bind(this)); + + this._inputElement.addEventListener("compositionstart", inputOrImeChangeHandler); + this._inputElement.addEventListener("compositionupdate", inputOrImeChangeHandler); + this._inputElement.addEventListener("compositionend", inputOrImeChangeHandler); + + if (this._inputElement.msGetInputContext && this._inputElement.msGetInputContext()) { + var context = this._inputElement.msGetInputContext(); + context.addEventListener("MSCandidateWindowShow", this._msCandidateWindowShowHandler.bind(this)); + context.addEventListener("MSCandidateWindowHide", this._msCandidateWindowHideHandler.bind(this)); + } + }, + + _repeaterChangedHandler: function SearchBox_repeaterChangedHandler(ev) { + this._updateFlyoutTopAndTouchAction(); + if (this._isFlyoutShown()) { + this._repeaterDivElement.style.display = "none"; + this._repeaterDivElement.style.display = "block"; + } + }, + + _wireupRepeaterEvents: function SearchBox_wireupRepeaterEvents() { + var repeaterChangeHandler = this._repeaterChangedHandler.bind(this); + this._repeater.addEventListener("itemchanged", repeaterChangeHandler); + this._repeater.addEventListener("iteminserted", repeaterChangeHandler); + this._repeater.addEventListener("itemremoved", repeaterChangeHandler); + this._repeater.addEventListener("itemsreloaded", repeaterChangeHandler); + }, + + _inputPointerDownHandler: function SearchBox_inputPointerDownHandler(ev) { + if ((document.activeElement === this._inputElement) && (this._currentSelectedIndex !== -1)) { + this._currentFocusedIndex = -1; + this._selectSuggestionAtIndex(this._currentFocusedIndex); + } + }, + + _flyoutPointerDownHandler: function SearchBox_flyoutPointerDownHandler(ev) { + this._isFlyoutPointerDown = true; + var srcElement = ev.srcElement; + while (srcElement && (srcElement.parentNode !== this._repeaterDivElement)) { + srcElement = srcElement.parentNode; + } + var index = this._findSuggestionElementIndex(srcElement); + if ((index >= 0) && (index < this._suggestionsData.length) && (this._currentFocusedIndex !== index)) { + if (this._isSuggestionSelectable(this._suggestionsData.getAt(index))) { + this._currentFocusedIndex = index; + this._selectSuggestionAtIndex(index); + this._updateQueryTextWithSuggestionText(this._currentFocusedIndex); + } + } + // Prevent default so focus does not leave input element. + ev.preventDefault(); + }, + + _flyoutPointerReleasedHandler: function SearchBox_flyoutPointerReleasedHandler(ev) { + this._isFlyoutPointerDown = false; + + if (this._reflowImeOnPointerRelease) { + this._reflowImeOnPointerRelease = false; + var animation = WinJS.UI.Animation.createRepositionAnimation(this._flyoutDivElement.children); + this._flyoutDivElement.style.paddingTop = ""; + animation.execute(); + } + }, + + _isElementInSearchControl: function SearchBox_isElementInSearchControl(targetElement) { + return this.element.contains(targetElement) || (this.element === targetElement); + }, + + _hideFlyoutIfLeavingSearchControl: function SearchBox__hideFlyoutIfLeavingSearchControl(targetElement) { + if (!this._isFlyoutShown()) { + return; + } + if (!this._isElementInSearchControl(targetElement)) { + this._hideFlyout(); + } + }, + + _wireupWinRTEvents: function SearchBox_wireupWinRTEvents() { + if (this._searchSuggestions) { + this._searchSuggestions.addEventListener("vectorchanged", this._suggestionsChangedHandlerBind); + } + if (this._searchSuggestionManager) { + this._searchSuggestionManager.addEventListener("suggestionsrequested", this._suggestionsRequestedHandlerBind); + } + }, + + _suggestionsChangedHandler: function SearchBox_suggestionsChangedHandler(event) { + var collectionChange = event.collectionChange; + if (collectionChange === Windows.Foundation.Collections.CollectionChange.reset) { + if (this._isFlyoutShown()) { + this._hideFlyout(); + } + this._suggestionsData.splice(0, this._suggestionsData.length); + } else if (collectionChange === Windows.Foundation.Collections.CollectionChange.itemInserted) { + var index = event.index; + var suggestion = this._searchSuggestions[index]; + this._suggestionsData.splice(index, 0, suggestion); + + this._showFlyout(); + + } else if (collectionChange === Windows.Foundation.Collections.CollectionChange.itemRemoved) { + if ((this._suggestionsData.length === 1)) { + try { + this._inputElement.setActive(); + } catch (e) { + } + this._hideFlyout(); + } + var index = event.index; + this._suggestionsData.splice(index, 1); + } else if (collectionChange === Windows.Foundation.Collections.CollectionChange.itemChanged) { + var index = event.index; + var suggestion = this._searchSuggestions[index]; + if (suggestion !== this._suggestionsData.getAt(index)) { + this._suggestionsData.setAt(index, suggestion); + } else { + // If the suggestions manager gives us an identical item, it means that only the hit highlighted text has changed. + var existingElement = this._repeater.elementFromIndex(index); + if (utilities.hasClass(existingElement, ClassName.searchBoxSuggestionQuery)) { + this._addHitHighlightedText(existingElement, suggestion, suggestion.text); + } + else { + var resultSuggestionDiv = existingElement.querySelector("." + ClassName.searchBoxSuggestionResultText); + if (resultSuggestionDiv) { + this._addHitHighlightedText(resultSuggestionDiv, suggestion, suggestion.text); + var resultSuggestionDetailDiv = existingElement.querySelector("." + ClassName.searchBoxSuggestionResultDetailedText); + if (resultSuggestionDetailDiv) { + this._addHitHighlightedText(resultSuggestionDetailDiv, suggestion, suggestion.detailText); + } + } + } + } + } + + if (document.activeElement === this._inputElement) { + this._updateFakeFocus(); + } + }, + + _suggestionsRequestedHandler: function SearchBox_suggestionsRequestedHandler(event) { + // get the most up to date value of the input langauge from WinRT if available + if ((window.Windows) && (Windows.Globalization) && (Windows.Globalization.Language)) { + this._lastKeyPressLanguage = Windows.Globalization.Language.currentInputMethodLanguageTag; + } + + var suggestionsRequestedEventDetail = event; + var deferral; + this._fireEvent(WinJS.UI.SearchBox._EventName.suggestionsrequested, { + setPromise: function (promise) { + deferral = suggestionsRequestedEventDetail.request.getDeferral(); + promise.then(function () { + deferral.complete(); + }); + }, + searchSuggestionCollection: suggestionsRequestedEventDetail.request.searchSuggestionCollection, + language: this._lastKeyPressLanguage, + linguisticDetails: this._getLinguisticDetails(true /*useCache*/, true /*createFilled*/), + queryText: this._inputElement.value + }); + }, + + _fireEvent: function SearchBox_fireEvent(type, detail) { + // Returns true if ev.preventDefault() was not called + var event = document.createEvent("CustomEvent"); + event.initCustomEvent(type, true, true, detail); + return this.element.dispatchEvent(event); + }, + + _requestingFocusOnKeyboardInputHandler: function SearchBox_requestingFocusOnKeyboardInputHandler(event) { + this._fireEvent(WinJS.UI.SearchBox._EventName.receivingfocusonkeyboardinput, null); + if (document.activeElement !== this._inputElement) { + try { + this._inputElement.focus(); + } catch (e) { + } + } + }, + + _hasLinguisticDetailsChanged: function SearchBox_hasLinguisticDetailsChanged(newLinguisticDetails) { + var hasLinguisticDetailsChanged = false; + if ((this._prevLinguisticDetails.queryTextCompositionStart !== newLinguisticDetails.queryTextCompositionStart) || + (this._prevLinguisticDetails.queryTextCompositionLength !== newLinguisticDetails.queryTextCompositionLength) || + (this._prevLinguisticDetails.queryTextAlternatives.length !== newLinguisticDetails.queryTextAlternatives.length)) { + hasLinguisticDetailsChanged = true; + } + this._prevLinguisticDetails = newLinguisticDetails; + return hasLinguisticDetailsChanged; + }, + + _isProcessingImeFocusLossKey: function SearchBox_isProcessingImeFocusLossKey() { + return this._isProcessingDownKey || this._isProcessingUpKey || this._isProcessingTabKey || this._isProcessingEnterKey; + }, + + _updateQueryTextWithSuggestionText: function SearchBox_updateQueryTextWithSuggestionText(suggestionIndex) { + if ((suggestionIndex >= 0) && (suggestionIndex < this._suggestionsData.length)) { + this.queryText = this._suggestionsData.getAt(suggestionIndex).text; + } + } + + }, { + _EventName: { + querychanged: EventName.querychanged, + querysubmitted: EventName.querysubmitted, + resultsuggestionchosen: EventName.resultsuggestionchosen, + suggestionsrequested: EventName.suggestionsrequested, + receivingfocusonkeyboardinput: EventName.receivingfocusonkeyboardinput + }, + + _Constants: { + MIN_POPUP_HEIGHT: 152, + IME_HANDLED_KEYCODE: 229 + }, + + _getKeyModifiers: function SearchBox_getKeyModifiers(ev) { + + // Returns the same value as http://msdn.microsoft.com/en-us/library/windows/apps/xaml/windows.system.virtualkeymodifiers + var VirtualKeys = { + ctrlKey: 1, + altKey: 2, + shiftKey: 4 + }; + + var keyModifiers = 0; + if (ev.ctrlKey) { + keyModifiers |= VirtualKeys.ctrlKey; + } + if (ev.altKey) { + keyModifiers |= VirtualKeys.altKey; + } + if (ev.shiftKey) { + keyModifiers |= VirtualKeys.shiftKey; + } + return keyModifiers; + }, + + _sortAndMergeHits: function searchBox_sortAndMergeHits(hitsProvided) { + var reducedHits = []; + if (hitsProvided) { + // Copy hitsprovided array as winrt objects are immutable. + var hits = new Array(hitsProvided.length); + for (var i = 0; i < hitsProvided.length; i++) { + hits.push({ startPosition: hitsProvided[i].startPosition, length: hitsProvided[i].length }); + } + hits.sort(WinJS.UI.SearchBox._hitStartPositionAscendingSorter); + hits.reduce(WinJS.UI.SearchBox._hitIntersectionReducer, reducedHits); + } + return reducedHits; + }, + + _hitStartPositionAscendingSorter: function searchBox_hitStartPositionAscendingSorter(firstHit, secondHit) { + var returnValue = 0; + if (firstHit.startPosition < secondHit.startPosition) { + returnValue = -1; + } else if (firstHit.startPosition > secondHit.startPosition) { + returnValue = 1; + } + return returnValue; + }, + + _hitIntersectionReducer: function searchBox_hitIntersectionReducer(reducedHits, nextHit, currentIndex, originalList) { + if (currentIndex === 0) { + reducedHits.push(nextHit); + } else { + var curHit = reducedHits[reducedHits.length - 1]; + var curHitEndPosition = curHit.startPosition + curHit.length; + //#DBG _ASSERT(nextHit.startPosition >= curHit.startPosition); + if (nextHit.startPosition <= curHitEndPosition) { + // The next hit intersects or is next to current hit. Merge it. + var nextHitEndPosition = nextHit.startPosition + nextHit.length; + if (nextHitEndPosition > curHitEndPosition) { + curHit.length = nextHitEndPosition - curHit.startPosition; + } + } else { + // No intersection, simply add to reduced list. + reducedHits.push(nextHit); + } + } + return reducedHits; + }, + + _isTypeToSearchKey: function searchBox__isTypeToSearchKey(event) { + if (event.shiftKey || event.ctrlKey || event.altKey) { + return false; + } + return true; + } + }); + WinJS.Class.mix(SearchBox, WinJS.UI.DOMEventMixin); + return SearchBox; + }) + }); + +})(this, WinJS); + +/// appbar,Flyout,Flyouts,registeredforsettings,SettingsFlyout,Statics,Syriac +(function settingsFlyoutInit(WinJS) { + "use strict"; + + WinJS.Namespace.define("WinJS.UI", { + /// + /// Provides users with fast, in-context access to settings that affect the current app. + /// + /// + /// Settings Flyout + /// + /// + /// + ///
+ /// + ///
Custom Settings
+ ///
+ ///
+ /// {Your Content Here} + ///
+ /// ]]>
+ /// Raised just before showing a SettingsFlyout. + /// Raised immediately after a SettingsFlyout is fully shown. + /// Raised just before hiding a SettingsFlyout. + /// Raised immediately after a SettingsFlyout is fully hidden. + /// The SettingsFlyout control itself. + /// + /// + /// + SettingsFlyout: WinJS.Namespace._lazy(function () { + var thisWinUI = WinJS.UI; + + var focusHasHitSettingsPaneOnce; + + // Class Names + var settingsFlyoutClass = "win-settingsflyout", + fullSettingsFlyoutClassName = "." + settingsFlyoutClass, + settingsFlyoutLightClass = "win-ui-light", + narrowClass = "win-narrow", + wideClass = "win-wide"; + + var firstDivClass = "win-firstdiv"; + var finalDivClass = "win-finaldiv"; + + // Constants for width + var settingsNarrow = "narrow", + settingsWide = "wide"; + + // Determine if the settings pane (system language) is RTL or not. + function _shouldAnimateFromLeft() { + if (WinJS.Utilities.hasWinRT && Windows.UI.ApplicationSettings.SettingsEdgeLocation) { + var appSettings = Windows.UI.ApplicationSettings; + return (appSettings.SettingsPane.edge === appSettings.SettingsEdgeLocation.left); + } else { + return false; + } + }; + + // Get the settings control by matching the settingsCommandId + // if no match we'll try to match element id + function _getChildSettingsControl(parentElement, id) { + var settingElements = parentElement.querySelectorAll(fullSettingsFlyoutClassName); + var retValue, + control; + for (var i = 0; i < settingElements.length; i++) { + control = settingElements[i].winControl; + if (control) { + if (control.settingsCommandId === id) { + retValue = control; + break; + } + if (settingElements[i].id === id) { + retValue = retValue || control; + } + } + } + + return retValue; + } + + var SettingsFlyout = WinJS.Class.derive(WinJS.UI._Overlay, function SettingsFlyout_ctor(element, options) { + /// + /// Creates a new SettingsFlyout control. + /// + /// The DOM element that will host the control. + /// + /// + /// The set of properties and values to apply to the new SettingsFlyout. + /// + /// The new SettingsFlyout control. + /// + /// + + // Make sure there's an input element + this._element = element || document.createElement("div"); + this._id = this._element.id || this._element.uniqueID; + this._writeProfilerMark("constructor,StartTM"); + + // Call the base overlay constructor helper + this._baseOverlayConstructor(this._element, options); + + this._addFirstDiv(); + this._addFinalDiv(); + + // Handle "esc" & "tab" key presses + this._element.addEventListener("keydown", this._handleKeyDown, true); + + // Make a click eating div + thisWinUI._Overlay._createClickEatingDivAppBar(); + + // Start settings hidden + this._element.style.visibilty = "hidden"; + this._element.style.display = "none"; + + // Attach our css class + WinJS.Utilities.addClass(this._element, settingsFlyoutClass); + + // apply the light theme styling to the win-content elements inside the SettingsFlyout + WinJS.Utilities.query("div.win-content", this._element). + forEach(function (e) { + if (!e.msMatchesSelector('.win-ui-dark, .win-ui-dark *')) { + WinJS.Utilities.addClass(e, settingsFlyoutLightClass); + } + }); + + // Make sure we have an ARIA role + var role = this._element.getAttribute("role"); + if (role === null || role === "" || role === undefined) { + this._element.setAttribute("role", "dialog"); + } + var label = this._element.getAttribute("aria-label"); + if (label === null || label === "" || label === undefined) { + this._element.setAttribute("aria-label", strings.ariaLabel); + } + + // Make sure _Overlay event handlers are hooked up + this._addOverlayEventHandlers(true); + + // Make sure animations are hooked up + this._currentAnimateIn = this._animateSlideIn; + this._currentAnimateOut = this._animateSlideOut; + this._writeProfilerMark("constructor,StopTM"); + }, { + // Public Properties + + /// + /// Width of the SettingsFlyout, "narrow", or "wide". + /// + /// SettingsFlyout.width may be altered or unavailable in future versions. Instead, style the CSS width property on elements with the .win-settingsflyout class. + /// + /// + /// + width: { + get: function () { + return this._width; + }, + + set: function (value) { + WinJS.Utilities._deprecated(strings.widthDeprecationMessage); + if (value === this._width) { + return; + } + // Get rid of old class + if (this._width === settingsNarrow) { + WinJS.Utilities.removeClass(this._element, narrowClass); + } else if (this._width === settingsWide) { + WinJS.Utilities.removeClass(this._element, wideClass); + } + this._width = value; + + // Attach our new css class + if (this._width === settingsNarrow) { + WinJS.Utilities.addClass(this._element, narrowClass); + } else if (this._width === settingsWide) { + WinJS.Utilities.addClass(this._element, wideClass); + } + } + }, + + /// + /// Define the settings command Id for the SettingsFlyout control. + /// + /// + settingsCommandId: { + get: function () { + return this._settingsCommandId; + }, + + set: function (value) { + this._settingsCommandId = value; + } + }, + + show: function () { + /// + /// + /// Shows the SettingsFlyout, if hidden. + /// + /// + /// + // Just call private version to make appbar flags happy + + // Don't do anything if disabled + if (this.disabled) { + return; + } + this._writeProfilerMark("show,StartTM"); // The corresponding "stop" profiler mark is handled in _Overlay._baseEndShow(). + this._show(); + }, + + _dispose: function SettingsFlyout_dispose() { + WinJS.Utilities.disposeSubTree(this.element); + this._dismiss(); + }, + + _show: function SettingsFlyout_show() { + // We call our base "_baseShow" because SettingsFlyout overrides show + this._baseShow(); + // Need click-eating div to be visible, + // (even if now hiding, we'll show and need click eater) + thisWinUI._Overlay._showClickEatingDivAppBar(); + }, + + _endShow: function SettingsFlyout_endShow() { + // Clean up after showing + this._initAfterAnimation(); + }, + + _initAfterAnimation: function SettingsFlyout_initAfterAnimation() { + focusHasHitSettingsPaneOnce = 0; + + // Verify that the firstDiv and finalDiv are in the correct location. + // Move them to the correct location or add them if they are not. + if (!WinJS.Utilities.hasClass(this.element.children[0], firstDivClass)) { + var firstDiv = this.element.querySelectorAll(".win-first"); + if (firstDiv && firstDiv.length > 0) { + firstDiv.item(0).parentNode.removeChild(firstDiv.item(0)); + } + + this._addFirstDiv(); + } + + // Set focus to the firstDiv + if (this.element.children[0]) { + this.element.children[0].addEventListener("focusout", function () { focusHasHitSettingsPaneOnce = 1; }, false); + this.element.children[0].focus(); + } + + if (!WinJS.Utilities.hasClass(this.element.children[this.element.children.length - 1], finalDivClass)) { + var finalDiv = this.element.querySelectorAll(".win-final"); + if (finalDiv && finalDiv.length > 0) { + finalDiv.item(0).parentNode.removeChild(finalDiv.item(0)); + } + + this._addFinalDiv(); + } + + this._setBackButtonsAriaLabel(); + }, + + _setBackButtonsAriaLabel: function SettingsFlyout_setBackButtonsAriaLabel() { + var backbuttons = this.element.querySelectorAll(".win-backbutton"); + var label; + for (var i = 0; i < backbuttons.length; i++) { + label = backbuttons[i].getAttribute("aria-label"); + if (label === null || label === "" || label === undefined) { + backbuttons[i].setAttribute("aria-label", strings.backbuttonAriaLabel); + } + } + }, + + hide: function () { + /// + /// + /// Hides the SettingsFlyout, if visible, regardless of other state. + /// + /// + /// + // Just call private version to make appbar flags happy + this._writeProfilerMark("hide,StartTM"); // The corresponding "stop" profiler mark is handled in _Overlay._baseEndHide(). + this._hide(); + }, + + _hide: function SettingsFlyout_hide() { + if (this._baseHide()) { + // Need click-eating div to be hidden + thisWinUI._Overlay._hideClickEatingDivAppBar(); + } + }, + + // SettingsFlyout animations + _animateSlideIn: function SettingsFlyout_animateSlideIn() { + var animateFromLeft = _shouldAnimateFromLeft(); + var offset = animateFromLeft ? "-100px" : "100px"; + WinJS.Utilities.query("div.win-content", this._element). + forEach(function (e) { WinJS.UI.Animation.enterPage(e, { left: offset }) }); + + var where, + width = this._element.offsetWidth; + // Slide in from right side or left side? + if (animateFromLeft) { + // RTL + where = { top: "0px", left: "-" + width + "px" }; + this._element.style.right = "auto"; + this._element.style.left = "0px"; + } else { + // From right side + where = { top: "0px", left: width + "px" }; + this._element.style.right = "0px"; + this._element.style.left = "auto"; + } + + this._element.style.opacity = 1; + this._element.style.visibility = "visible"; + + return WinJS.UI.Animation.showPanel(this._element, where); + }, + + _animateSlideOut: function SettingsFlyout_animateSlideOut() { + var where, + width = this._element.offsetWidth; + if (_shouldAnimateFromLeft()) { + // RTL + where = { top: "0px", left: width + "px" }; + this._element.style.right = "auto"; + this._element.style.left = "-" + width + "px"; + } else { + // From right side + where = { top: "0px", left: "-" + width + "px" }; + this._element.style.right = "-" + width + "px"; + this._element.style.left = "auto"; + } + + return WinJS.UI.Animation.showPanel(this._element, where); + }, + + _fragmentDiv: { + get: function SettingsFlyout_fragmentDiv_get() { + return this._fragDiv; + }, + + set: function SettingsFlyout_fragmentDiv_set(value) { + this._fragDiv = value; + } + }, + + _unloadPage: function SettingsFlyout_unloadPage(event) { + var settingsControl = event.currentTarget.winControl; + settingsControl.removeEventListener(thisWinUI._Overlay.afterHide, this._unloadPage, false); + + WinJS.Promise.as().then(function () { + if (settingsControl._fragmentDiv) { + document.body.removeChild(settingsControl._fragmentDiv); + settingsControl._fragmentDiv = null; + } + }); + }, + + _dismiss: function SettingsFlyout_dismiss() { + this.addEventListener(thisWinUI._Overlay.afterHide, this._unloadPage, false); + this._hide(); + }, + + _handleKeyDown: function SettingsFlyout_handleKeyDown(event) { + if (event.key === "Esc") { + event.preventDefault(); + event.stopPropagation(); + this.winControl._dismiss(); + } else if ((event.key === "Spacebar" || event.key === "Enter") + && (this.children[0] === document.activeElement)) { + event.preventDefault(); + event.stopPropagation(); + this.winControl._dismiss(); + } else if (event.shiftKey && event.key === "Tab" + && this.children[0] === document.activeElement) { + event.preventDefault(); + event.stopPropagation(); + var _elms = this.getElementsByTagName("*"); + + for (var i = _elms.length - 2; i >= 0; i--) { + _elms[i].focus(); + + if (_elms[i] === document.activeElement) { + break; + } + } + } + }, + + _focusOnLastFocusableElementFromParent: function SettingsFlyout_focusOnLastFocusableElementFromParent() { + var active = document.activeElement; + if (!focusHasHitSettingsPaneOnce || !active || !WinJS.Utilities.hasClass(active, firstDivClass)) { + return; + } + + var _elms = this.parentElement.getElementsByTagName("*"); + + // There should be at least 1 element in addition to the firstDiv & finalDiv + if (_elms.length <= 2) { + return; + } + + // Get the tabIndex set to the finalDiv (which is the highest) + var _highestTabIndex = _elms[_elms.length - 1].tabIndex; + + // If there are positive tabIndices, set focus to the element with the highest tabIndex. + // Otherwise set focus to the last focusable element in DOM order. + if (_highestTabIndex) { + for (var i = _elms.length - 2; i > 0; i--) { + if (_elms[i].tabIndex === _highestTabIndex) { + _elms[i].focus(); + break; + } + } + } else { + for (i = _elms.length - 2; i > 0; i--) { + // Skip
with undefined tabIndex (To work around Win8 bug #622245) + if ((_elms[i].tagName !== "DIV") || (_elms[i].getAttribute("tabIndex") !== null)) { + _elms[i].focus(); + + if (_elms[i] === document.activeElement) { + break; + } + } + } + } + }, + + _focusOnFirstFocusableElementFromParent: function SettingsFlyout_focusOnFirstFocusableElementFromParent() { + var active = document.activeElement; + if (!active || !WinJS.Utilities.hasClass(active, finalDivClass)) { + return; + } + var _elms = this.parentElement.getElementsByTagName("*"); + + // There should be at least 1 element in addition to the firstDiv & finalDiv + if (_elms.length <= 2) { + return; + } + + // Get the tabIndex set to the firstDiv (which is the lowest) + var _lowestTabIndex = _elms[0].tabIndex; + + // If there are positive tabIndices, set focus to the element with the lowest tabIndex. + // Otherwise set focus to the first focusable element in DOM order. + if (_lowestTabIndex) { + for (var i = 1; i < _elms.length - 1; i++) { + if (_elms[i].tabIndex === _lowestTabIndex) { + _elms[i].focus(); + break; + } + } + } else { + for (i = 1; i < _elms.length - 1; i++) { + // Skip
with undefined tabIndex (To work around Win8 bug #622245) + if ((_elms[i].tagName !== "DIV") || (_elms[i].getAttribute("tabIndex") !== null)) { + _elms[i].focus(); + + if (_elms[i] === document.activeElement) { + break; + } + } + } + } + }, + + // Create and add a new first div to the beginning of the list + _addFirstDiv: function SettingsFlyout_addFirstDiv() { + var _elms = this._element.getElementsByTagName("*"); + var _minTab = 0; + for (var i = 0; i < _elms.length; i++) { + if ((0 < _elms[i].tabIndex) && (_minTab === 0 || _elms[i].tabIndex < _minTab)) { + _minTab = _elms[i].tabIndex; + } + } + var firstDiv = document.createElement("div"); + firstDiv.className = firstDivClass; + firstDiv.style.display = "inline"; + firstDiv.setAttribute("role", "menuitem"); + firstDiv.setAttribute("aria-hidden", "true"); + firstDiv.tabIndex = _minTab; + firstDiv.addEventListener("focus", this._focusOnLastFocusableElementFromParent, false); + + this._element.insertAdjacentElement("AfterBegin", firstDiv); + }, + + // Create and add a new final div to the end of the list + _addFinalDiv: function SettingsFlyout_addFinalDiv() { + var _elms = this._element.getElementsByTagName("*"); + var _maxTab = 0; + for (var i = 0; i < _elms.length; i++) { + if (_elms[i].tabIndex > _maxTab) { + _maxTab = _elms[i].tabIndex; + } + } + var finalDiv = document.createElement("div"); + finalDiv.className = finalDivClass; + finalDiv.style.display = "inline"; + finalDiv.setAttribute("role", "menuitem"); + finalDiv.setAttribute("aria-hidden", "true"); + finalDiv.tabIndex = _maxTab; + finalDiv.addEventListener("focus", this._focusOnFirstFocusableElementFromParent, false); + + this._element.appendChild(finalDiv); + }, + + _writeProfilerMark: function SettingsFlyout_writeProfilerMark(text) { + msWriteProfilerMark("WinJS.UI.SettingsFlyout:" + this._id + ":" + text); + } + }); + + // Statics + SettingsFlyout.show = function () { + /// + /// + /// Shows the SettingsPane UI, if hidden, regardless of other states. + /// + /// + /// + if (WinJS.Utilities.hasWinRT) { + Windows.UI.ApplicationSettings.SettingsPane.show(); + } + // And hide the WWA one + var elements = document.querySelectorAll('div[data-win-control="WinJS.UI.SettingsFlyout"]'); + var len = elements.length; + for (var i = 0; i < len; i++) { + var settingsFlyout = elements[i].winControl; + if (settingsFlyout) { + settingsFlyout._dismiss(); + } + } + }; + + var _settingsEvent = { event: undefined }; + SettingsFlyout.populateSettings = function (e) { + /// + /// + /// Loads a portion of the SettingsFlyout. Your app calls this when the user invokes a settings command and the WinJS.Application.onsettings event occurs. + /// + /// + /// An object that contains information about the event, received from the WinJS.Application.onsettings event. The detail property of this object contains + /// the applicationcommands sub-property that you set to an array of settings commands. + /// + /// + /// + _settingsEvent.event = e.detail; + + if (_settingsEvent.event.applicationcommands) { + var n = Windows.UI.ApplicationSettings; + Object.keys(_settingsEvent.event.applicationcommands).forEach(function (name) { + var setting = _settingsEvent.event.applicationcommands[name]; + if (!setting.title) { setting.title = name; } + var command = new n.SettingsCommand(name, setting.title, thisWinUI.SettingsFlyout._onSettingsCommand); + _settingsEvent.event.e.request.applicationCommands.append(command); + }); + } + }; + + SettingsFlyout._onSettingsCommand = function (command) { + var id = command.id; + if (_settingsEvent.event.applicationcommands && _settingsEvent.event.applicationcommands[id]) { + thisWinUI.SettingsFlyout.showSettings(id, _settingsEvent.event.applicationcommands[id].href); + } + }; + + SettingsFlyout.showSettings = function (id, path) { + /// + /// + /// Show the SettingsFlyout using the settings element identifier (ID) and the path of the page that contains the settings element. + /// + /// + /// The ID of the settings element. + /// + /// + /// The path of the page that contains the settings element. + /// + /// + /// + var control = _getChildSettingsControl(document, id); + if (control) { + control.show(); + } else if (path) { + var divElement = document.createElement("div"); + divElement = document.body.appendChild(divElement); + WinJS.UI.Pages.render(path, divElement).then(function () { + control = _getChildSettingsControl(divElement, id); + if (control) { + control._fragmentDiv = divElement; + control.show(); + } else { + document.body.removeChild(divElement); + } + }); + } else { + throw new WinJS.ErrorFromName("WinJS.UI.SettingsFlyout.BadReference", strings.badReference); + } + }; + + var strings = { + get ariaLabel() { return WinJS.Resources._getWinJSString("ui/settingsFlyoutAriaLabel").value; }, + get badReference() { return WinJS.Resources._getWinJSString("ui/badReference").value; }, + get backbuttonAriaLabel() { return WinJS.Resources._getWinJSString("ui/backbuttonarialabel").value; }, + get widthDeprecationMessage() { return WinJS.Resources._getWinJSString("ui/settingsFlyoutWidthIsDeprecated").value; }, + }; + + return SettingsFlyout; + }) + }); + + +})(WinJS); + + +(function itemContainerInit(global, WinJS, undefined) { + "use strict"; + + var utilities = WinJS.Utilities; + var createEvent = utilities._createEventProperty; + var eventNames = { + invoked: "invoked", + selectionchanging: "selectionchanging", + selectionchanged: "selectionchanged" + }; + + WinJS.Namespace.define("WinJS.UI", { + /// + /// Defines an item that can be pressed, swiped, and dragged. + /// + /// + /// + /// HTML content
+ /// ]]> + /// Raised when the user taps or clicks the item. + /// Raised before the item is selected or deselected. + /// Raised after the item is selected or deselected. + /// Main container for the selection item control. + /// The background of a selection checkmark. + /// A selection checkmark. + /// Used to display an outline when the main container has keyboard focus. + /// + /// + /// + ItemContainer: WinJS.Namespace._lazy(function () { + var strings = { + get duplicateConstruction() { return WinJS.Resources._getWinJSString("ui/duplicateConstruction").value; } + }; + + var ItemContainer = WinJS.Class.define(function ItemContainer_ctor(element, options) { + /// + /// + /// Creates a new ItemContainer control. + /// + /// + /// The DOM element that hosts the ItemContainer control. + /// + /// + /// An object that contains one or more property/value pairs to apply to the new control. + /// Each property of the options object corresponds to one of the control's properties or events. + /// Event names must begin with "on". For example, to provide a handler for the selectionchanging event, + /// add a property named "onselectionchanging" to the options object and set its value to the event handler. + /// + /// + /// The new ItemContainer control. + /// + /// + element = element || document.createElement("DIV"); + this._id = element.id || element.uniqueID; + this._writeProfilerMark("constructor,StartTM"); + + options = options || {}; + + if (element.winControl) { + throw new WinJS.ErrorFromName("WinJS.UI.ItemContainer.DuplicateConstruction", strings.duplicateConstruction); + } + + // Attaching JS control to DOM element + element.winControl = this; + + this._element = element; + WinJS.Utilities.addClass(element, "win-disposable"); + this._selectionMode = WinJS.UI.SelectionMode.single; + this._draggable = false; + this._pressedEntity = { type: WinJS.UI.ObjectType.item, index: WinJS.UI._INVALID_INDEX }; + + this.tapBehavior = WinJS.UI.TapBehavior.invokeOnly; + this.swipeOrientation = WinJS.UI.Orientation.vertical; + this.swipeBehavior = WinJS.UI.SwipeBehavior.select; + + WinJS.Utilities.addClass(this.element, WinJS.UI.ItemContainer._ClassName.itemContainer + " " + WinJS.UI._containerClass); + + this._setupInternalTree(); + + this._selection = new WinJS.UI._SingleItemSelectionManager(element, this._itemBox); + this._setTabIndex(); + + WinJS.UI.setOptions(this, options); + + this._mutationObserver = new MutationObserver(this._itemPropertyChange.bind(this)); + this._mutationObserver.observe(element, { attributes: true, attributeFilter: ["aria-selected"] }); + this._setAriaRole(); + + var that = this; + if (!this.selectionDisabled) { + setImmediate(function () { + that._setDirectionClass(); + }); + } + this._itemEventsHandler = new WinJS.UI._ItemEventsHandler(Object.create({ + containerFromElement: function (element) { + return that.element; + }, + indexForItemElement: function (element) { + return that.element.uniqueID; + }, + indexForHeaderElement: function () { + return WinJS.UI._INVALID_INDEX; + }, + itemBoxAtIndex: function (index) { + return that._itemBox; + }, + itemAtIndex: function (index) { + return that.element; + }, + headerAtIndex: function (index) { + return null; + }, + containerAtIndex: function (index) { + return that.element; + }, + isZombie: function () { + return this._disposed; + }, + getItemPosition: function (index) { + return that._getItemPosition(); + }, + rtl: function () { + return that._rtl(); + }, + fireInvokeEvent: function (itemIndex, itemElement) { + that._fireInvokeEvent(); + }, + verifySelectionAllowed: function (index) { + return that._verifySelectionAllowed(); + }, + changeFocus: function (newFocus, skipSelection, ctrlKeyDown, skipEnsureVisible, keyboardFocused) { }, + selectRange: function (firstIndex, lastIndex, additive) { + return that._selection.set({ firstIndex: firstIndex, lastIndex: lastIndex }); + } + }, { + pressedEntity: { + get: function () { + return that._pressedEntity; + }, + set: function (value) { + that._pressedEntity = value; + } + }, + pressedElement: { + enumerable: true, + set: function (value) { + that._pressedElement = value; + } + }, + eventHandlerRoot: { + enumerable: true, + get: function () { + return that.element; + } + }, + swipeBehavior: { + enumerable: true, + get: function () { + return that._swipeBehavior; + } + }, + selectionMode: { + enumerable: true, + get: function () { + return that._selectionMode; + } + }, + accessibleItemClass: { + enumerable: true, + get: function () { + // CSS class of the element with the aria role + return WinJS.UI._containerClass; + } + }, + canvasProxy: { + enumerable: true, + get: function () { + return that._captureProxy; + } + }, + tapBehavior: { + enumerable: true, + get: function () { + return that._tapBehavior; + } + }, + draggable: { + enumerable: true, + get: function () { + return that._draggable; + } + }, + selection: { + enumerable: true, + get: function () { + return that._selection; + } + }, + horizontal: { + enumerable: true, + get: function () { + return that._swipeOrientation === WinJS.UI.Orientation.vertical; + } + }, + customFootprintParent: { + enumerable: true, + get: function () { + // Use the main container as the footprint + return null; + } + }, + skipPreventDefaultOnPointerDown: { + enumerable: true, + get: function () { + return true; + } + } + })); + + function eventHandler(eventName, caseSensitive, capture) { + return { + name: (caseSensitive ? eventName : eventName.toLowerCase()), + handler: function (eventObject) { + that["_on" + eventName](eventObject); + }, + capture: capture + }; + } + var events = [ + eventHandler("MSManipulationStateChanged", true, true), + eventHandler("PointerDown"), + eventHandler("Click"), + eventHandler("PointerUp"), + eventHandler("PointerCancel"), + eventHandler("LostPointerCapture"), + eventHandler("ContextMenu"), + eventHandler("MSHoldVisual", true), + eventHandler("Focus"), + eventHandler("Blur"), + eventHandler("DragStart"), + eventHandler("DragEnd"), + eventHandler("KeyDown") + ]; + events.forEach(function (eventHandler) { + that.element.addEventListener(eventHandler.name, eventHandler.handler, !!eventHandler.capture); + }); + + this._writeProfilerMark("constructor,StopTM"); + }, { + /// + element: { + get: function () { + return this._element; + } + }, + + /// + /// Gets or sets a value that specifies whether the item can be dragged. The default value is false. + /// + /// + draggable: { + get: function () { + return this._draggable; + }, + + set: function (value) { + if (this._draggable !== value) { + this._draggable = value; + this._updateDraggableAttribute(); + } + } + }, + + /// + /// Gets or sets a value that specifies whether the item is selected. + /// + selected: { + get: function () { + return this._selection.selected; + }, + + set: function (value) { + if (this._selection.selected !== value) { + this._selection.selected = value; + } + } + }, + + /// + /// Gets or sets the swipe orientation of the ItemContainer control. + /// The default value is "vertical". + /// + swipeOrientation: { + get: function () { + return this._swipeOrientation; + }, + set: function (value) { + if (value === WinJS.UI.Orientation.vertical) { + WinJS.Utilities.removeClass(this.element, WinJS.UI.ItemContainer._ClassName.horizontal); + WinJS.Utilities.addClass(this.element, WinJS.UI.ItemContainer._ClassName.vertical); + } else { + value = WinJS.UI.Orientation.horizontal; + WinJS.Utilities.removeClass(this.element, WinJS.UI.ItemContainer._ClassName.vertical); + WinJS.Utilities.addClass(this.element, WinJS.UI.ItemContainer._ClassName.horizontal); + } + this._swipeOrientation = value; + } + }, + + /// + /// Gets or sets how the ItemContainer control reacts when the user taps or clicks an item. + /// The tap or click can invoke the item, select it and invoke it, or have no effect. + /// Possible values: "toggleSelect", "invokeOnly", and "none". The default value is "invokeOnly". + /// + tapBehavior: { + get: function () { + return this._tapBehavior; + }, + set: function (value) { + this._tapBehavior = value; + this._setAriaRole(); + } + }, + + /// + /// Gets or sets how the ItemContainer control reacts to the swipe interaction. + /// The swipe gesture can select the item or it can have no effect on the current selection. + /// Possible values: "select", "none". The default value is: "select". + /// + /// + swipeBehavior: { + get: function () { + return this._swipeBehavior; + }, + set: function (value) { + this._swipeBehavior = value; + this._setSwipeClass(); + } + }, + + /// + /// Gets or sets whether the item selection is disabled. The default value is false. + /// + selectionDisabled: { + get: function () { + return this._selectionMode === WinJS.UI.SelectionMode.none; + }, + + set: function (value) { + if (value) { + this._selectionMode = WinJS.UI.SelectionMode.none; + } else { + this._setDirectionClass(); + this._selectionMode = WinJS.UI.SelectionMode.single; + } + this._setSwipeClass(); + this._setAriaRole(); + } + }, + + /// + /// Raised when the item is invoked. You can use the tapBehavior property to specify whether taps and clicks invoke the item. + /// + oninvoked: createEvent(eventNames.invoked), + + /// + /// Raised just before the item is selected or deselected. + /// + onselectionchanging: createEvent(eventNames.selectionchanging), + + /// + /// Raised after the item is selected or deselected. + /// + onselectionchanged: createEvent(eventNames.selectionchanged), + + forceLayout: function () { + /// + /// + /// Forces the ItemContainer control to update its layout. + /// Use this function when the reading direction of the app changes after the control has been initialized. + /// + /// + this._forceLayout(); + }, + + dispose: function () { + /// + /// + /// Disposes this control. + /// + /// + + if (this._disposed) { + return; + } + this._disposed = true; + + this._itemEventsHandler.dispose(); + WinJS.Utilities.disposeSubTree(this.element); + }, + + _onMSManipulationStateChanged: function ItemContainer_onMSManipulationStateChanged(eventObject) { + this._itemEventsHandler.onMSManipulationStateChanged(eventObject); + }, + + _onPointerDown: function ItemContainer_onPointerDown(eventObject) { + this._itemEventsHandler.onPointerDown(eventObject); + }, + + _onClick: function ItemContainer_onClick(eventObject) { + this._itemEventsHandler.onClick(eventObject); + }, + + _onPointerUp: function ItemContainer_onPointerUp(eventObject) { + if (utilities.hasClass(this._itemBox, WinJS.UI._itemFocusClass)) { + this._onBlur(eventObject); + } + this._itemEventsHandler.onPointerUp(eventObject); + }, + + _onPointerCancel: function ItemContainer_onPointerCancel(eventObject) { + this._itemEventsHandler.onPointerCancel(eventObject); + }, + + _onLostPointerCapture: function ItemContainer_onLostPointerCapture(eventObject) { + this._itemEventsHandler.onLostPointerCapture(eventObject); + }, + + _onContextMenu: function ItemContainer_onContextMenu(eventObject) { + this._itemEventsHandler.onContextMenu(eventObject); + }, + + _onMSHoldVisual: function ItemContainer_onMSHoldVisual(eventObject) { + this._itemEventsHandler.onMSHoldVisual(eventObject); + }, + + _onFocus: function ItemContainer_onFocus(eventObject) { + if (this._itemBox.querySelector("." + WinJS.UI._itemFocusOutlineClass) || !WinJS.UI._keyboardSeenLast) { + return; + } + utilities.addClass(this._itemBox, WinJS.UI._itemFocusClass); + var outline = document.createElement("div"); + outline.className = WinJS.UI._itemFocusOutlineClass; + this._itemBox.appendChild(outline); + }, + + _onBlur: function ItemContainer_onBlur(eventObject) { + utilities.removeClass(this._itemBox, WinJS.UI._itemFocusClass); + var outline = this._itemBox.querySelector("." + WinJS.UI._itemFocusOutlineClass); + if (outline) { + outline.parentNode.removeChild(outline); + } + }, + + _onDragStart: function ItemContainer_onDragStart(eventObject) { + // Drag shouldn't be initiated when the user holds down the mouse on a win-interactive element and moves. + // The problem is that the dragstart event's srcElement+target will both be an itembox (which has draggable=true), so we can't check for win-interactive in the dragstart event handler. + // The itemEventsHandler sets our _pressedElement field on PointerDown, so we use that instead when checking for interactive. + if (this._pressedElement && this._itemEventsHandler._isInteractive(this._pressedElement)) { + eventObject.preventDefault(); + } else { + this._dragging = true; + var that = this; + // We delay setting the win-dragsource CSS class so that IE has time to create a thumbnail before me make it opaque + setImmediate(function () { + if (that._dragging) { + utilities.addClass(that._itemBox, WinJS.UI._dragSourceClass); + } + }); + } + }, + + _onDragEnd: function ItemContainer_onDragEnd(eventObject) { + this._dragging = false; + utilities.removeClass(this._itemBox, WinJS.UI._dragSourceClass); + this._itemEventsHandler.resetPointerDownState(); + }, + + _onKeyDown: function ItemContainer_onKeyDown(eventObject) { + if (!this._itemEventsHandler._isInteractive(eventObject.srcElement)) { + var Key = utilities.Key, + keyCode = eventObject.keyCode, + swipeEnabled = this._swipeBehavior === WinJS.UI.SwipeBehavior.select; + + var handled = false; + if (!eventObject.ctrlKey && keyCode === Key.enter) { + var allowed = this._verifySelectionAllowed(); + if (allowed.canTapSelect) { + this.selected = !this.selected; + } + this._fireInvokeEvent(); + handled = true; + } else if (eventObject.ctrlKey && keyCode === Key.enter || + (swipeEnabled && eventObject.shiftKey && keyCode === Key.F10) || + (swipeEnabled && keyCode === Key.menu) || + keyCode === Key.space) { + if (!this.selectionDisabled) { + this.selected = !this.selected; + try { + this.element.setActive(); + } catch (e) { } + handled = true; + } + } else if (keyCode === Key.escape && this.selected) { + this.selected = false; + handled = true; + } + + if (handled) { + eventObject.stopPropagation(); + eventObject.preventDefault(); + } + } + }, + + _setTabIndex: function ItemContainer_setTabIndex() { + var currentTabIndex = this.element.getAttribute("tabindex"); + if (!currentTabIndex) { + // Set the tabindex to 0 only if the application did not already + // provide a tabindex + this.element.setAttribute("tabindex", "0") + } + }, + + _rtl: function ItemContainer_rtl() { + if (typeof this._cachedRTL !== "boolean") { + this._cachedRTL = window.getComputedStyle(this.element, null).direction === "rtl"; + } + return this._cachedRTL; + }, + + _setDirectionClass: function ItemContainer_setDirectionClass() { + utilities[this._rtl() ? "addClass" : "removeClass"](this.element, WinJS.UI._rtlListViewClass); + }, + + _forceLayout: function ItemContainer_forceLayout() { + this._cachedRTL = window.getComputedStyle(this.element, null).direction === "rtl"; + this._setDirectionClass(); + }, + + _getItemPosition: function ItemContainer_getItemPosition() { + var container = this.element; + if (container) { + return WinJS.Promise.wrap({ + left: (this._rtl() ? + container.offsetParent.offsetWidth - container.offsetLeft - container.offsetWidth : + container.offsetLeft), + top: container.offsetTop, + totalWidth: utilities.getTotalWidth(container), + totalHeight: utilities.getTotalHeight(container), + contentWidth: utilities.getContentWidth(container), + contentHeight: utilities.getContentHeight(container) + }); + } else { + return WinJS.Promise.cancel; + } + }, + + _itemPropertyChange: function ItemContainer_itemPropertyChange(list) { + if (this._disposed) { return; } + + var container = list[0].target; + var ariaSelected = container.getAttribute("aria-selected") === "true"; + + // Only respond to aria-selected changes coming from UIA. This check + // relies on the fact that, in renderSelection, we update the selection + // visual before aria-selected. + if (ariaSelected !== WinJS.UI._isSelectionRendered(this._itemBox)) { + if (this.selectionDisabled) { + // Revert the change made by UIA since the control has selection disabled + WinJS.UI._setAttribute(container, "aria-selected", !ariaSelected); + } else { + this.selected = ariaSelected; + // Revert the change because the update was prevented on the selectionchanging event + if (ariaSelected !== this.selected) { + WinJS.UI._setAttribute(container, "aria-selected", !ariaSelected); + } + } + } + }, + + _setSwipeClass: function ItemContainer_setSwipeClass() { + // We apply an -ms-touch-action style to block panning and swiping from occurring at the same time. + if ((this._swipeBehavior === WinJS.UI.SwipeBehavior.select && this._selectionMode !== WinJS.UI.SelectionMode.none) || this._draggable) { + utilities.addClass(this._element, WinJS.UI._swipeableClass); + } else { + utilities.removeClass(this._element, WinJS.UI._swipeableClass); + } + }, + + _updateDraggableAttribute: function ItemContainer_setSwipeClass() { + this._setSwipeClass(); + this._itemBox.setAttribute("draggable", this._draggable); + }, + + _verifySelectionAllowed: function ItemContainer_verifySelectionAllowed() { + if (this._selectionMode !== WinJS.UI.SelectionMode.none && (this._tapBehavior === WinJS.UI.TapBehavior.toggleSelect || this._swipeBehavior === WinJS.UI.SwipeBehavior.select)) { + var canSelect = this._selection.fireSelectionChanging(); + return { + canSelect: canSelect, + canTapSelect: canSelect && this._tapBehavior === WinJS.UI.TapBehavior.toggleSelect + }; + } else { + return { + canSelect: false, + canTapSelect: false + }; + } + }, + + _setupInternalTree: function ItemContainer_setupInternalTree() { + var item = document.createElement("div"); + item.className = WinJS.UI._itemClass; + this._captureProxy = document.createElement("div"); + this._itemBox = document.createElement("div"); + this._itemBox.className = WinJS.UI._itemBoxClass; + var child = this.element.firstChild; + while (child) { + var sibling = child.nextSibling; + item.appendChild(child); + child = sibling; + } + this.element.appendChild(this._itemBox); + this._itemBox.appendChild(item); + this.element.appendChild(this._captureProxy); + }, + + _fireInvokeEvent: function ItemContainer_fireInvokeEvent() { + if (this.tapBehavior !== WinJS.UI.TapBehavior.none) { + var eventObject = document.createEvent("CustomEvent"); + eventObject.initCustomEvent(eventNames.invoked, true, false, {}); + this.element.dispatchEvent(eventObject); + } + }, + + _setAriaRole: function ItemContainer_setAriaRole() { + if (!this.element.getAttribute("role") || this._usingDefaultItemRole) { + this._usingDefaultItemRole = true; + var defaultItemRole; + if (this.tapBehavior === WinJS.UI.TapBehavior.none && this.selectionDisabled) { + defaultItemRole = "listitem"; + } else { + defaultItemRole = "option"; + } + WinJS.UI._setAttribute(this.element, "role", defaultItemRole); + } + }, + + _writeProfilerMark: function ItemContainer_writeProfilerMark(text) { + var message = "WinJS.UI.ItemContainer:" + this._id + ":" + text; + msWriteProfilerMark(message); + WinJS.log && WinJS.log(message, null, "itemcontainerprofiler"); + } + }, { + // Names of classes used by the ItemContainer. + _ClassName: { + itemContainer: "win-itemcontainer", + vertical: "win-vertical", + horizontal: "win-horizontal", + } + }); + WinJS.Class.mix(ItemContainer, WinJS.UI.DOMEventMixin); + return ItemContainer; + }), + + _SingleItemSelectionManager: WinJS.Namespace._lazy(function () { + return WinJS.Class.define(function SingleItemSelectionManager_ctor(element, itemBox) { + this._selected = false; + this._element = element; + this._itemBox = itemBox; + }, { + selected: { + get: function () { + return this._selected; + }, + set: function (value) { + value = !!value; + if (this._selected !== value) { + if (this.fireSelectionChanging()) { + this._selected = value; + WinJS.UI._ItemEventsHandler.renderSelection(this._itemBox, this._element, value, true, this._element); + this.fireSelectionChanged(); + } + } + } + }, + + count: function SingleItemSelectionManager_count() { + return this._selected ? 1 : 0; + }, + + getIndices: function SingleItemSelectionManager_getIndices() { + // not used + }, + + getItems: function SingleItemSelectionManager_getItems() { + // not used + }, + + getRanges: function SingleItemSelectionManager_getRanges() { + // not used + }, + + isEverything: function SingleItemSelectionManager_isEverything() { + return false; + }, + + set: function SingleItemSelectionManager_set(items) { + this.selected = true; + }, + + clear: function SingleItemSelectionManager_clear() { + this.selected = false; + }, + + add: function SingleItemSelectionManager_add(items) { + this.selected = true; + }, + + remove: function SingleItemSelectionManager_remove(items) { + this.selected = false; + }, + + selectAll: function SingleItemSelectionManager_selectAll() { + // not used + }, + + fireSelectionChanging: function SingleItemSelectionManager_fireSelectionChanging() { + var eventObject = document.createEvent("CustomEvent"); + eventObject.initCustomEvent(eventNames.selectionchanging, true, true, {}); + return this._element.dispatchEvent(eventObject); + }, + + fireSelectionChanged: function ItemContainer_fireSelectionChanged() { + var eventObject = document.createEvent("CustomEvent"); + eventObject.initCustomEvent(eventNames.selectionchanged, true, false, {}); + this._element.dispatchEvent(eventObject); + }, + + _isIncluded: function SingleItemSelectionManager_isIncluded(index) { + return this._selected; + }, + + _getFocused: function SingleItemSelectionManager_getFocused(index) { + return { type: WinJS.UI.ObjectType.item, index: WinJS.UI._INVALID_INDEX }; + } + }) + }) + }); + +})(this, WinJS); + +(function KeyboardBehaviorInit(global, WinJS, undefined) { + "use strict"; + + // not supported in WebWorker + if (!global.document) { + return; + } + + WinJS.UI._keyboardSeenLast = false; + + window.addEventListener("pointerdown", function (ev) { + if (WinJS.UI._keyboardSeenLast) { + WinJS.UI._keyboardSeenLast = false; + } + }, true); + + window.addEventListener("keydown", function (ev) { + if (!WinJS.UI._keyboardSeenLast) { + WinJS.UI._keyboardSeenLast = true; + } + }, true); + + WinJS.Namespace.define("WinJS.UI", { + _WinKeyboard: function (element) { + // Win Keyboard behavior is a solution that would be similar to -ms-keyboard-focus. + // It monitors the last input (keyboard/mouse) and adds/removes a win-keyboard class + // so that you can style .foo.win-keyboard:focus vs .foo:focus to add a keyboard rect + // on an item only when the last input method was keyboard. + // Reminder: Touch edgy does not count as an input method. + element.addEventListener("pointerdown", function (ev) { + // In case pointer down came on the active element. + WinJS.Utilities.removeClass(ev.srcElement, "win-keyboard"); + }, true); + element.addEventListener("keydown", function (ev) { + WinJS.Utilities.addClass(ev.srcElement, "win-keyboard"); + }, true); + element.addEventListener("focusin", function (ev) { + WinJS.UI._keyboardSeenLast && WinJS.Utilities.addClass(ev.srcElement, "win-keyboard"); + }, true); + element.addEventListener("focusout", function (ev) { + WinJS.Utilities.removeClass(ev.srcElement, "win-keyboard"); + }, true); + }, + _KeyboardBehavior: WinJS.Namespace._lazy(function () { + return WinJS.Class.define(function KeyboardBehavior_ctor(element, options) { + // KeyboardBehavior allows you to easily convert a bunch of tabable elements into a single tab stop with + // navigation replaced by keyboard arrow (Up/Down/Left/Right) + Home + End + Custom keys. + // + // Example use cases: + // + // 1 Dimensional list: FixedDirection = height and FixedSize = 1; + // [1] [ 2 ] [ 3 ] [4] [ 5 ]... + // + // 2 Dimensional list: FixedDirection = height and FixedSize = 2; + // [1] [3] [5] [7] ... + // [2] [4] [6] [8] + // + // 1 Dimensional list: FixedDirection = width and FixedSize = 1; + // [ 1 ] + // - - + // | | + // | 2 | + // | | + // - - + // [ 3 ] + // [ 4 ] + // ... + // + // 2 Dimensional list: FixedDirection = width and FixedSize = 2; + // [1][2] + // [3][4] + // [5][6] + // ... + // + // Currently it is a "behavior" instead of a "control" so it can be attached to the same element as a + // winControl. The main scenario for this would be to attach it to the same element as a repeater. + // + // It also blocks "Portaling" where you go off the end of one column and wrap around to the other + // column. It also blocks "Carousel" where you go from the end of the list to the beginning. + // + // Keyboarding behavior supports nesting. It supports your tab stops having sub tab stops. If you want + // an interactive element within the tab stop you need to use the win-interactive classname or during the + // keydown event stop propogation so that the event is skipped. + // + // If you have custom keyboarding the getAdjacent API is provided. This can be used to enable keyboarding + // in multisize 2d lists or custom keyboard commands. PageDown and PageUp are the most common since this + // behavior does not detect scrollers. + // + // It also allows developers to show/hide keyboard focus rectangles themselves. + // + // It has an API called currentIndex so that Tab (or Shift+Tab) or a developer imitating Tab will result in + // the correct item having focus. + // + // It also allows an element to be represented as 2 arrow stops (commonly used for a split button) by calling + // the _getFocusInto API on the child element's winControl if it exists. + + element = element || document.createElement("DIV"); + options = options || {}; + + element._keyboardBehavior = this; + this._element = element; + + this._fixedDirection = WinJS.UI._KeyboardBehavior.FixedDirection.width; + this._fixedSize = 1; + this._currentIndex = 0; + + WinJS.UI.setOptions(this, options); + + this._element.addEventListener('keydown', this._keyDownHandler.bind(this)); + this._element.addEventListener('pointerdown', this._MSPointerDownHandler.bind(this)); + this._element.addEventListener("beforeactivate", this._beforeActivateHandler.bind(this)); + }, { + element: { + get: function () { + return this._element; + } + }, + + fixedDirection: { + get: function () { + return this._fixedDirection; + }, + set: function (value) { + this._fixedDirection = value; + } + }, + + fixedSize: { + get: function () { + return this._fixedSize; + }, + set: function (value) { + if (+value === value) { + value = Math.max(1, value); + this._fixedSize = value; + } + } + }, + + currentIndex: { + get: function () { + if (this._element.children.length > 0) { + return this._currentIndex; + } + return -1; + }, + set: function (value) { + if (+value === value) { + var length = this._element.children.length; + value = Math.max(0, Math.min(length - 1, value)); + this._currentIndex = value; + } + } + }, + + getAdjacent: { + get: function () { + return this._getAdjacent + }, + set: function (value) { + this._getAdjacent = value; + } + }, + + _keyDownHandler: function _KeyboardBehavior_keyDownHandler(ev) { + if (!ev.altKey) { + if (ev.srcElement.msMatchesSelector(".win-interactive, .win-interactive *")) { + return; + } + var blockScrolling = false; + + var newIndex = this.currentIndex; + var maxIndex = this._element.children.length - 1; + var minIndex = 0; + + var rtl = getComputedStyle(this._element).direction === "rtl"; + var leftStr = rtl ? "Right" : "Left"; + var rightStr = rtl ? "Left" : "Right"; + + var targetIndex = this.getAdjacent && this.getAdjacent(newIndex, ev.key); + if (+targetIndex === targetIndex) { + blockScrolling = true; + newIndex = targetIndex; + } else { + var modFixedSize = newIndex % this.fixedSize; + + if (ev.key === leftStr) { + blockScrolling = true; + if (this.fixedDirection === WinJS.UI._KeyboardBehavior.FixedDirection.width) { + if (modFixedSize !== 0) { + newIndex--; + } + } else { + if (newIndex >= this.fixedSize) { + newIndex -= this.fixedSize; + } + } + } else if (ev.key === rightStr) { + blockScrolling = true; + if (this.fixedDirection === WinJS.UI._KeyboardBehavior.FixedDirection.width) { + if (modFixedSize !== this.fixedSize - 1) { + newIndex++; + } + } else { + if (newIndex + this.fixedSize - modFixedSize <= maxIndex) { + newIndex += this.fixedSize; + } + } + } else if (ev.key === "Up") { + blockScrolling = true; + if (this.fixedDirection === WinJS.UI._KeyboardBehavior.FixedDirection.height) { + if (modFixedSize !== 0) { + newIndex--; + } + } else { + if (newIndex >= this.fixedSize) { + newIndex -= this.fixedSize; + } + } + } else if (ev.key === "Down") { + blockScrolling = true; + if (this.fixedDirection === WinJS.UI._KeyboardBehavior.FixedDirection.height) { + if (modFixedSize !== this.fixedSize - 1) { + newIndex++; + } + } else { + if (newIndex + this.fixedSize - modFixedSize <= maxIndex) { + newIndex += this.fixedSize; + } + } + } else if (ev.key === "Home") { + blockScrolling = true; + newIndex = 0; + } else if (ev.key === "End") { + blockScrolling = true; + newIndex = this._element.children.length - 1; + } else if (ev.key === "PageUp") { + blockScrolling = true; + } else if (ev.key === "PageDown") { + blockScrolling = true; + } + } + + newIndex = Math.max(0, Math.min(this._element.children.length - 1, newIndex)); + + if (newIndex !== this.currentIndex) { + this._focus(newIndex, ev.key); + + // Allow KeyboardBehavior to be nested + if (ev.key === leftStr || ev.key === rightStr || ev.key === "Up" || ev.key === "Down") { + ev.stopPropagation(); + } + } + + if (blockScrolling) { + ev.preventDefault(); + } + } + }, + + _focus: function _KeyboardBehavior_focus(index, key) { + index = (+index === index) ? index : this.currentIndex; + + var elementToFocus = this._element.children[index]; + if (elementToFocus) { + if (elementToFocus.winControl && elementToFocus.winControl._getFocusInto) { + elementToFocus = elementToFocus.winControl._getFocusInto(key); + } + + this.currentIndex = index; + + try { + elementToFocus.setActive(); + } catch (e) { + } + } + }, + + _MSPointerDownHandler: function _KeyboardBehavior_MSPointerDownHandler(ev) { + var srcElement = ev.srcElement; + if (srcElement === this.element) { + return; + } + + while (srcElement.parentNode !== this.element) { + srcElement = srcElement.parentNode; + } + + var index = -1; + while (srcElement) { + index++; + srcElement = srcElement.previousElementSibling; + } + + this.currentIndex = index; + }, + + _beforeActivateHandler: function _KeyboardBehavior_beforeActivateHandler(ev) { + var allowActivate = false; + if (this._element.children.length) { + var currentItem = this._element.children[this.currentIndex]; + if (currentItem === ev.srcElement || currentItem.contains(ev.srcElement)) { + allowActivate = true; + } + } + + if (!allowActivate) { + ev.stopPropagation(); + ev.preventDefault(); + } + } + }, { + FixedDirection: { + height: "height", + width: "width" + } + }) + }) + }); + +})(this, WinJS); +(function NavBarInit(global, WinJS, undefined) { + "use strict"; + + WinJS.Namespace.define("WinJS.UI", { + /// + /// + /// Displays navigation commands in a toolbar that the user can show or hide. + /// + /// + /// + /// + /// + /// + ///
+ ///
+ ///
+ ///
]]> + /// Raised just before showing the NavBar. + /// Raised immediately after an NavBar is fully shown. + /// Raised just before hiding the NavBar. + /// Raised immediately after the NavBar is fully hidden. + /// Fired when children of NavBar control have been processed from a WinJS.UI.processAll call. + /// Styles the entire NavBar. + /// + /// + /// + NavBar: WinJS.Namespace._lazy(function () { + var childrenProcessedEventName = "childrenprocessed"; + var createEvent = WinJS.Utilities._createEventProperty; + + return WinJS.Class.derive(WinJS.UI.AppBar, function NavBar_ctor(element, options) { + /// + /// + /// Creates a new NavBar. + /// + /// + /// The DOM element that will host the new NavBar control. + /// + /// + /// An object that contains one or more property/value pairs to apply to the new control. Each property of the options object corresponds to one of the control's + /// properties or events. + /// + /// + /// The new NavBar control. + /// + /// + /// + + options = options || {}; + + // Shallow copy object so we can modify it. + options = WinJS.Utilities._shallowCopy(options); + + // Default to Placement = Top and Layout = Custom + options.placement = options.placement || "top"; + options.layout = "custom"; + + WinJS.UI.AppBar.call(this, element, options); + + this._element.addEventListener("beforeshow", this._handleBeforeShow.bind(this)); + + WinJS.Utilities.addClass(this.element, WinJS.UI.NavBar._ClassName.navbar); + + if (window.Windows && Windows.ApplicationModel && Windows.ApplicationModel.DesignMode && Windows.ApplicationModel.DesignMode.designModeEnabled) { + this._processChildren(); + } else { + WinJS.Utilities.Scheduler.schedule(this._processChildren.bind(this), WinJS.Utilities.Scheduler.Priority.idle, null, "WinJS.UI.NavBar.processChildren"); + } + }, { + // Block others from setting the layout property. + + /// + /// The layout of the NavBar contents. + /// + /// + layout: { + value: "custom", + writable: false + }, + + /// + /// Raised when children of NavBar control have been processed by a WinJS.UI.processAll call. + /// + /// + onchildrenprocessed: createEvent(childrenProcessedEventName), + + _processChildren: function NavBar_processChildren() { + // The NavBar control schedules processAll on its children at idle priority to avoid hurting startup + // performance. If the NavBar is shown before the scheduler gets to the idle job, the NavBar will + // immediately call processAll on its children. If your app needs the children to be processed before + // the scheduled job executes, you may call processChildren to force the processAll call. + if (!this._processed) { + this._processed = true; + + this._writeProfilerMark("processChildren,StartTM"); + var that = this; + var processed = WinJS.Promise.as(); + if (this._processors) { + this._processors.forEach(function (processAll) { + for (var i = 0, len = that.element.children.length; i < len; i++) { + (function (child) { + processed = processed.then(function () { + processAll(child); + }); + }(that.element.children[i])); + } + }); + } + return processed.then( + function () { + that._writeProfilerMark("processChildren,StopTM"); + that._fireEvent(WinJS.UI.NavBar._EventName.childrenProcessed); + }, + function () { + that._writeProfilerMark("processChildren,StopTM"); + that._fireEvent(WinJS.UI.NavBar._EventName.childrenProcessed); + } + ); + } + return WinJS.Promise.wrap(); + }, + + _show: function NavBar_show() { + // Override _show to call processChildren first. + // + if (this.disabled) { + return; + } + var that = this; + this._processChildren().then(function () { + WinJS.UI.AppBar.prototype._show.call(that); + }); + }, + + _handleBeforeShow: function NavBar_handleBeforeShow() { + // Navbar needs to ensure its elements to have their correct height and width after AppBar changes display="none" + // to display="" and AppBar needs the elements to have their final height before it measures its own element height + // to do the slide in animation over the correct amount of pixels. + if (this._disposed) { + return; + } + + var navbarcontainerEls = this.element.querySelectorAll('.win-navbarcontainer'); + for (var i = 0; i < navbarcontainerEls.length; i++) { + navbarcontainerEls[i].winControl.forceLayout(); + } + }, + + _fireEvent: function NavBar_fireEvent(type, detail) { + var event = document.createEvent("CustomEvent"); + event.initCustomEvent(type, true, false, detail || {}); + this.element.dispatchEvent(event); + }, + + _writeProfilerMark: function NavBar_writeProfilerMark(text) { + msWriteProfilerMark("WinJS.UI.NavBar:" + this._id + ":" + text); + } + }, { + _ClassName: { + navbar: "win-navbar" + }, + _EventName: { + childrenProcessed: childrenProcessedEventName + }, + isDeclarativeControlContainer: WinJS.Utilities.markSupportedForProcessing(function (navbar, callback) { + if (navbar._processed) { + for (var i = 0, len = navbar.element.children.length; i < len; i++) { + callback(navbar.element.children[i]); + } + } else { + navbar._processors = navbar._processors || []; + navbar._processors.push(callback); + } + }) + }); + }) + }); + +})(this, WinJS); + +(function NavBarContainerInit(global, WinJS, undefined) { + "use strict"; + + WinJS.Namespace.define("WinJS.UI", { + /// + /// + /// Contains a group of NavBarCommand objects in a NavBar. + /// + /// + /// + /// + /// + /// + ///
+ /// ]]>
+ /// Raised when a NavBarCommand is invoked. + /// Raised when the split button on a NavBarCommand is toggled. + /// Styles the entire NavBarContainer control. + /// + /// Styles the page indication for the NavBarContainer. + /// + /// Styles the page indication for each page. + /// + /// Styles the indication of the current page. + /// + /// Styles the area that contains items for the NavBarContainer. + /// Styles left and right navigation arrows. + /// Styles the left navigation arrow. + /// Styles the right navigation arrow. + /// + /// + /// + NavBarContainer: WinJS.Namespace._lazy(function () { + var buttonFadeDelay = 3000; + var PT_TOUCH = MSPointerEvent.MSPOINTER_TYPE_TOUCH || "touch"; + var MS_MANIPULATION_STATE_STOPPED = 0; + + var createEvent = WinJS.Utilities._createEventProperty; + var eventNames = { + invoked: "invoked", + splittoggle: "splittoggle" + }; + + var strings = { + get duplicateConstruction() { return WinJS.Resources._getWinJSString("ui/duplicateConstruction").value; }, + get navBarContainerViewportAriaLabel() { return WinJS.Resources._getWinJSString("ui/navBarContainerViewportAriaLabel").value; } + }; + + var NavBarContainer = WinJS.Class.define(function NavBarContainer_ctor(element, options) { + /// + /// + /// Creates a new NavBarContainer. + /// + /// + /// The DOM element that will host the NavBarContainer control. + /// + /// + /// An object that contains one or more property/value pairs to apply to the new control. + /// Each property of the options object corresponds to one of the control's properties or events. + /// Event names must begin with "on". + /// + /// + /// The new NavBarContainer. + /// + /// + /// + + element = element || document.createElement("DIV"); + this._id = element.id || element.uniqueID; + this._writeProfilerMark("constructor,StartTM"); + + options = options || {}; + + if (element.winControl) { + throw new WinJS.ErrorFromName("WinJS.UI.NavBarContainer.DuplicateConstruction", strings.duplicateConstruction); + } + + // Attaching JS control to DOM element + element.winControl = this; + this._element = element; + WinJS.Utilities.addClass(this.element, WinJS.UI.NavBarContainer._ClassName.navbarcontainer); + WinJS.Utilities.addClass(this.element, "win-disposable"); + + this._currentManipulationState = MS_MANIPULATION_STATE_STOPPED; + + this._fixedSize = false; + this._maxRows = 1; + this._sizes = {}; + + this._setupTree(); + + this._duringConstructor = true; + + this._dataChangingBound = this._dataChanging.bind(this); + this._dataChangedBound = this._dataChanged.bind(this); + + this._navigatedBound = this._navigated.bind(this); + + WinJS.Navigation.addEventListener('navigated', this._navigatedBound); + + // Don't use set options for the properties so we can control the ordering to avoid rendering multiple times. + this.layout = options.layout || WinJS.UI.Orientation.horizontal; + if (options.maxRows) { + this.maxRows = options.maxRows; + } + if (options.template) { + this.template = options.template; + } + if (options.data) { + this.data = options.data; + } + if (options.fixedSize) { + this.fixedSize = options.fixedSize; + } + + // Events only + WinJS.UI._setOptions(this, options, true); + + this._duringConstructor = false; + + if (options.currentIndex) { + this.currentIndex = options.currentIndex; + } + + this._updatePageUI(); + + this._writeProfilerMark("constructor,StopTM"); + }, { + /// + element: { + get: function () { + return this._element; + } + }, + + /// + /// Gets or sets a Template or custom rendering function that defines the HTML of each item within the NavBarContainer. + /// + /// + template: { + get: function () { + return this._template; + }, + set: function (value) { + this._template = value; + if (this._repeater) { + var hadFocus = this.element.contains(document.activeElement); + + if (!this._duringConstructor) { + this._closeSplitIfOpen(); + } + + // the repeater's template is wired up to this._render() so just resetting it will rebuild the tree. + this._repeater.template = this._repeater.template; + + if (!this._duringConstructor) { + this._measured = false; + this._sizes.itemMeasured = false; + this._reset(); + if (hadFocus) { + this._keyboardBehavior._focus(0); + } + } + } + } + }, + + _render: function NavBarContainer_render(item, wrapperElement) { + var navbarCommandEl = document.createElement('div'); + + var template = this._template; + if (template) { + if (template.render) { + template.render(item, navbarCommandEl); + } else if (template.winControl && template.winControl.render) { + template.winControl.render(item, navbarCommandEl); + } else { + navbarCommandEl.appendChild(template(item)); + } + } + + // Create the NavBarCommand after calling render so that the reparenting in navbarCommand works. + var navbarCommand = new WinJS.UI.NavBarCommand(navbarCommandEl, item); + return navbarCommandEl; + }, + + /// + /// Gets or sets the WinJS.Binding.List that provides the NavBarContainer with items to display. + /// + /// + data: { + get: function () { + return this._repeater && this._repeater.data; + }, + set: function (value) { + if (!value) { + value = new WinJS.Binding.List(); + } + + if (!this._duringConstructor) { + this._closeSplitIfOpen(); + } + + this._removeDataChangingEvents(); + this._removeDataChangedEvents(); + + var hadFocus = this.element.contains(document.activeElement); + + if (!this._repeater) { + this._surfaceEl.innerHTML = ""; + this._repeater = new WinJS.UI.Repeater(this._surfaceEl, { + template: this._render.bind(this) + }); + } + + this._addDataChangingEvents(value); + this._repeater.data = value; + this._addDataChangedEvents(value); + + if (!this._duringConstructor) { + this._measured = false; + this._sizes.itemMeasured = false; + this._reset(); + if (hadFocus) { + this._keyboardBehavior._focus(0); + } + } + } + }, + + /// + /// Gets or sets the number of rows allowed to be used before items are placed on additional pages. + /// + /// + maxRows: { + get: function () { + return this._maxRows; + }, + set: function (value) { + value = (+value === value) ? value : 1; + this._maxRows = Math.max(1, value); + + if (!this._duringConstructor) { + this._closeSplitIfOpen(); + + this._measured = false; + this._reset(); + } + } + }, + + /// + /// Gets or sets a value that specifies whether the NavBarContainer has a horizontal or vertical layout. The default is "horizontal". + /// + /// + layout: { + get: function () { + return this._layout; + }, + set: function (value) { + if (value === WinJS.UI.Orientation.vertical) { + this._layout = WinJS.UI.Orientation.vertical; + WinJS.Utilities.removeClass(this.element, WinJS.UI.NavBarContainer._ClassName.horizontal); + WinJS.Utilities.addClass(this.element, WinJS.UI.NavBarContainer._ClassName.vertical); + } else { + this._layout = WinJS.UI.Orientation.horizontal; + WinJS.Utilities.removeClass(this.element, WinJS.UI.NavBarContainer._ClassName.vertical); + WinJS.Utilities.addClass(this.element, WinJS.UI.NavBarContainer._ClassName.horizontal); + } + + this._viewportEl.style.msScrollSnapType = ""; + this._zooming = false; + + if (!this._duringConstructor) { + this._measured = false; + this._sizes.itemMeasured = false; + this._ensureVisible(this._keyboardBehavior.currentIndex, true); + this._updatePageUI(); + this._closeSplitIfOpen(); + } + } + }, + + /// + currentIndex: { + get: function () { + return this._keyboardBehavior.currentIndex; + }, + set: function (value) { + if (value === +value) { + var hadFocus = this.element.contains(document.activeElement); + + this._keyboardBehavior.currentIndex = value; + if (this._surfaceEl.children.length > 0) { + this._surfaceEl.children[this._keyboardBehavior.currentIndex].winControl._splitButtonActive = false; + } + + this._ensureVisible(this._keyboardBehavior.currentIndex, true); + + if (hadFocus) { + this._keyboardBehavior._focus(); + } + } + } + }, + + /// + /// Gets or sets a value that specifies whether child NavBarCommand objects should be a fixed width when there are multiple pages. A value of true indicates + /// that the NavBarCommand objects use a fixed width; a value of false indicates that they use a dynamic width. + /// + /// + fixedSize: { + get: function () { + return this._fixedSize; + }, + set: function (value) { + this._fixedSize = !!value; + + if (!this._duringConstructor) { + this._closeSplitIfOpen(); + + if (!this._measured) { + this._measure(); + } else if (this._surfaceEl.children.length > 0) { + this._updateGridStyles(); + } + } + } + }, + + /// + /// Raised when a NavBarCommand has been invoked. + /// + /// + oninvoked: createEvent(eventNames.invoked), + + /// + /// Raised when the split button on a NavBarCommand is toggled. + /// + /// + onsplittoggle: createEvent(eventNames.splittoggle), + + forceLayout: function NavBarContainer_forceLayout() { + /// + /// + /// Forces the NavBarContainer to update scroll positions and if there are internal pending measurements, it will also re-measure. + /// Use this function when making the NavBarContainer visible again after you set its style.display property to "none". + /// + /// + /// + if (this._measured) { + if (this.layout === WinJS.UI.Orientation.horizontal) { + this._scrollPosition = this._viewportEl.scrollLeft; + } else { + this._scrollPosition = this._viewportEl.scrollTop; + } + } + + this._duringForceLayout = true; + this._ensureVisible(this._keyboardBehavior.currentIndex, true); + this._updatePageUI(); + this._duringForceLayout = false; + }, + + _navigated: function NavBarContainer_navigated() { + this._closeSplitIfOpen(); + this._reset(); + }, + + _dataChanging: function NavBarContainer_dataChanging(ev) { + // Store the element that was active so that we can detect + // if the focus went away because of the data change. + this._elementHadFocus = document.activeElement; + + if (this._currentSplitNavItem && this._currentSplitNavItem.splitOpened) { + if (ev.type === "itemremoved") { + if (this._surfaceEl.children[ev.detail.index].winControl === this._currentSplitNavItem) { + this._closeSplitIfOpen(); + } + } else if (ev.type === "itemchanged") { + if (this._surfaceEl.children[ev.detail.index].winControl === this._currentSplitNavItem) { + this._closeSplitIfOpen(); + } + } else if (ev.type === "itemmoved") { + if (this._surfaceEl.children[ev.detail.oldIndex].winControl === this._currentSplitNavItem) { + this._closeSplitIfOpen(); + } + } else if (ev.type === "reload") { + this._closeSplitIfOpen(); + } + } + }, + + _dataChanged: function NavBarContainer_dataChanged(ev) { + this._measured = false; + + if (ev.type === "itemremoved") { + if (ev.detail.index < this._keyboardBehavior.currentIndex) { + this._keyboardBehavior.currentIndex--; + } else if (ev.detail.index === this._keyboardBehavior.currentIndex) { + // This clamps if the item being removed was the last item in the list + this._keyboardBehavior.currentIndex = this._keyboardBehavior.currentIndex; + if (document.activeElement === null && this._elementHadFocus) { + this._keyboardBehavior._focus(); + } + } + } else if (ev.type === "itemchanged") { + if (ev.detail.index === this._keyboardBehavior.currentIndex) { + if (document.activeElement === null && this._elementHadFocus) { + this._keyboardBehavior._focus(); + } + } + } else if (ev.type === "iteminserted") { + if (ev.detail.index <= this._keyboardBehavior.currentIndex) { + this._keyboardBehavior.currentIndex++; + } + } else if (ev.type === "itemmoved") { + if (ev.detail.oldIndex === this._keyboardBehavior.currentIndex) { + this._keyboardBehavior.currentIndex = ev.detail.newIndex; + if (document.activeElement === null && this._elementHadFocus) { + this._keyboardBehavior._focus(); + } + } + } else if (ev.type === "reload") { + this._keyboardBehavior.currentIndex = 0; + if (document.activeElement === null && this._elementHadFocus) { + this._keyboardBehavior._focus(); + } + } + + this._ensureVisible(this._keyboardBehavior.currentIndex, true); + this._updatePageUI(); + }, + + _reset: function NavBarContainer_reset() { + this._keyboardBehavior.currentIndex = 0; + if (this._surfaceEl.children.length > 0) { + this._surfaceEl.children[this._keyboardBehavior.currentIndex].winControl._splitButtonActive = false; + } + + if (this.element.contains(document.activeElement)) { + this._keyboardBehavior._focus(0); + } + + this._viewportEl.style.msScrollSnapType = ""; + this._zooming = false; + + this._ensureVisible(0, true); + this._updatePageUI(); + }, + + _removeDataChangedEvents: function NavBarContainer_removeDataChangedEvents() { + if (this._repeater) { + this._repeater.data.removeEventListener("itemchanged", this._dataChangedBound); + this._repeater.data.removeEventListener("iteminserted", this._dataChangedBound); + this._repeater.data.removeEventListener("itemmoved", this._dataChangedBound); + this._repeater.data.removeEventListener("itemremoved", this._dataChangedBound); + this._repeater.data.removeEventListener("reload", this._dataChangedBound); + } + }, + + _addDataChangedEvents: function NavBarContainer_addDataChangedEvents(bindingList) { + if (this._repeater) { + this._repeater.data.addEventListener("itemchanged", this._dataChangedBound); + this._repeater.data.addEventListener("iteminserted", this._dataChangedBound); + this._repeater.data.addEventListener("itemmoved", this._dataChangedBound); + this._repeater.data.addEventListener("itemremoved", this._dataChangedBound); + this._repeater.data.addEventListener("reload", this._dataChangedBound); + } + }, + + _removeDataChangingEvents: function NavBarContainer_removeDataChangingEvents() { + if (this._repeater) { + this._repeater.data.removeEventListener("itemchanged", this._dataChangingBound); + this._repeater.data.removeEventListener("iteminserted", this._dataChangingBound); + this._repeater.data.removeEventListener("itemmoved", this._dataChangingBound); + this._repeater.data.removeEventListener("itemremoved", this._dataChangingBound); + this._repeater.data.removeEventListener("reload", this._dataChangingBound); + } + }, + + _addDataChangingEvents: function NavBarContainer_addDataChangingEvents(bindingList) { + bindingList.addEventListener("itemchanged", this._dataChangingBound); + bindingList.addEventListener("iteminserted", this._dataChangingBound); + bindingList.addEventListener("itemmoved", this._dataChangingBound); + bindingList.addEventListener("itemremoved", this._dataChangingBound); + bindingList.addEventListener("reload", this._dataChangingBound); + }, + + _mouseleave: function NavBarContainer_mouseleave(ev) { + if (this._mouseInViewport) { + this._mouseInViewport = false; + this._updateArrows(); + } + }, + + _MSPointerDown: function NavBarContainer_MSPointerDown(ev) { + if (ev.pointerType === PT_TOUCH) { + if (this._mouseInViewport) { + this._mouseInViewport = false; + this._updateArrows(); + } + } + }, + + _MSPointerMove: function NavBarContainer_MSPointerMove(ev) { + if (ev.pointerType !== PT_TOUCH) { + if (!this._mouseInViewport) { + this._mouseInViewport = true; + this._updateArrows(); + } + } + }, + + _setupTree: function NavBarContainer_setupTree() { + this._animateNextPreviousButtons = WinJS.Promise.wrap(); + this._element.addEventListener('mouseleave', this._mouseleave.bind(this)); + this._element.addEventListener('pointerdown', this._MSPointerDown.bind(this)); + this._element.addEventListener('pointermove', this._MSPointerMove.bind(this)); + this._element.addEventListener("focus", this._focusHandler.bind(this), true); + + this._pageindicatorsEl = document.createElement('div'); + WinJS.Utilities.addClass(this._pageindicatorsEl, WinJS.UI.NavBarContainer._ClassName.pageindicators); + this._element.appendChild(this._pageindicatorsEl); + + this._ariaStartMarker = document.createElement("div"); + this._element.appendChild(this._ariaStartMarker); + + this._viewportEl = document.createElement('div'); + WinJS.Utilities.addClass(this._viewportEl, WinJS.UI.NavBarContainer._ClassName.viewport); + this._element.appendChild(this._viewportEl); + this._viewportEl.setAttribute("role", "group"); + this._viewportEl.setAttribute("aria-label", strings.navBarContainerViewportAriaLabel); + + this._boundResizeHandler = this._resizeHandler.bind(this); + window.addEventListener("resize", this._boundResizeHandler); + this._viewportEl.addEventListener("mselementresize", this._resizeHandler.bind(this)); + this._viewportEl.addEventListener("scroll", this._scrollHandler.bind(this)); + this._viewportEl.addEventListener("MSManipulationStateChanged", this._MSManipulationStateChangedHandler.bind(this)); + + this._ariaEndMarker = document.createElement("div"); + this._element.appendChild(this._ariaEndMarker); + + this._surfaceEl = document.createElement('div'); + WinJS.Utilities.addClass(this._surfaceEl, WinJS.UI.NavBarContainer._ClassName.surface); + this._viewportEl.appendChild(this._surfaceEl); + + this._surfaceEl.addEventListener("_invoked", this._navbarCommandInvokedHandler.bind(this)); + this._surfaceEl.addEventListener("_splittoggle", this._navbarCommandSplitToggleHandler.bind(this)); + this._surfaceEl.addEventListener("focus", this._itemsFocusHandler.bind(this), true); + this._surfaceEl.addEventListener("keydown", this._keyDownHandler.bind(this)); + + // Reparent NavBarCommands which were in declarative markup + var tempEl = this.element.firstElementChild; + while (tempEl !== this._pageindicatorsEl) { + this._surfaceEl.appendChild(tempEl); + WinJS.UI.process(tempEl); + tempEl = this.element.firstElementChild; + } + + this._leftArrowEl = document.createElement('div'); + WinJS.Utilities.addClass(this._leftArrowEl, WinJS.UI.NavBarContainer._ClassName.navleftarrow); + WinJS.Utilities.addClass(this._leftArrowEl, WinJS.UI.NavBarContainer._ClassName.navarrow); + this._element.appendChild(this._leftArrowEl); + this._leftArrowEl.addEventListener('click', this._goLeft.bind(this)); + this._leftArrowEl.style.opacity = 0; + this._leftArrowEl.style.visibility = 'hidden'; + this._leftArrowFadeOut = WinJS.Promise.wrap(); + + this._rightArrowEl = document.createElement('div'); + WinJS.Utilities.addClass(this._rightArrowEl, WinJS.UI.NavBarContainer._ClassName.navrightarrow); + WinJS.Utilities.addClass(this._rightArrowEl, WinJS.UI.NavBarContainer._ClassName.navarrow); + this._element.appendChild(this._rightArrowEl); + this._rightArrowEl.addEventListener('click', this._goRight.bind(this)); + this._rightArrowEl.style.opacity = 0; + this._rightArrowEl.style.visibility = 'hidden'; + this._rightArrowFadeOut = WinJS.Promise.wrap(); + + this._keyboardBehavior = new WinJS.UI._KeyboardBehavior(this._surfaceEl); + this._winKeyboard = new WinJS.UI._WinKeyboard(this._surfaceEl); + }, + + _goRight: function NavBarContainer_goRight() { + if (this._sizes.rtl) { + this._goPrev(); + } else { + this._goNext(); + } + }, + + _goLeft: function NavBarContainer_goLeft() { + if (this._sizes.rtl) { + this._goNext(); + } else { + this._goPrev(); + } + }, + + _goNext: function NavBarContainer_goNext() { + this._measure(); + var itemsPerPage = this._sizes.rowsPerPage * this._sizes.columnsPerPage; + var targetPage = Math.min(Math.floor(this._keyboardBehavior.currentIndex / itemsPerPage) + 1, this._sizes.pages - 1); + this._keyboardBehavior.currentIndex = Math.min(itemsPerPage * targetPage, this._surfaceEl.children.length); + this._keyboardBehavior._focus(); + }, + + _goPrev: function NavBarContainer_goPrev() { + this._measure(); + var itemsPerPage = this._sizes.rowsPerPage * this._sizes.columnsPerPage; + var targetPage = Math.max(0, Math.floor(this._keyboardBehavior.currentIndex / itemsPerPage) - 1); + this._keyboardBehavior.currentIndex = Math.max(itemsPerPage * targetPage, 0); + this._keyboardBehavior._focus(); + }, + + _currentPage: { + get: function () { + if (this.layout === WinJS.UI.Orientation.horizontal) { + this._measure(); + if (this._sizes.viewportOffsetWidth > 0) { + return Math.min(this._sizes.pages - 1, Math.round(this._scrollPosition / this._sizes.viewportOffsetWidth)); + } + } + return 0; + } + }, + + _resizeHandler: function NavBarContainer_resizeHandler() { + if (this._disposed) { + return; + } + if (this._measured) { + this._measured = false; + + if (!this._pendingResize) { + this._pendingResize = true; + + this._resizeImplBound = this._resizeImplBound || this._resizeImpl.bind(this); + + if (!this._appBarEl || !this._appBarEl.contains(this.element)) { + if (this._appBarEl) { + this._appBarEl.removeEventListener('beforeshow', this._resizeImplBound); + } + + var appBarEl = this.element.parentNode; + while (appBarEl && !WinJS.Utilities.hasClass(appBarEl, 'win-appbar')) { + appBarEl = appBarEl.parentNode; + } + + this._appBarEl = appBarEl; + } + + if (this._appBarEl && this._appBarEl.winControl && this._appBarEl.winControl.hidden) { + // Do resize lazily. + WinJS.Utilities.Scheduler.schedule(this._resizeImplBound, WinJS.Utilities.Scheduler.Priority.idle, null, "WinJS.UI.NavBarContainer._resizeImpl"); + this._appBarEl.addEventListener('beforeshow', this._resizeImplBound); + } else { + // Do resize now + this._resizeImpl(); + } + } + } + }, + + _resizeImpl: function NavBarContainer_resizeImpl() { + if (!this._disposed && this._pendingResize) { + this._pendingResize = false; + if (this._appBarEl) { + this._appBarEl.removeEventListener('beforeshow', this._resizeImplBound); + } + + this._keyboardBehavior.currentIndex = 0; + if (this._surfaceEl.children.length > 0) { + this._surfaceEl.children[0].winControl._splitButtonActive = false; + } + if (this.element.contains(document.activeElement)) { + this._keyboardBehavior._focus(this._keyboardBehavior.currentIndex); + } + this._closeSplitIfOpen(); + this._ensureVisible(this._keyboardBehavior.currentIndex, true); + this._updatePageUI(); + } + }, + + _keyDownHandler: function NavBarContainer_keyDownHandler(ev) { + var key = ev.key + if (!ev.altKey && (key === "PageUp" || key === "PageDown")) { + var srcElement = ev.srcElement; + if (srcElement.msMatchesSelector(".win-interactive, .win-interactive *")) { + return; + } + + var index = this._keyboardBehavior.currentIndex; + this._measure(); + + var sizes = this._sizes; + var page = Math.floor(index / (sizes.columnsPerPage * sizes.rowsPerPage)); + + var scrollPositionTarget = null; + if (key === "PageUp") { + if (this.layout === WinJS.UI.Orientation.horizontal) { + var indexOfFirstItemOnPage = page * sizes.columnsPerPage * sizes.rowsPerPage; + if (index === indexOfFirstItemOnPage && this._surfaceEl.children[index].winControl._buttonEl === document.activeElement) { + // First item on page so go back 1 page. + index = index - sizes.columnsPerPage * sizes.rowsPerPage; + } else { + // Not first item on page so go to the first item on page. + index = indexOfFirstItemOnPage; + } + } else { + var currentItem = this._surfaceEl.children[index]; + var top = currentItem.offsetTop; + var bottom = top + currentItem.offsetHeight; + var scrollPosition = this._zooming ? this._zoomPosition : this._scrollPosition; + + if (top >= scrollPosition && bottom < scrollPosition + sizes.viewportOffsetHeight) { + // current item is fully on screen. + while (index > 0 && + this._surfaceEl.children[index - 1].offsetTop > scrollPosition) { + index--; + } + } + + if (this._keyboardBehavior.currentIndex === index) { + var scrollPositionForOnePageAboveItem = bottom - sizes.viewportOffsetHeight; + index = Math.max(0, index - 1); + while (index > 0 && + this._surfaceEl.children[index - 1].offsetTop > scrollPositionForOnePageAboveItem) { + index--; + } + if (index > 0) { + scrollPositionTarget = this._surfaceEl.children[index].offsetTop - this._sizes.itemMarginTop; + } else { + scrollPositionTarget = 0; + } + } + } + + index = Math.max(index, 0); + this._keyboardBehavior.currentIndex = index; + + var element = this._surfaceEl.children[index].winControl._buttonEl; + this._surfaceEl.children[index].winControl._splitButtonActive = false; + + if (scrollPositionTarget !== null) { + this._scrollTo(scrollPositionTarget); + } + + try { + element.setActive(); + } catch (e) { + } + } else { + if (this.layout === WinJS.UI.Orientation.horizontal) { + var indexOfLastItemOnPage = (page + 1) * sizes.columnsPerPage * sizes.rowsPerPage - 1; + + if (index === indexOfLastItemOnPage) { + // Last item on page so go forward 1 page. + index = index + sizes.columnsPerPage * sizes.rowsPerPage; + } else { + // Not Last item on page so go to last item on page. + index = indexOfLastItemOnPage; + } + } else { + var currentItem = this._surfaceEl.children[this._keyboardBehavior.currentIndex]; + var top = currentItem.offsetTop; + var bottom = top + currentItem.offsetHeight; + var scrollPosition = this._zooming ? this._zoomPosition : this._scrollPosition; + + if (top >= scrollPosition && bottom < scrollPosition + sizes.viewportOffsetHeight) { + // current item is fully on screen. + while (index < this._surfaceEl.children.length - 1 && + this._surfaceEl.children[index + 1].offsetTop + this._surfaceEl.children[index + 1].offsetHeight < scrollPosition + sizes.viewportOffsetHeight) { + index++; + } + } + + if (index === this._keyboardBehavior.currentIndex) { + var scrollPositionForOnePageBelowItem = top + sizes.viewportOffsetHeight; + index = Math.min(this._surfaceEl.children.length - 1, index + 1); + while (index < this._surfaceEl.children.length - 1 && + this._surfaceEl.children[index + 1].offsetTop + this._surfaceEl.children[index + 1].offsetHeight < scrollPositionForOnePageBelowItem) { + index++; + } + + if (index < this._surfaceEl.children.length - 1) { + scrollPositionTarget = this._surfaceEl.children[index + 1].offsetTop - this._sizes.viewportOffsetHeight; + } else { + scrollPositionTarget = this._scrollLength - this._sizes.viewportOffsetHeight; + } + } + } + + index = Math.min(index, this._surfaceEl.children.length - 1); + this._keyboardBehavior.currentIndex = index; + + var element = this._surfaceEl.children[index].winControl._buttonEl; + this._surfaceEl.children[index].winControl._splitButtonActive = false; + + if (scrollPositionTarget !== null) { + this._scrollTo(scrollPositionTarget); + } + + try { + element.setActive(); + } catch (e) { + } + } + } + }, + + _focusHandler: function NavBarContainer_focusHandler(ev) { + var srcElement = ev.srcElement; + if (!this._surfaceEl.contains(srcElement)) { + // Forward focus from NavBarContainer, viewport or surface to the currentIndex. + this._skipEnsureVisible = true; + this._keyboardBehavior._focus(this._keyboardBehavior.currentIndex); + } + }, + + _itemsFocusHandler: function NavBarContainer_itemsFocusHandler(ev) { + // Find the item which is being focused and scroll it to view. + var srcElement = ev.srcElement; + if (srcElement === this._surfaceEl) { + return; + } + + while (srcElement.parentNode !== this._surfaceEl) { + srcElement = srcElement.parentNode; + } + + var index = -1; + while (srcElement) { + index++; + srcElement = srcElement.previousSibling; + } + + if (this._skipEnsureVisible) { + this._skipEnsureVisible = false; + } else { + this._ensureVisible(index); + } + }, + + _ensureVisible: function NavBarContainer_ensureVisible(index, withoutAnimation) { + this._measure(); + + if (this.layout === WinJS.UI.Orientation.horizontal) { + var page = Math.floor(index / (this._sizes.rowsPerPage * this._sizes.columnsPerPage)); + this._scrollTo(page * this._sizes.viewportOffsetWidth, withoutAnimation); + } else { + var element = this._surfaceEl.children[index]; + var maxScrollPosition; + if (index > 0) { + maxScrollPosition = element.offsetTop - this._sizes.itemMarginTop; + } else { + maxScrollPosition = 0; + } + var minScrollPosition; + if (index < this._surfaceEl.children.length - 1) { + minScrollPosition = this._surfaceEl.children[index + 1].offsetTop - this._sizes.viewportOffsetHeight; + } else { + minScrollPosition = this._scrollLength - this._sizes.viewportOffsetHeight; + } + + var newScrollPosition = this._zooming ? this._zoomPosition : this._scrollPosition; + newScrollPosition = Math.max(newScrollPosition, minScrollPosition); + newScrollPosition = Math.min(newScrollPosition, maxScrollPosition); + this._scrollTo(newScrollPosition, withoutAnimation); + } + }, + + _scrollTo: function NavBarContainer_scrollTo(targetScrollPosition, withoutAnimation) { + this._measure(); + if (this.layout === WinJS.UI.Orientation.horizontal) { + targetScrollPosition = Math.max(0, Math.min(this._scrollLength - this._sizes.viewportOffsetWidth, targetScrollPosition)); + } else { + targetScrollPosition = Math.max(0, Math.min(this._scrollLength - this._sizes.viewportOffsetHeight, targetScrollPosition)); + } + + if (withoutAnimation) { + if (Math.abs(this._scrollPosition - targetScrollPosition) > 1) { + this._zooming = false; + + this._scrollPosition = targetScrollPosition; + this._updatePageUI(); + if (!this._duringForceLayout) { + this._closeSplitIfOpen(); + } + + if (this.layout === WinJS.UI.Orientation.horizontal) { + this._viewportEl.scrollLeft = targetScrollPosition; + } else { + this._viewportEl.scrollTop = targetScrollPosition; + } + } + } else { + if ((!this._zooming && Math.abs(this._scrollPosition - targetScrollPosition) > 1) || (this._zooming && Math.abs(this._zoomPosition - targetScrollPosition) > 1)) { + this._zoomPosition = targetScrollPosition; + + this._zooming = true; + + if (this.layout === WinJS.UI.Orientation.horizontal) { + this._viewportEl.style.msScrollSnapType = "none"; + this._viewportEl.msZoomTo({ contentX: targetScrollPosition, contentY: 0, viewportX: 0, viewportY: 0 }); + } else { + this._viewportEl.msZoomTo({ contentX: 0, contentY: targetScrollPosition, viewportX: 0, viewportY: 0 }); + } + + this._closeSplitIfOpen(); + } + } + }, + + _MSManipulationStateChangedHandler: function NavBarContainer_MSManipulationStateChangedHandler(e) { + this._currentManipulationState = e.currentState; + + if (e.currentState === e.MS_MANIPULATION_STATE_ACTIVE) { + this._viewportEl.style.msScrollSnapType = ""; + this._zooming = false; + } + + clearTimeout(this._manipulationStateTimeoutId); + // The extra stop event is firing when an msZoomTo is called during another msZoomTo and + // also the first msZoomTo after a resize. + if (e.currentState === e.MS_MANIPULATION_STATE_STOPPED) { + this._manipulationStateTimeoutId = setTimeout(function () { + this._viewportEl.style.msScrollSnapType = ""; + this._zooming = false; + this._updateCurrentIndexIfPageChanged(); + }.bind(this), 100); + } + }, + + _scrollHandler: function NavBarContainer_scrollHandler() { + this._measured = false; + if (!this._checkingScroll) { + var that = this; + this._checkingScroll = requestAnimationFrame(function () { + that._checkingScroll = null; + + var newScrollPosition; + if (that.layout === WinJS.UI.Orientation.horizontal) { + newScrollPosition = that._viewportEl.scrollLeft; + } else { + newScrollPosition = that._viewportEl.scrollTop; + } + if (newScrollPosition !== that._scrollPosition) { + that._scrollPosition = newScrollPosition; + that._closeSplitIfOpen(); + } + that._updatePageUI(); + + if (!that._zooming && this._currentManipulationState === MS_MANIPULATION_STATE_STOPPED) { + that._updateCurrentIndexIfPageChanged(); + } + }); + } + }, + + _updateCurrentIndexIfPageChanged: function NavBarContainer_updateCurrentIndexIfPageChanged() { + // If you change pages via pagination arrows, mouse wheel, or panning we need to update the current + // item to be the first item on the new page. + if (this.layout === WinJS.UI.Orientation.horizontal) { + this._measure(); + var currentPage = this._currentPage; + var firstIndexOnPage = currentPage * this._sizes.rowsPerPage * this._sizes.columnsPerPage; + var lastIndexOnPage = (currentPage + 1) * this._sizes.rowsPerPage * this._sizes.columnsPerPage - 1; + + if (this._keyboardBehavior.currentIndex < firstIndexOnPage || this._keyboardBehavior.currentIndex > lastIndexOnPage) { + // Page change occurred. + this._keyboardBehavior.currentIndex = firstIndexOnPage; + if (this._surfaceEl.children.length > 0) { + this._surfaceEl.children[this._keyboardBehavior.currentIndex].winControl._splitButtonActive = false; + } + + if (this.element.contains(document.activeElement)) { + this._keyboardBehavior._focus(this._keyboardBehavior.currentIndex); + } + } + } + }, + + _measure: function NavBarContainer_measure() { + if (!this._measured) { + this._resizeImpl(); + this._writeProfilerMark("measure,StartTM"); + + var sizes = this._sizes; + + sizes.rtl = getComputedStyle(this._element).direction === "rtl"; + + var itemCount = this._surfaceEl.children.length; + if (itemCount > 0) { + if (!this._sizes.itemMeasured) { + this._writeProfilerMark("measureItem,StartTM"); + + var elementToMeasure = this._surfaceEl.firstElementChild; + // Clear inline margins set by NavBarContainer before measuring. + elementToMeasure.style.margin = ""; + elementToMeasure.style.width = ""; + var elementComputedStyle = getComputedStyle(elementToMeasure); + sizes.itemOffsetWidth = parseFloat(getComputedStyle(elementToMeasure).width); + if (elementToMeasure.offsetWidth === 0) { + sizes.itemOffsetWidth = 0; + } + sizes.itemMarginLeft = parseFloat(elementComputedStyle.marginLeft); + sizes.itemMarginRight = parseFloat(elementComputedStyle.marginRight); + sizes.itemWidth = sizes.itemOffsetWidth + sizes.itemMarginLeft + sizes.itemMarginRight; + sizes.itemOffsetHeight = parseFloat(getComputedStyle(elementToMeasure).height); + if (elementToMeasure.offsetHeight === 0) { + sizes.itemOffsetHeight = 0; + } + sizes.itemMarginTop = parseFloat(elementComputedStyle.marginTop); + sizes.itemMarginBottom = parseFloat(elementComputedStyle.marginBottom); + sizes.itemHeight = sizes.itemOffsetHeight + sizes.itemMarginTop + sizes.itemMarginBottom; + if (sizes.itemOffsetWidth > 0 && sizes.itemOffsetHeight > 0) { + sizes.itemMeasured = true; + } + this._writeProfilerMark("measureItem,StopTM"); + } + + sizes.viewportOffsetWidth = parseFloat(getComputedStyle(this._viewportEl).width); + if (this._viewportEl.offsetWidth === 0) { + sizes.viewportOffsetWidth = 0; + } + sizes.viewportOffsetHeight = parseFloat(getComputedStyle(this._viewportEl).height); + if (this._viewportEl.offsetHeight === 0) { + sizes.viewportOffsetHeight = 0; + } + + if (sizes.viewportOffsetWidth === 0 || sizes.itemOffsetHeight === 0) { + this._measured = false; + } else { + this._measured = true; + } + + if (this.layout === WinJS.UI.Orientation.horizontal) { + this._scrollPosition = this._viewportEl.scrollLeft; + + sizes.leadingEdge = this._leftArrowEl.offsetWidth + parseInt(getComputedStyle(this._leftArrowEl).marginLeft) + parseInt(getComputedStyle(this._leftArrowEl).marginRight); + var usableSpace = sizes.viewportOffsetWidth - sizes.leadingEdge * 2; + sizes.maxColumns = sizes.itemWidth ? Math.max(1, Math.floor(usableSpace / sizes.itemWidth)) : 1; + sizes.rowsPerPage = Math.min(this.maxRows, Math.ceil(itemCount / sizes.maxColumns)); + sizes.columnsPerPage = Math.min(sizes.maxColumns, itemCount); + sizes.pages = Math.ceil(itemCount / (sizes.columnsPerPage * sizes.rowsPerPage)); + sizes.trailingEdge = sizes.leadingEdge; + sizes.extraSpace = usableSpace - (sizes.columnsPerPage * sizes.itemWidth); + + this._scrollLength = sizes.viewportOffsetWidth * sizes.pages; + + this._keyboardBehavior.fixedSize = sizes.rowsPerPage; + this._keyboardBehavior.fixedDirection = WinJS.UI._KeyboardBehavior.FixedDirection.height; + + this._surfaceEl.style.height = (sizes.itemHeight * sizes.rowsPerPage) + "px"; + this._surfaceEl.style.width = this._scrollLength + "px"; + } else { + this._scrollPosition = this._viewportEl.scrollTop; + + sizes.leadingEdge = 0; + sizes.rowsPerPage = itemCount; + sizes.columnsPerPage = 1; + sizes.pages = 1; + sizes.trailingEdge = 0; + + // Reminder there is margin collapsing so just use scrollHeight instead of itemHeight * itemCount + this._scrollLength = this._viewportEl.scrollHeight; + + this._keyboardBehavior.fixedSize = sizes.columnsPerPage; + this._keyboardBehavior.fixedDirection = WinJS.UI._KeyboardBehavior.FixedDirection.width; + + this._surfaceEl.style.height = ""; + this._surfaceEl.style.width = ""; + } + + this._updateGridStyles(); + } else { + sizes.pages = 1; + this._hasPreviousContent = false; + this._hasNextContent = false; + this._surfaceEl.style.height = ""; + this._surfaceEl.style.width = ""; + } + + this._writeProfilerMark("measure,StopTM"); + } + }, + + _updateGridStyles: function NavBarContainer_updateGridStyles() { + var sizes = this._sizes; + var itemCount = this._surfaceEl.children.length; + + for (var index = 0; index < itemCount; index++) { + var itemEl = this._surfaceEl.children[index]; + + var marginRight; + var marginLeft; + var cssRow; + var cssColumn; + var width = ""; + + if (this.layout === WinJS.UI.Orientation.horizontal) { + var row = Math.floor(index % sizes.rowsPerPage); + var column = Math.floor(index / sizes.rowsPerPage); + var isFirstColumnOnPage = column % sizes.columnsPerPage === 0; + var isLastColumnOnPage = column % sizes.columnsPerPage === sizes.columnsPerPage - 1; + + var extraTrailingMargin = sizes.trailingEdge; + if (this.fixedSize) { + extraTrailingMargin += sizes.extraSpace; + } else { + var spaceToDistribute = sizes.extraSpace - (sizes.maxColumns - sizes.columnsPerPage) * sizes.itemWidth; + width = (sizes.itemOffsetWidth + (spaceToDistribute / sizes.maxColumns)) + "px"; + } + + cssRow = row + 1; + cssColumn = column + 1; + var extraMarginRight; + var extraMarginLeft; + + if (sizes.rtl) { + extraMarginRight = (isFirstColumnOnPage ? sizes.leadingEdge : 0); + extraMarginLeft = (isLastColumnOnPage ? extraTrailingMargin : 0); + } else { + extraMarginRight = (isLastColumnOnPage ? extraTrailingMargin : 0); + extraMarginLeft = (isFirstColumnOnPage ? sizes.leadingEdge : 0); + } + + marginRight = extraMarginRight + sizes.itemMarginRight + "px"; + marginLeft = extraMarginLeft + sizes.itemMarginLeft + "px"; + } else { + cssRow = ""; + cssColumn = ""; + marginRight = ""; + marginLeft = ""; + } + + if (itemEl.style.msGridRow !== cssRow) { + itemEl.style.msGridRow = cssRow; + } + if (itemEl.style.msGridColumn !== cssColumn) { + itemEl.style.msGridColumn = cssColumn; + } + if (itemEl.style.marginRight !== marginRight) { + itemEl.style.marginRight = marginRight; + } + if (itemEl.style.marginLeft !== marginLeft) { + itemEl.style.marginLeft = marginLeft; + } + if (itemEl.style.width !== width) { + itemEl.style.width = width; + } + } + }, + + _updatePageUI: function NavBarContainer_updatePageUI() { + this._measure(); + var currentPage = this._currentPage; + + this._hasPreviousContent = (currentPage !== 0); + this._hasNextContent = (currentPage < this._sizes.pages - 1); + this._updateArrows(); + + // Always output the pagination indicators so they reserves up space. + if (this._indicatorCount !== this._sizes.pages) { + this._indicatorCount = this._sizes.pages; + this._pageindicatorsEl.innerHTML = new Array(this._sizes.pages + 1).join(''); + } + + for (var i = 0; i < this._pageindicatorsEl.children.length; i++) { + if (i === currentPage) { + WinJS.Utilities.addClass(this._pageindicatorsEl.children[i], WinJS.UI.NavBarContainer._ClassName.currentindicator); + } else { + WinJS.Utilities.removeClass(this._pageindicatorsEl.children[i], WinJS.UI.NavBarContainer._ClassName.currentindicator); + } + } + + if (this._sizes.pages > 1) { + this._viewportEl.style.overflowX = ""; + this._pageindicatorsEl.style.visibility = ""; + } else { + this._viewportEl.style.overflowX = "hidden"; + this._pageindicatorsEl.style.visibility = "hidden"; + } + + if (this._sizes.pages <= 1 || this._layout !== WinJS.UI.Orientation.horizontal) { + this._ariaStartMarker.removeAttribute("aria-flowto"); + this._ariaEndMarker.removeAttribute("x-ms-aria-flowfrom"); + } else { + var firstIndexOnCurrentPage = currentPage * this._sizes.rowsPerPage * this._sizes.columnsPerPage; + var firstItem = this._surfaceEl.children[firstIndexOnCurrentPage].winControl._buttonEl; + WinJS.UI._ensureId(firstItem); + this._ariaStartMarker.setAttribute("aria-flowto", firstItem.id); + + var lastIndexOnCurrentPage = Math.min(this._surfaceEl.children.length - 1, (currentPage + 1) * this._sizes.rowsPerPage * this._sizes.columnsPerPage - 1); + var lastItem = this._surfaceEl.children[lastIndexOnCurrentPage].winControl._buttonEl; + WinJS.UI._ensureId(lastItem); + this._ariaEndMarker.setAttribute("x-ms-aria-flowfrom", lastItem.id); + } + }, + + _closeSplitIfOpen: function NavBarContainer_closeSplitIfOpen() { + if (this._currentSplitNavItem) { + if (this._currentSplitNavItem.splitOpened) { + this._currentSplitNavItem._toggleSplit(); + } + this._currentSplitNavItem = null; + } + }, + + _updateArrows: function NavBarContainer_updateArrows() { + var hasLeftContent = this._sizes.rtl ? this._hasNextContent : this._hasPreviousContent; + var hasRightContent = this._sizes.rtl ? this._hasPreviousContent : this._hasNextContent; + + var that = this; + // Previous and next are the arrows, not states. On mouse hover the arrows fade in immediately. If you + // mouse out the arrows fade out after a delay. When you reach the last/first page, the corresponding + // arrow fades out immediately as well. + if (this._mouseInViewport && hasLeftContent) { + this._leftArrowWaitingToFadeOut && this._leftArrowWaitingToFadeOut.cancel(); + this._leftArrowWaitingToFadeOut = null; + this._leftArrowFadeOut && this._leftArrowFadeOut.cancel(); + this._leftArrowFadeOut = null; + this._leftArrowEl.style.visibility = ''; + this._leftArrowFadeIn = this._leftArrowFadeIn || WinJS.UI.Animation.fadeIn(this._leftArrowEl); + } else { + if (hasLeftContent) { + // If we need a delayed fade out and we are already running a delayed fade out just use that one, don't extend it. + // Otherwise create a delayed fade out. + this._leftArrowWaitingToFadeOut = this._leftArrowWaitingToFadeOut || WinJS.Promise.timeout(buttonFadeDelay); + } else { + // If we need a immediate fade out and already have a delayed fade out cancel that one and create an immediate one. + this._leftArrowWaitingToFadeOut && this._leftArrowWaitingToFadeOut.cancel(); + this._leftArrowWaitingToFadeOut = WinJS.Promise.wrap(); + } + this._leftArrowWaitingToFadeOut.then(function () { + // After the delay cancel any fade in if running. If we already were fading out continue it otherwise start the fade out. + this._leftArrowFadeIn && this._leftArrowFadeIn.cancel(); + this._leftArrowFadeIn = null; + this._leftArrowFadeOut = this._leftArrowFadeOut || WinJS.UI.Animation.fadeOut(this._leftArrowEl).then(function () { + that._leftArrowEl.style.visibility = 'hidden'; + }); + }.bind(this)); + } + + // Same pattern for Next arrow. + if (this._mouseInViewport && hasRightContent) { + this._rightArrowWaitingToFadeOut && this._rightArrowWaitingToFadeOut.cancel(); + this._rightArrowWaitingToFadeOut = null; + this._rightArrowFadeOut && this._rightArrowFadeOut.cancel(); + this._rightArrowFadeOut = null; + this._rightArrowEl.style.visibility = ''; + this._rightArrowFadeIn = this._rightArrowFadeIn || WinJS.UI.Animation.fadeIn(this._rightArrowEl); + } else { + if (hasRightContent) { + this._rightArrowWaitingToFadeOut = this._rightArrowWaitingToFadeOut || WinJS.Promise.timeout(buttonFadeDelay); + } else { + this._rightArrowWaitingToFadeOut && this._rightArrowWaitingToFadeOut.cancel(); + this._rightArrowWaitingToFadeOut = WinJS.Promise.wrap(); + } + this._rightArrowWaitingToFadeOut.then(function () { + this._rightArrowFadeIn && this._rightArrowFadeIn.cancel(); + this._rightArrowFadeIn = null; + this._rightArrowFadeOut = this._rightArrowFadeOut || WinJS.UI.Animation.fadeOut(this._rightArrowEl).then(function () { + that._rightArrowEl.style.visibility = 'hidden'; + }); + }.bind(this)); + } + }, + + _navbarCommandInvokedHandler: function NavBarContainer_navbarCommandInvokedHandler(ev) { + var srcElement = ev.srcElement; + var index = -1; + while (srcElement) { + index++; + srcElement = srcElement.previousSibling; + } + + this._fireEvent(WinJS.UI.NavBarContainer._EventName.invoked, { + index: index, + navbarCommand: ev.srcElement.winControl, + data: this._repeater ? this._repeater.data.getAt(index) : null + }); + }, + + _navbarCommandSplitToggleHandler: function NavBarContainer_navbarCommandSplitToggleHandler(ev) { + var srcElement = ev.srcElement; + var index = -1; + while (srcElement) { + index++; + srcElement = srcElement.previousSibling; + } + + var navbarCommand = ev.srcElement.winControl; + + this._closeSplitIfOpen(); + + if (navbarCommand.splitOpened) { + this._currentSplitNavItem = navbarCommand; + } + + this._fireEvent(WinJS.UI.NavBarContainer._EventName.splitToggle, { + opened: navbarCommand.splitOpened, + index: index, + navbarCommand: navbarCommand, + data: this._repeater ? this._repeater.data.getAt(index) : null + }); + }, + + _fireEvent: function NavBarContainer_fireEvent(type, detail) { + var event = document.createEvent("CustomEvent"); + event.initCustomEvent(type, true, false, detail); + this.element.dispatchEvent(event); + }, + + _writeProfilerMark: function NavBarContainer_writeProfilerMark(text) { + var message = "WinJS.UI.NavBarContainer:" + this._id + ":" + text; + msWriteProfilerMark(message); + WinJS.log && WinJS.log(message, null, "navbarcontainerprofiler"); + }, + + dispose: function NavBarContainer_dispose() { + /// + /// + /// Disposes this control. + /// + /// + /// + if (this._disposed) { + return; + } + this._disposed = true; + + if (this._appBarEl) { + this._appBarEl.removeEventListener('beforeshow', this._resizeImplBound); + } + + WinJS.Navigation.removeEventListener('navigated', this._navigatedBound); + + this._leftArrowWaitingToFadeOut && this._leftArrowWaitingToFadeOut.cancel(); + this._leftArrowFadeOut && this._leftArrowFadeOut.cancel(); + this._leftArrowFadeIn && this._leftArrowFadeIn.cancel(); + this._rightArrowWaitingToFadeOut && this._rightArrowWaitingToFadeOut.cancel(); + this._rightArrowFadeOut && this._rightArrowFadeOut.cancel(); + this._rightArrowFadeIn && this._rightArrowFadeIn.cancel(); + + window.removeEventListener("resize", this._boundResizeHandler); + + this._removeDataChangingEvents(); + this._removeDataChangedEvents(); + } + }, { + // Names of classes used by the NavBarContainer. + _ClassName: { + navbarcontainer: "win-navbarcontainer", + pageindicators: "win-navbarcontainer-pageindicator-box", + indicator: "win-navbarcontainer-pageindicator", + currentindicator: "win-navbarcontainer-pageindicator-current", + vertical: "win-navbarcontainer-vertical", + horizontal: "win-navbarcontainer-horizontal", + viewport: "win-navbarcontainer-viewport", + surface: "win-navbarcontainer-surface", + navarrow: "win-navbarcontainer-navarrow", + navleftarrow: "win-navbarcontainer-navleft", + navrightarrow: "win-navbarcontainer-navright" + }, + _EventName: { + invoked: eventNames.invoked, + splitToggle: eventNames.splittoggle + } + }); + WinJS.Class.mix(NavBarContainer, WinJS.UI.DOMEventMixin); + return NavBarContainer; + }) + }); + +})(this, WinJS); + +(function NavBarCommandInit(global, WinJS, undefined) { + "use strict"; + + WinJS.Namespace.define("WinJS.UI", { + _WinPressed: WinJS.Namespace._lazy(function () { + return WinJS.Class.define(function _WinPressed_ctor(element) { + // WinPressed is the combination of :hover:active + // :hover is delayed by trident for touch by 300ms so if you want :hover:active to work quickly you need to + // use this behavior. + // :active does not bubble to its parent like :hover does so this is also useful for that scenario. + this._element = element; + this._element.addEventListener("pointerdown", this._MSPointerDownButtonHandler.bind(this)); + }, { + _MSPointerDownButtonHandler: function _WinPressed_MSPointerDownButtonHandler(ev) { + if (!this._pointerUpBound) { + this._pointerUpBound = this._MSPointerUpHandler.bind(this); + this._pointerCancelBound = this._MSPointerCancelHandler.bind(this); + this._pointerOverBound = this._MSPointerOverHandler.bind(this); + this._pointerOutBound = this._MSPointerOutHandler.bind(this); + } + + if (ev.isPrimary) { + if (this._pointerId) { + this._resetPointer(); + } + + if (!ev.srcElement.msMatchesSelector(".win-interactive, .win-interactive *")) { + this._pointerId = ev.pointerId; + + window.addEventListener("pointerup", this._pointerUpBound, true); + window.addEventListener("pointercancel", this._pointerCancelBound), true; + this._element.addEventListener("pointerover", this._pointerOverBound, true); + this._element.addEventListener("pointerout", this._pointerOutBound, true); + + WinJS.Utilities.addClass(this._element, WinJS.UI._WinPressed.winPressed); + } + } + }, + + _MSPointerOverHandler: function _WinPressed_MSPointerOverHandler(ev) { + if (this._pointerId === ev.pointerId) { + WinJS.Utilities.addClass(this._element, WinJS.UI._WinPressed.winPressed); + } + }, + + _MSPointerOutHandler: function _WinPressed_MSPointerOutHandler(ev) { + if (this._pointerId === ev.pointerId) { + WinJS.Utilities.removeClass(this._element, WinJS.UI._WinPressed.winPressed); + } + }, + + _MSPointerCancelHandler: function _WinPressed_MSPointerCancelHandler(ev) { + if (this._pointerId === ev.pointerId) { + this._resetPointer(); + } + }, + + _MSPointerUpHandler: function _WinPressed_MSPointerUpHandler(ev) { + if (this._pointerId === ev.pointerId) { + this._resetPointer(); + } + }, + + _resetPointer: function _WinPressed_resetPointer() { + this._pointerId = null; + + window.removeEventListener("pointerup", this._pointerUpBound, true); + window.removeEventListener("pointercancel", this._pointerCancelBound, true); + this._element.removeEventListener("pointerover", this._pointerOverBound, true); + this._element.removeEventListener("pointerout", this._pointerOutBound, true); + + WinJS.Utilities.removeClass(this._element, WinJS.UI._WinPressed.winPressed); + }, + + dispose: function _WinPressed_dispose() { + if (this._disposed) { + return; + } + this._disposed = true; + + this._resetPointer(); + } + }, { + winPressed: "win-pressed" + }) + }), + /// + /// + /// Represents a navigation command in an NavBarContainer. + /// + /// + /// + /// + /// + /// ]]> + /// Styles the entire NavBarCommand control. + /// Styles the main button in a NavBarCommand. + /// Styles the split button in a NavBarCommand + /// Styles the icon in the main button of a NavBarCommand. + /// Styles the label in the main button of a NavBarCommand. + /// + /// + /// + NavBarCommand: WinJS.Namespace._lazy(function () { + var strings = { + get duplicateConstruction() { return WinJS.Resources._getWinJSString("ui/duplicateConstruction").value; } + }; + + var NavBarCommand = WinJS.Class.define(function NavBarCommand_ctor(element, options) { + /// + /// + /// Creates a new NavBarCommand. + /// + /// + /// The DOM element that will host the new NavBarCommand control. + /// + /// + /// An object that contains one or more property/value pairs to apply to the new control. + /// Each property of the options object corresponds to one of the control's properties or events. + /// Event names must begin with "on". + /// + /// + /// The new NavBarCommand. + /// + /// + /// + element = element || document.createElement("DIV"); + options = options || {}; + + if (element.winControl) { + throw new WinJS.ErrorFromName("WinJS.UI.NavBarCommand.DuplicateConstruction", strings.duplicateConstruction); + } + + // Attaching JS control to DOM element + element.winControl = this; + this._element = element; + WinJS.Utilities.addClass(this.element, WinJS.UI.NavBarCommand._ClassName.navbarcommand); + WinJS.Utilities.addClass(this.element, "win-disposable"); + + this._tooltip = null; + this._splitOpened = false; + this._buildDom(); + element.addEventListener('keydown', this._keydownHandler.bind(this)); + + WinJS.UI.setOptions(this, options); + }, { + /// + element: { + get: function () { + return this._element; + } + }, + + /// + /// Gets or sets the label of the NavBarCommand. + /// + /// + label: { + get: function () { + return this._label; + }, + set: function (value) { + this._label = value; + this._labelEl.textContent = value; + } + }, + + /// + /// Gets or sets the tooltip of the NavBarCommand. + /// + /// + tooltip: { + get: function () { + return this._tooltip; + }, + set: function (value) { + this._tooltip = value; + if (this._tooltip || this._tooltip === "") { + this._element.setAttribute('title', this._tooltip); + } else { + this._element.removeAttribute('title'); + } + } + }, + + /// + /// Gets or sets the icon of the NavBarCommand. This value is either one of the values of the AppBarIcon enumeration or the path of a custom PNG file. + /// + /// + icon: { + get: function () { + return this._icon; + }, + set: function (value) { + this._icon = (WinJS.UI.AppBarIcon[value] || value); + + // If the icon's a single character, presume a glyph + if (this._icon && this._icon.length === 1) { + // Set the glyph + this._imageSpan.innerText = this._icon; + this._imageSpan.style.backgroundImage = ""; + this._imageSpan.style.msHighContrastAdjust = ""; + this._imageSpan.style.display = ""; + } else if (this._icon && this._icon.length > 1) { + // Must be an image, set that + this._imageSpan.innerText = ""; + this._imageSpan.style.backgroundImage = this._icon; + this._imageSpan.style.msHighContrastAdjust = "none"; + this._imageSpan.style.display = ""; + } else { + this._imageSpan.innerText = ""; + this._imageSpan.style.backgroundImage = ""; + this._imageSpan.style.msHighContrastAdjust = ""; + this._imageSpan.style.display = "none"; + } + } + }, + + /// + /// Gets or sets the command's target location. + /// + /// + location: { + get: function () { + return this._location; + }, + set: function (value) { + this._location = value; + } + }, + + /// + /// Gets or sets the state value used for navigation. The command passes this object to the WinJS.Navigation.navigate function. + /// + /// + state: { + get: function () { + return this._state; + }, + set: function (value) { + this._state = value; + } + }, + + /// + /// Gets or sets a value that specifies whether the NavBarCommand has a split button. + /// + /// + splitButton: { + get: function () { + return this._split; + }, + set: function (value) { + this._split = value; + if (this._split) { + this._splitButtonEl.style.display = "flex"; + } else { + this._splitButtonEl.style.display = "none"; + } + } + }, + + /// + splitOpened: { + get: function () { + return this._splitOpened; + }, + set: function (value) { + if (this._splitOpened !== !!value) { + this._toggleSplit(); + } + } + }, + + _toggleSplit: function NavBarCommand_toggleSplit() { + this._splitOpened = !this._splitOpened; + if (this._splitOpened) { + WinJS.Utilities.addClass(this._splitButtonEl, WinJS.UI.NavBarCommand._ClassName.navbarcommandsplitbuttonopened); + this._splitButtonEl.setAttribute("aria-expanded", "true"); + } else { + WinJS.Utilities.removeClass(this._splitButtonEl, WinJS.UI.NavBarCommand._ClassName.navbarcommandsplitbuttonopened); + this._splitButtonEl.setAttribute("aria-expanded", "false"); + } + this._fireEvent(WinJS.UI.NavBarCommand._EventName._splitToggle); + }, + + _rtl: { + get: function () { + return window.getComputedStyle(this.element).direction === "rtl"; + } + }, + + _keydownHandler: function NavBarCommand_keydownHandler(ev) { + if (ev.srcElement.msMatchesSelector(".win-interactive, .win-interactive *")) { + return; + } + + var leftStr = this._rtl ? "Right" : "Left"; + var rightStr = this._rtl ? "Left" : "Right"; + + if (!ev.altKey && (ev.key === leftStr || ev.key === "Home" || ev.key === "End") && ev.srcElement === this._splitButtonEl) { + this._splitButtonActive = false; + try { + this._buttonEl.setActive(); + } catch (e) { + } + if (ev.key === leftStr) { + ev.stopPropagation(); + } + ev.preventDefault(); + } else if (!ev.altKey && ev.key === rightStr && this.splitButton && (ev.srcElement === this._buttonEl || this._buttonEl.contains(ev.srcElement))) { + this._splitButtonActive = true; + try { + this._splitButtonEl.setActive(); + } catch (e) { + } + if (ev.key === rightStr) { + ev.stopPropagation(); + } + ev.preventDefault(); + } else if ((ev.key === "Spacebar" || ev.key === "Enter") && (ev.srcElement === this._buttonEl || this._buttonEl.contains(ev.srcElement))) { + if (this.location) { + WinJS.Navigation.navigate(this.location, this.state); + } + this._fireEvent(WinJS.UI.NavBarCommand._EventName._invoked); + } else if ((ev.key === "Spacebar" || ev.key === "Enter") && ev.srcElement === this._splitButtonEl) { + this._toggleSplit(); + } + }, + + _getFocusInto: function NavBarCommand_getFocusInto(key) { + var leftStr = this._rtl ? "Right" : "Left"; + if ((key === leftStr) && this.splitButton) { + this._splitButtonActive = true; + return this._splitButtonEl; + } else { + this._splitButtonActive = false; + return this._buttonEl; + } + }, + + _buildDom: function NavBarCommand_buildDom() { + var markup = + '
' + + '
' + + '
' + + '
' + + '
' + + '
' + + ''; + this.element.insertAdjacentHTML("afterBegin", markup); + + this._buttonEl = this.element.firstElementChild; + this._buttonPressedBehavior = new WinJS.UI._WinPressed(this._buttonEl); + this._contentEl = this._buttonEl.firstElementChild; + this._imageSpan = this._contentEl.firstElementChild; + this._imageSpan.style.display = "none"; + this._labelEl = this._imageSpan.nextElementSibling; + this._splitButtonEl = this._buttonEl.nextElementSibling; + this._splitButtonPressedBehavior = new WinJS.UI._WinPressed(this._splitButtonEl); + this._splitButtonEl.style.display = "none"; + + WinJS.UI._ensureId(this._buttonEl); + this._splitButtonEl.setAttribute("aria-labelledby", this._buttonEl.id); + + this._buttonEl.addEventListener("click", this._handleButtonClick.bind(this)); + this._buttonEl.addEventListener("beforeactivate", this._beforeactivateButtonHandler.bind(this)); + this._buttonEl.addEventListener("pointerdown", this._MSPointerDownButtonHandler.bind(this)); + + var mutationObserver = new MutationObserver(this._splitButtonAriaExpandedPropertyChangeHandler.bind(this)); + mutationObserver.observe(this._splitButtonEl, { attributes: true, attributeFilter: ["aria-expanded"] }); + this._splitButtonEl.addEventListener("click", this._handleSplitButtonClick.bind(this)); + this._splitButtonEl.addEventListener("beforeactivate", this._beforeactivateSplitButtonHandler.bind(this)); + this._splitButtonEl.addEventListener("pointerdown", this._MSPointerDownSplitButtonHandler.bind(this)); + + // reparent any other elements. + var tempEl = this._splitButtonEl.nextSibling; + while (tempEl) { + this._buttonEl.insertBefore(tempEl, this._contentEl); + if (tempEl.nodeName !== "#text") { + WinJS.UI.processAll(tempEl); + } + tempEl = this._splitButtonEl.nextSibling; + } + }, + + _MSPointerDownButtonHandler: function NavBarCommand_MSPointerDownButtonHandler(ev) { + this._splitButtonActive = false; + }, + + _MSPointerDownSplitButtonHandler: function NavBarCommand_MSPointerDownSplitButtonHandler(ev) { + this._splitButtonActive = true; + }, + + _handleButtonClick: function NavBarCommand_handleButtonClick(ev) { + var srcElement = ev.srcElement; + if (!srcElement.msMatchesSelector(".win-interactive, .win-interactive *")) { + if (this.location) { + WinJS.Navigation.navigate(this.location, this.state); + } + this._fireEvent(WinJS.UI.NavBarCommand._EventName._invoked); + } + }, + + _splitButtonAriaExpandedPropertyChangeHandler: function NavBarCommand_splitButtonAriaExpandedPropertyChangeHandler() { + if ((this._splitButtonEl.getAttribute("aria-expanded") === "true") !== this._splitOpened) { + this._toggleSplit(); + } + }, + + _handleSplitButtonClick: function NavBarCommand_handleSplitButtonClick(ev) { + this._toggleSplit(); + }, + + _beforeactivateSplitButtonHandler: function NavBarCommand_beforeactivateSplitButtonHandler(ev) { + if (!this._splitButtonActive) { + ev.stopPropagation(); + ev.preventDefault(); + } + }, + + _beforeactivateButtonHandler: function NavBarCommand_beforeactivateButtonHandler(ev) { + if (this._splitButtonActive) { + ev.stopPropagation(); + ev.preventDefault(); + } + }, + + _fireEvent: function NavBarCommand_fireEvent(type, detail) { + var event = document.createEvent("CustomEvent"); + event.initCustomEvent(type, true, false, detail); + this.element.dispatchEvent(event); + }, + + dispose: function NavBarCommand_dispose() { + /// + /// + /// Disposes this control. + /// + /// + /// + if (this._disposed) { + return; + } + this._disposed = true; + + this._buttonPressedBehavior.dispose(); + this._splitButtonPressedBehavior.dispose(); + } + }, { + _ClassName: { + navbarcommand: "win-navbarcommand", + navbarcommandbutton: "win-navbarcommand-button", + navbarcommandbuttoncontent: "win-navbarcommand-button-content", + navbarcommandsplitbutton: "win-navbarcommand-splitbutton", + navbarcommandsplitbuttonopened: "win-navbarcommand-splitbutton-opened", + navbarcommandicon: "win-navbarcommand-icon", + navbarcommandlabel: "win-navbarcommand-label" + }, + _EventName: { + _invoked: "_invoked", + _splitToggle: "_splittoggle" + } + }); + WinJS.Class.mix(NavBarCommand, WinJS.UI.DOMEventMixin); + return NavBarCommand; + }) + }); + +})(this, WinJS); + +(function tooltipInit(global) { + "use strict"; + + // Tooltip control implementation + WinJS.Namespace.define("WinJS.UI", { + /// + /// + /// Displays a tooltip that can contain images and formatting. + /// + /// + /// + /// + /// + /// ]]> + /// Raised when the tooltip is about to appear. + /// Raised when the tooltip is showing. + /// Raised when the tooltip is about to become hidden. + /// Raised when the tooltip is hidden. + /// The entire Tooltip control. + /// + /// + /// + Tooltip: WinJS.Namespace._lazy(function () { + var lastCloseTime = 0; + var utilities = WinJS.Utilities; + var animation = WinJS.UI.Animation; + + // Constants definition + var DEFAULT_PLACEMENT = "top"; + var DELAY_INITIAL_TOUCH_SHORT = 400; + var DELAY_INITIAL_TOUCH_LONG = 1200; + var DEFAULT_MOUSE_HOVER_TIME = 400; // 0.4 second + var DEFAULT_MESSAGE_DURATION = 5000; // 5 secs + var DELAY_RESHOW_NONINFOTIP_TOUCH = 0; + var DELAY_RESHOW_NONINFOTIP_NONTOUCH = 600; + var DELAY_RESHOW_INFOTIP_TOUCH = 400; + var DELAY_RESHOW_INFOTIP_NONTOUCH = 600; + var RESHOW_THRESHOLD = 200; + var HIDE_DELAY_MAX = 300000; // 5 mins + var OFFSET_KEYBOARD = 12; + var OFFSET_MOUSE = 20; + var OFFSET_TOUCH = 45; + var OFFSET_PROGRAMMATIC_TOUCH = 20; + var OFFSET_PROGRAMMATIC_NONTOUCH = 12; + var SAFETY_NET_GAP = 1; // We set a 1-pixel gap between the right or bottom edge of the tooltip and the viewport to avoid possible re-layout + var PT_TOUCH = MSPointerEvent.MSPOINTER_TYPE_TOUCH || "touch"; // pointer type to indicate a touch event + + var EVENTS_INVOKE = { "keyup": "", "pointerover": "" }, + EVENTS_UPDATE = { "pointermove": "" }, + EVENTS_DISMISS = { "pointerdown": "", "keydown": "", "blur": "", "pointerout": "", "pointercancel": "", "pointerup": "" }, + EVENTS_BY_CHILD = { "pointerover": "", "pointerout": "" }; + + // CSS class names + var msTooltip = "win-tooltip", + msTooltipPhantom = "win-tooltip-phantom"; + + // Global attributes + var mouseHoverTime = DEFAULT_MOUSE_HOVER_TIME, + nonInfoTooltipNonTouchShowDelay = 2 * mouseHoverTime, + infoTooltipNonTouchShowDelay = 2.5 * mouseHoverTime, + messageDuration = DEFAULT_MESSAGE_DURATION, + isLeftHanded = false; + + var hasInitWinRTSettings = false; + + var createEvent = WinJS.Utilities._createEventProperty; + + return WinJS.Class.define(function Tooltip_ctor(anchorElement, options) { + /// + /// + /// Creates a new Tooltip. + /// + /// + /// The DOM element that hosts the Tooltip. + /// + /// + /// An object that contains one or more property/value pairs to apply to the new control. + /// Each property of the options object corresponds to one of the control's properties or events. + /// Event names must begin with "on". For example, to provide a handler for the opened event, + /// add a property named "onopened" to the options object and set its value to the event handler. + /// This parameter is optional. + /// + /// + /// The new Tooltip. + /// + /// + /// + anchorElement = anchorElement || document.createElement("div"); + + var tooltip = utilities.data(anchorElement).tooltip; + if (tooltip) { + return tooltip; + } + + // Set system attributes if it is in WWA, otherwise, use the default values + if (!hasInitWinRTSettings && WinJS.Utilities.hasWinRT) { // in WWA + var uiSettings = new Windows.UI.ViewManagement.UISettings(); + mouseHoverTime = uiSettings.mouseHoverTime; + nonInfoTooltipNonTouchShowDelay = 2 * mouseHoverTime; + infoTooltipNonTouchShowDelay = 2.5 * mouseHoverTime; + messageDuration = uiSettings.messageDuration * 1000; // uiSettings.messageDuration is in seconds. + var handedness = uiSettings.handPreference; + isLeftHanded = (handedness == Windows.UI.ViewManagement.HandPreference.leftHanded); + } + hasInitWinRTSettings = true; + + // Need to initialize properties + this._disposed = false; + this._placement = DEFAULT_PLACEMENT; + this._infotip = false; + this._innerHTML = null; + this._contentElement = null; + this._extraClass = null; + this._lastContentType = "html"; + this._anchorElement = anchorElement; + this._domElement = null; + this._phantomDiv = null; + this._triggerByOpen = false; + this._eventListenerRemoveStack = []; + + // To handle keyboard navigation + this._lastKeyOrBlurEvent = null; + this._currentKeyOrBlurEvent = null; + + // Remember ourselves + anchorElement.winControl = this; + WinJS.Utilities.addClass(anchorElement, "win-disposable"); + + // If anchor element's title is defined, set as the default tooltip content + if (anchorElement.title) { + this._innerHTML = this._anchorElement.title; + this._anchorElement.removeAttribute("title"); + } + + WinJS.UI.setOptions(this, options); + this._events(); + utilities.data(anchorElement).tooltip = this; + }, { + /// + /// Gets or sets the HTML content of the Tooltip. + /// + /// + innerHTML: { + get: function () { + return this._innerHTML; + }, + set: function (value) { + this._innerHTML = value; + if (this._domElement) { + // If we set the innerHTML to null or "" while tooltip is up, we should close it + if (!this._innerHTML || this._innerHTML === "") { + this._onDismiss(); + return; + } + this._domElement.innerHTML = value; + this._position(); + } + this._lastContentType = "html"; + } + }, + + /// + element: { + get: function () { + return this._anchorElement; + } + }, + + /// + /// Gets or sets the DOM element that is the content for the ToolTip. + /// + /// + contentElement: { + get: function () { + return this._contentElement; + }, + set: function (value) { + this._contentElement = value; + if (this._domElement) { + // If we set the contentElement to null while tooltip is up, we should close it + if (!this._contentElement) { + this._onDismiss(); + return; + } + this._domElement.innerHTML = ""; + this._domElement.appendChild(this._contentElement); + this._position(); + } + this._lastContentType = "element"; + } + }, + + /// + /// Gets or sets the position for the Tooltip relative to its target element: top, bottom, left or right. + /// + /// + placement: { + get: function () { + return this._placement; + }, + set: function (value) { + if (value !== "top" && value !== "bottom" && value !== "left" && value !== "right") { + value = DEFAULT_PLACEMENT; + } + this._placement = value; + if (this._domElement) { + this._position(); + } + } + }, + + /// + /// Gets or sets a value that specifies whether the Tooltip is an infotip, a tooltip that contains + /// a lot of info and should be displayed for longer than a typical Tooltip. + /// The default value is false. + /// + /// + infotip: { + get: function () { + return this._infotip; + }, + set: function (value) { + this._infotip = !!value; //convert the value to boolean + } + }, + + /// + /// Gets or sets additional CSS classes to apply to the Tooltip control's host element. + /// + /// + extraClass: { + get: function () { + return this._extraClass; + }, + set: function (value) { + this._extraClass = value; + } + }, + + /// + /// Raised just before the Tooltip appears. + /// + /// + onbeforeopen: createEvent("beforeopen"), + + /// + /// Raised when the Tooltip is shown. + /// + /// + onopened: createEvent("opened"), + + /// + /// Raised just before the Tooltip is hidden. + /// + /// + onbeforeclose: createEvent("beforeclose"), + + /// + /// Raised when the Tooltip is no longer displayed. + /// + /// + onclosed: createEvent("closed"), + + dispose: function () { + /// + /// + /// Disposes this Tooltip. + /// + /// + /// + if (this._disposed) { + return; + } + + this._disposed = true; + WinJS.Utilities.disposeSubTree(this.element); + for (var i = 0, len = this._eventListenerRemoveStack.length; i < len; i++) { + this._eventListenerRemoveStack[i](); + } + this._onDismiss(); + var data = utilities.data(this._anchorElement); + if (data) { + delete data.tooltip; + } + }, + + addEventListener: function (eventName, eventCallBack, capture) { + /// + /// + /// Registers an event handler for the specified event. + /// + /// The name of the event. + /// The event handler function to associate with this event. + /// Set to true to register the event handler for the capturing phase; set to false to register for the bubbling phase. + /// + /// + + if (this._anchorElement) { + this._anchorElement.addEventListener(eventName, eventCallBack, capture); + + var that = this; + this._eventListenerRemoveStack.push(function () { + that._anchorElement.removeEventListener(eventName, eventCallBack, capture); + }); + } + }, + + removeEventListener: function (eventName, eventCallBack, capture) { + /// + /// + /// Unregisters an event handler for the specified event. + /// + /// The name of the event. + /// The event handler function to remove. + /// Set to true to unregister the event handler for the capturing phase; otherwise, set to false to unregister the event handler for the bubbling phase. + /// + /// + + if (this._anchorElement) { + this._anchorElement.removeEventListener(eventName, eventCallBack, capture); + } + }, + + open: function (type) { + /// + /// + /// Shows the Tooltip. + /// + /// The type of tooltip to show: "touch", "mouseover", "mousedown", or "keyboard". The default value is "mousedown". + /// + /// + + // Open takes precedence over other triggering events + // Once tooltip is opened using open(), it can only be closed by time out(mouseover or keyboard) or explicitly by close(). + this._triggerByOpen = true; + + if (type !== "touch" && type !== "mouseover" && type !== "mousedown" && type !== "keyboard") { + type = "default"; + } + + switch (type) { + case "touch": + this._onInvoke("touch", "never"); + break; + case "mouseover": + this._onInvoke("mouse", "auto"); + break; + case "keyboard": + this._onInvoke("keyboard", "auto"); + break; + case "mousedown": + case "default": + this._onInvoke("nodelay", "never"); + break; + } + + }, + + close: function () { + /// + /// + /// Hids the Tooltip. + /// + /// + /// + + this._onDismiss(); + }, + + _cleanUpDOM: function () { + if (this._domElement) { + WinJS.Utilities.disposeSubTree(this._domElement); + document.body.removeChild(this._domElement); + this._domElement = null; + + document.body.removeChild(this._phantomDiv); + this._phantomDiv = null; + } + }, + + _createTooltipDOM: function () { + this._cleanUpDOM(); + + this._domElement = document.createElement("div"); + + var id = this._domElement.uniqueID; + this._domElement.setAttribute("id", id); + + // Set the direction of tooltip according to anchor element's + var computedStyle = document.defaultView.getComputedStyle(this._anchorElement, null); + var elemStyle = this._domElement.style; + elemStyle.direction = computedStyle.direction; + elemStyle.writingMode = computedStyle["writing-mode"]; // must use CSS name, not JS name + + // Make the tooltip non-focusable + this._domElement.setAttribute("tabindex", -1); + + // Set the aria tags for accessibility + this._domElement.setAttribute("role", "tooltip"); + this._anchorElement.setAttribute("aria-describedby", id); + + // Set the tooltip content + if (this._lastContentType === "element") { // Last update through contentElement option + this._domElement.appendChild(this._contentElement); + } else { // Last update through innerHTML option + this._domElement.innerHTML = this._innerHTML; + } + + document.body.appendChild(this._domElement); + utilities.addClass(this._domElement, msTooltip); + + // In the event of user-assigned classes, add those too + if (this._extraClass) { + utilities.addClass(this._domElement, this._extraClass); + } + + // Create a phantom div on top of the tooltip div to block all interactions + this._phantomDiv = document.createElement("div"); + this._phantomDiv.setAttribute("tabindex", -1); + document.body.appendChild(this._phantomDiv); + utilities.addClass(this._phantomDiv, msTooltipPhantom); + var zIndex = document.defaultView.getComputedStyle(this._domElement, null).zIndex + 1; + this._phantomDiv.style.zIndex = zIndex; + }, + + _raiseEvent: function (type, eventProperties) { + if (this._anchorElement) { + var customEvent = document.createEvent("CustomEvent"); + customEvent.initCustomEvent(type, false, false, eventProperties); + this._anchorElement.dispatchEvent(customEvent); + } + }, + + // Support for keyboard navigation + _captureLastKeyBlurOrPointerOverEvent: function (event, listener) { + listener._lastKeyOrBlurEvent = listener._currentKeyOrBlurEvent; + switch (event.type) { + case "keyup": + if (event.key === "Shift") { + listener._currentKeyOrBlurEvent = null; + } else { + listener._currentKeyOrBlurEvent = "keyboard"; + } + break; + case "blur": + //anchor elment no longer in focus, clear up the stack + listener._currentKeyOrBlurEvent = null; + break; + default: + break; + + } + }, + + _registerEventToListener: function (element, eventType, listener) { + var handler = function (event) { + listener._captureLastKeyBlurOrPointerOverEvent(event, listener); + listener._handleEvent(event); + }; + element.addEventListener(eventType, handler, false); + + this._eventListenerRemoveStack.push(function () { + element.removeEventListener(eventType, handler, false); + }); + }, + + _events: function () { + for (var eventType in EVENTS_INVOKE) { + this._registerEventToListener(this._anchorElement, eventType, this); + } + for (var eventType in EVENTS_UPDATE) { + this._registerEventToListener(this._anchorElement, eventType, this); + } + for (eventType in EVENTS_DISMISS) { + this._registerEventToListener(this._anchorElement, eventType, this); + } + + + }, + + _handleEvent: function (event) { + var eventType = event.type; + if (!this._triggerByOpen) { + // If the anchor element has children, we should ignore events that are caused within the anchor element + // Please note that we are not using event.target here as in bubbling phases from the child, the event target + // is usually the child + if (eventType in EVENTS_BY_CHILD) { + var elem = event.relatedTarget; + + while (elem && elem !== this._anchorElement && elem !== document.body) { + try { + elem = elem.parentNode; + } + catch (e) { + if (e instanceof Error && e.message === 'Permission denied') { + //Permission denied error, if we can't access the node's + //information, we should not handle the event + //Put this guard prior Bug 484666 is fixed + return; + } + else { + throw e; + } + } + } + if (elem === this._anchorElement) { + return; + } + } + if (eventType in EVENTS_INVOKE) { + if (event.pointerType == PT_TOUCH) { + this._onInvoke("touch", "never", event); + this._showTrigger = "touch"; + } else { + var type = eventType.substring(0, 3) === "key" ? "keyboard" : "mouse"; + this._onInvoke(type, "auto", event); + this._showTrigger = type; + } + } else if (eventType in EVENTS_UPDATE) { + this._contactPoint = { x: event.clientX, y: event.clientY }; + } else if (eventType in EVENTS_DISMISS) { + var eventTrigger; + if (event.pointerType == PT_TOUCH) { + if (eventType == "pointerdown") { + return; + } + eventTrigger = "touch"; + } + else { + eventTrigger = eventType.substring(0, 3) === "key" ? "keyboard" : "mouse"; + } + if (eventType != "blur" && eventTrigger != this._showTrigger) { + return; + } + this._onDismiss(); + } + } + }, + + _onShowAnimationEnd: function () { + if (this._shouldDismiss || this._disposed) { + return; + } + this._raiseEvent("opened"); + if (this._domElement) { + if (this._hideDelay !== "never") { + var that = this; + var delay = this._infotip ? Math.min(3 * messageDuration, HIDE_DELAY_MAX) : messageDuration; + this._hideDelayTimer = setTimeout(function () { + that._onDismiss(); + }, delay); + } + } + }, + + + _onHideAnimationEnd: function () { + document.body.removeEventListener("DOMNodeRemoved", this._removeTooltip, false); + this._cleanUpDOM(); + // Once we remove the tooltip from the DOM, we should remove the aria tag from the anchor + if (this._anchorElement) { + this._anchorElement.removeAttribute("aria-describedby"); + } + lastCloseTime = (new Date()).getTime(); + this._triggerByOpen = false; + if (!this._disposed) { + this._raiseEvent("closed"); + } + }, + + _decideOnDelay: function (type) { + var value; + this._useAnimation = true; + + if (type == "nodelay") { + value = 0; + this._useAnimation = false; + } + else { + var curTime = (new Date()).getTime(); + // If the mouse is moved immediately from another anchor that has + // tooltip open, we should use a shorter delay + if (curTime - lastCloseTime <= RESHOW_THRESHOLD) { + if (type == "touch") { + value = this._infotip ? DELAY_RESHOW_INFOTIP_TOUCH : DELAY_RESHOW_NONINFOTIP_TOUCH; + } + else { + value = this._infotip ? DELAY_RESHOW_INFOTIP_NONTOUCH : DELAY_RESHOW_NONINFOTIP_NONTOUCH; + } + this._useAnimation = false; + } else if (type == "touch") { + value = this._infotip ? DELAY_INITIAL_TOUCH_LONG : DELAY_INITIAL_TOUCH_SHORT; + } else { + value = this._infotip ? infoTooltipNonTouchShowDelay : nonInfoTooltipNonTouchShowDelay; + } + } + return value; + }, + + // This function returns the anchor element's position in the Window coordinates. + _getAnchorPositionFromElementWindowCoord: function () { + var rect = this._anchorElement.getBoundingClientRect(); + + return { + x: rect.left, + y: rect.top, + width: rect.width, + height: rect.height + }; + }, + + _getAnchorPositionFromPointerWindowCoord: function (contactPoint) { + return { + x: contactPoint.x, + y: contactPoint.y, + width: 1, + height: 1 + }; + }, + + _canPositionOnSide: function (placement, viewport, anchor, tip) { + var availWidth = 0, availHeight = 0; + + switch (placement) { + case "top": + availWidth = tip.width + this._offset; + availHeight = anchor.y; + break; + case "bottom": + availWidth = tip.width + this._offset; + availHeight = viewport.height - anchor.y - anchor.height; + break; + case "left": + availWidth = anchor.x; + availHeight = tip.height + this._offset; + break; + case "right": + availWidth = viewport.width - anchor.x - anchor.width; + availHeight = tip.height + this._offset; + break; + } + return ((availWidth >= tip.width + this._offset) && (availHeight >= tip.height + this._offset)); + }, + + _positionOnSide: function (placement, viewport, anchor, tip) { + var left = 0, top = 0; + + switch (placement) { + case "top": + case "bottom": + // Align the tooltip to the anchor's center horizontally + left = anchor.x + anchor.width / 2 - tip.width / 2; + + // If the left boundary is outside the window, set it to 0 + // If the right boundary is outside the window, set it to align with the window right boundary + left = Math.min(Math.max(left, 0), viewport.width - tip.width - SAFETY_NET_GAP); + + top = (placement == "top") ? anchor.y - tip.height - this._offset : anchor.y + anchor.height + this._offset; + break; + case "left": + case "right": + // Align the tooltip to the anchor's center vertically + top = anchor.y + anchor.height / 2 - tip.height / 2; + + // If the top boundary is outside the window, set it to 0 + // If the bottom boundary is outside the window, set it to align with the window bottom boundary + top = Math.min(Math.max(top, 0), viewport.height - tip.height - SAFETY_NET_GAP); + + left = (placement == "left") ? anchor.x - tip.width - this._offset : anchor.x + anchor.width + this._offset; + break; + } + + // Actually set the position + this._domElement.style.left = left + "px"; + this._domElement.style.top = top + "px"; + + // Set the phantom's position and size + this._phantomDiv.style.left = left + "px"; + this._phantomDiv.style.top = top + "px"; + this._phantomDiv.style.width = tip.width + "px"; + this._phantomDiv.style.height = tip.height + "px"; + }, + + _position: function (contactType) { + var viewport = { width: 0, height: 0 }; + var anchor = { x: 0, y: 0, width: 0, height: 0 }; + var tip = { width: 0, height: 0 }; + + viewport.width = document.documentElement.clientWidth; + viewport.height = document.documentElement.clientHeight; + if (document.defaultView.getComputedStyle(document.body, null)["writing-mode"] === "tb-rl") { + viewport.width = document.documentElement.clientHeight; + viewport.height = document.documentElement.clientWidth; + } + + if (this._contactPoint && (contactType === "touch" || contactType === "mouse")) { + anchor = this._getAnchorPositionFromPointerWindowCoord(this._contactPoint); + } + else { + // keyboard or programmatic is relative to element + anchor = this._getAnchorPositionFromElementWindowCoord(); + } + tip.width = this._domElement.offsetWidth; + tip.height = this._domElement.offsetHeight; + var fallback_order = { + "top": ["top", "bottom", "left", "right"], + "bottom": ["bottom", "top", "left", "right"], + "left": ["left", "right", "top", "bottom"], + "right": ["right", "left", "top", "bottom"] + }; + if (isLeftHanded) { + fallback_order.top[2] = "right"; + fallback_order.top[3] = "left"; + fallback_order.bottom[2] = "right"; + fallback_order.bottom[3] = "left"; + } + + // Try to position the tooltip according to the placement preference + // We use this order: + // 1. Try the preferred placement + // 2. Try the opposite placement + // 3. If the preferred placement is top or bottom, we should try left + // and right (or right and left if left handed) + // If the preferred placement is left or right, we should try top and bottom + var order = fallback_order[this._placement]; + var length = order.length; + for (var i = 0; i < length; i++) { + if (i == length - 1 || this._canPositionOnSide(order[i], viewport, anchor, tip)) { + this._positionOnSide(order[i], viewport, anchor, tip); + break; + } + } + return order[i]; + }, + + _showTooltip: function (contactType) { + // Give a chance to dismiss the tooltip before it starts to show + if (this._shouldDismiss) { + return; + } + this._isShown = true; + this._raiseEvent("beforeopen"); + + // If the anchor is not in the DOM tree, we don't create the tooltip + if (!document.body.contains(this._anchorElement)) { + return; + } + if (this._shouldDismiss) { + return; + } + + // If the contentElement is set to null or innerHTML set to null or "", we should NOT show the tooltip + if (this._lastContentType === "element") { // Last update through contentElement option + if (!this._contentElement) { + this._isShown = false; + return; + } + } else { // Last update through innerHTML option + if (!this._innerHTML || this._innerHTML === "") { + this._isShown = false; + return; + } + } + + var that = this; + this._removeTooltip = function (event) { + var current = that._anchorElement; + while (current) { + if (event.target == current) { + document.body.removeEventListener("DOMNodeRemoved", that._removeTooltip, false); + that._cleanUpDOM(); + break; + } + current = current.parentNode; + } + }; + + document.body.addEventListener("DOMNodeRemoved", this._removeTooltip, false); + this._createTooltipDOM(); + var pos = this._position(contactType); + var that = this; + if (this._useAnimation) { + animation.fadeIn(this._domElement) + .then(this._onShowAnimationEnd.bind(this)); + } else { + this._onShowAnimationEnd(); + } + }, + + _onInvoke: function (type, hide, event) { + // Reset the dismiss flag + this._shouldDismiss = false; + + // If the tooltip is already shown, ignore the current event + if (this._isShown) { + return; + } + + // To handle keyboard support, we only want to display tooltip on the first tab key event only + if (event && event.type === "keyup") { + if (this._lastKeyOrBlurEvent == "keyboard" || + !this._lastKeyOrBlurEvent && event.key !== "Tab") { + return; + } + } + + // Set the hide delay, + this._hideDelay = hide; + + this._contactPoint = null; + if (event) { // Open through interaction + this._contactPoint = { x: event.clientX, y: event.clientY }; + // Tooltip display offset differently for touch events and non-touch events + if (type == "touch") { + this._offset = OFFSET_TOUCH; + } else if (type === "keyboard") { + this._offset = OFFSET_KEYBOARD; + } else { + this._offset = OFFSET_MOUSE; + } + } else { // Open Programmatically + if (type == "touch") { + this._offset = OFFSET_PROGRAMMATIC_TOUCH; + } else { + this._offset = OFFSET_PROGRAMMATIC_NONTOUCH; + } + } + + clearTimeout(this._delayTimer); + clearTimeout(this._hideDelayTimer); + + // Set the delay time + var delay = this._decideOnDelay(type); + if (delay > 0) { + var that = this; + this._delayTimer = setTimeout(function () { + that._showTooltip(type); + }, delay); + } else { + this._showTooltip(type); + } + }, + + _onDismiss: function () { + // Set the dismiss flag so that we don't miss dismiss events + this._shouldDismiss = true; + + // If the tooltip is already dismissed, ignore the current event + if (!this._isShown) { + return; + } + + this._isShown = false; + + // Reset tooltip state + this._showTrigger = "mouse"; + + if (this._domElement) { + this._raiseEvent("beforeclose"); + if (this._useAnimation) { + animation.fadeOut(this._domElement) + .then(this._onHideAnimationEnd.bind(this)); + } else { + this._onHideAnimationEnd(); + } + } else { + this._raiseEvent("beforeclose"); + this._raiseEvent("closed"); + } + } + }); + }) + }); + +})(this, WinJS); +// ViewBox control +(function viewboxInit(global, undefined) { + "use strict"; + + WinJS.Namespace.define("WinJS.UI", { + /// + /// Scales a single child element to fill the available space without + /// resizing it. This control reacts to changes in the size of the container as well as + /// changes in size of the child element. For example, a media query may result in + /// a change in aspect ratio. + /// + /// View Box + /// + /// + ///
ViewBox
]]>
+ /// + /// + /// + ViewBox: WinJS.Namespace._lazy(function () { + var Scheduler = WinJS.Utilities.Scheduler; + + var strings = { + get invalidViewBoxChildren() { return WinJS.Resources._getWinJSString("ui/invalidViewBoxChildren").value; }, + }; + + function onresize(control) { + if (control && !control._resizing) { + control._resizing = control._resizing || 0; + control._resizing++; + try { + control._updateLayout(); + } finally { + control._resizing--; + } + } + } + + function onresizeBox(ev) { + if (ev.srcElement) { + onresize(ev.srcElement.winControl); + } + } + + function onresizeSizer(ev) { + if (ev.srcElement) { + onresize(ev.srcElement.parentElement.winControl); + } + } + + var ViewBox = WinJS.Class.define(function ViewBox_ctor(element, options) { + /// + /// Initializes a new instance of the ViewBox control + /// + /// The DOM element that functions as the scaling box. This element fills 100% of the width and height allotted to it. + /// + /// + /// The set of options to be applied initially to the ViewBox control. + /// + /// A constructed ViewBox control. + /// + this._disposed = false; + + this._element = element || document.createElement("div"); + var box = this.element; + box.winControl = this; + WinJS.Utilities.addClass(box, "win-disposable"); + WinJS.Utilities.addClass(box, "win-viewbox"); + this.forceLayout(); + }, { + _sizer: null, + _element: null, + + /// + element: { + get: function () { return this._element; } + }, + + _rtl: { + get: function () { + return window.getComputedStyle(this.element).direction === "rtl"; + } + }, + + _initialize: function () { + var box = this.element; + if (box.firstElementChild !== this._sizer) { + if (WinJS.validation) { + if (box.childElementCount != 1) { + throw new WinJS.ErrorFromName("WinJS.UI.ViewBox.InvalidChildren", strings.invalidViewBoxChildren); + } + } + if (this._sizer) { + this._sizer.onresize = null; + } + var sizer = box.firstElementChild; + this._sizer = sizer; + if (sizer) { + box.addEventListener("mselementresize", onresizeBox); + sizer.addEventListener("mselementresize", onresizeSizer); + } + if (box.clientWidth === 0 && box.clientHeight === 0) { + var that = this; + // Wait for the viewbox to get added to the DOM. It should be added + // in the synchronous block in which _initialize was called. + Scheduler.schedule(function () { + that._updateLayout(); + }, Scheduler.Priority.normal, null, "WinJS.UI.ViewBox._updateLayout") + } + } + }, + _updateLayout: function () { + var sizer = this._sizer; + if (sizer) { + var box = this.element; + var w = sizer.clientWidth; + var h = sizer.clientHeight; + var bw = box.clientWidth; + var bh = box.clientHeight; + var wRatio = bw / w; + var hRatio = bh / h; + var mRatio = Math.min(wRatio, hRatio); + var transX = Math.abs(bw - (w * mRatio)) / 2; + var transY = Math.abs(bh - (h * mRatio)) / 2; + var rtl = this._rtl; + this._sizer.style["transform"] = "translate(" + (rtl ? "-" : "") + transX + "px," + transY + "px) scale(" + mRatio + ")"; + this._sizer.style["transform-origin"] = rtl ? "top right" : "top left"; + } + }, + + dispose: function () { + /// + /// + /// Disposes this ViewBox. + /// + /// + if (this._disposed) { + return; + } + + this._disposed = true; + WinJS.Utilities.disposeSubTree(this._element); + }, + + forceLayout: function () { + this._initialize(); + this._updateLayout(); + } + }); + WinJS.Class.mix(ViewBox, WinJS.UI.DOMEventMixin); + return ViewBox; + }) + }); + +}(this)); + +msWriteProfilerMark("Microsoft.WinJS.2.0 1.0.9600.17018.winblue_gdr.140204-1946 ui.js,StopTM"); +