History

GSF.Web: Moved common script resources into GSF/Shared/Scripts - marked existing scripts as obsolete.

J. Ritchie Carroll committed 9 years ago

Commit 230686d1 parent 6d6e24d19 files · +1921 −40Browse files

Unified · Split

19 files
  1. Source/Libraries/GSF.Web/GSF.Web.csproj +7 −0
  2. Source/Libraries/GSF.Web/Model/Scripts/angular1.5.8.js +5 −0
  3. Source/Libraries/GSF.Web/Model/Scripts/gsf.web.client.js +6 −1
  4. Source/Libraries/GSF.Web/Model/Scripts/gsf.web.knockout.js +6 −1
  5. Source/Libraries/GSF.Web/Model/Scripts/gsf.web.primeui.js +6 −1
  6. Source/Libraries/GSF.Web/Model/Scripts/js.cookie.js +6 −1
  7. Source/Libraries/GSF.Web/Model/Scripts/ko-reactor.js +5 −0
  8. Source/Libraries/GSF.Web/Model/Scripts/ko-reactor.min.js +5 −0
  9. Source/Libraries/GSF.Web/Model/Scripts/ko.observableDictionary.js +6 −1
  10. Source/Libraries/GSF.Web/Model/Scripts/pagedViewModel.js +16 −0
  11. Source/Libraries/GSF.Web/Model/Views/PagedViewModel.cshtml +18 −14
  12. Source/Libraries/GSF.Web/Shared/Resources.cs +26 −21
  13. Source/Libraries/GSF.Web/Shared/Scripts/gsf.web.client.js +895 −0
  14. Source/Libraries/GSF.Web/Shared/Scripts/gsf.web.knockout.js +167 −0
  15. Source/Libraries/GSF.Web/Shared/Scripts/gsf.web.primeui.js +110 −0
  16. Source/Libraries/GSF.Web/Shared/Scripts/js.cookie.js +145 −0
  17. Source/Libraries/GSF.Web/Shared/Scripts/knockout.mapping.js +21 −0
  18. Source/Libraries/GSF.Web/Shared/Scripts/knockout.observableDictionary.js +224 −0
  19. Source/Libraries/GSF.Web/Shared/Scripts/knockout.reactor.js +247 −0
Source/Libraries/GSF.Web/GSF.Web.csproj+7−0
@@ -263,6 +263,13 @@ · 262 unmodified lines
263 <None Include="PostBuildSetup.bat" />263 <None Include="PostBuildSetup.bat" />
264 </ItemGroup>264 </ItemGroup>
265 <ItemGroup>265 <ItemGroup>
266 <EmbeddedResource Include="Shared\Scripts\knockout.mapping.js" />
267 <EmbeddedResource Include="Shared\Scripts\gsf.web.client.js" />
268 <EmbeddedResource Include="Shared\Scripts\gsf.web.knockout.js" />
269 <EmbeddedResource Include="Shared\Scripts\gsf.web.primeui.js" />
270 <EmbeddedResource Include="Shared\Scripts\js.cookie.js" />
271 <EmbeddedResource Include="Shared\Scripts\knockout.reactor.js" />
272 <EmbeddedResource Include="Shared\Scripts\knockout.observableDictionary.js" />
266 <EmbeddedResource Include="Security\Scripts\core.js" />273 <EmbeddedResource Include="Security\Scripts\core.js" />
267 <EmbeddedResource Include="Security\Scripts\enc-base64.js" />274 <EmbeddedResource Include="Security\Scripts\enc-base64.js" />
268 <EmbeddedResource Include="Security\Scripts\ntlm.js" />275 <EmbeddedResource Include="Security\Scripts\ntlm.js" />
Source/Libraries/GSF.Web/Model/Scripts/angular1.5.8.js+5−0
@@ -1,3 +1,8 @@
1/******************************************************************************************************\
2 !WARNING: The location for this resource has been moved!
3 New resource path is "/@GSF/Web/Shared/Scripts/", please update scripts to use this new location
4\******************************************************************************************************/
5
1/**6/**
2 * @license AngularJS v1.5.87 * @license AngularJS v1.5.8
3 * (c) 2010-2016 Google, Inc. http://angularjs.org8 * (c) 2010-2016 Google, Inc. http://angularjs.org
Source/Libraries/GSF.Web/Model/Scripts/gsf.web.client.js+6−1
@@ -1,4 +1,9 @@
1//******************************************************************************************************1/******************************************************************************************************\
2 !WARNING: The location for this resource has been moved!
3 New resource path is "/@GSF/Web/Shared/Scripts/", please update scripts to use this new location
4\******************************************************************************************************/
5
6//******************************************************************************************************
2// gsf.web.client.js - Gbtc7// gsf.web.client.js - Gbtc
3//8//
4// Copyright © 2016, Grid Protection Alliance. All Rights Reserved.9// Copyright © 2016, Grid Protection Alliance. All Rights Reserved.
Source/Libraries/GSF.Web/Model/Scripts/gsf.web.knockout.js+6−1
@@ -1,4 +1,9 @@
1//******************************************************************************************************1/******************************************************************************************************\
2 !WARNING: The location for this resource has been moved!
3 New resource path is "/@GSF/Web/Shared/Scripts/", please update scripts to use this new location
4\******************************************************************************************************/
5
6//******************************************************************************************************
2// gsf.web.knockout.js - Gbtc7// gsf.web.knockout.js - Gbtc
3//8//
4// Copyright © 2016, Grid Protection Alliance. All Rights Reserved.9// Copyright © 2016, Grid Protection Alliance. All Rights Reserved.
Source/Libraries/GSF.Web/Model/Scripts/gsf.web.primeui.js+6−1
@@ -1,4 +1,9 @@
1//******************************************************************************************************1/******************************************************************************************************\
2 !WARNING: The location for this resource has been moved!
3 New resource path is "/@GSF/Web/Shared/Scripts/", please update scripts to use this new location
4\******************************************************************************************************/
5
6//******************************************************************************************************
2// gsf.web.primeui.js - Gbtc7// gsf.web.primeui.js - Gbtc
3//8//
4// Copyright © 2016, Grid Protection Alliance. All Rights Reserved.9// Copyright © 2016, Grid Protection Alliance. All Rights Reserved.
Source/Libraries/GSF.Web/Model/Scripts/js.cookie.js+6−1
@@ -1,4 +1,9 @@
1/*!1/******************************************************************************************************\
2 !WARNING: The location for this resource has been moved!
3 New resource path is "/@GSF/Web/Shared/Scripts/", please update scripts to use this new location
4\******************************************************************************************************/
5
6/*!
2 * JavaScript Cookie v2.1.07 * JavaScript Cookie v2.1.0
3 * https://github.com/js-cookie/js-cookie8 * https://github.com/js-cookie/js-cookie
4 *9 *
Source/Libraries/GSF.Web/Model/Scripts/ko-reactor.js+5−0
@@ -1,3 +1,8 @@
1/******************************************************************************************************\
2 !WARNING: The location for this resource has been moved!
3 New resource path is "/@GSF/Web/Shared/Scripts/", please update scripts to use this new location
4\******************************************************************************************************/
5
1// Deep observer plugin for Knockout http://knockoutjs.com/6// Deep observer plugin for Knockout http://knockoutjs.com/
2// (c) Ziad Jeeroburkhan7// (c) Ziad Jeeroburkhan
3// License: MIT (http://www.opensource.org/licenses/mit-license.php)8// License: MIT (http://www.opensource.org/licenses/mit-license.php)
Source/Libraries/GSF.Web/Model/Scripts/ko-reactor.min.js+5−0
@@ -1 +1,6 @@
1/******************************************************************************************************\
2 !WARNING: The location for this resource has been moved!
3 New resource path is "/@GSF/Web/Shared/Scripts/", please update scripts to use this new location
4\******************************************************************************************************/
5
1ko.subscribable.fn.watch=function(a,b,c,d){var e=typeof a;return"boolean"===e||"undefined"===e?ko.watch(this,{enabled:a!==!1}):"function"!==e||ko.isSubscribable(a)?ko.watch(a,b,c,d||this):ko.watch(this,b||{},a,d||this),this},ko.watch=function(a,b,c,d){function e(f,g,h,i,j){if(f&&0!==b.depth&&(-1===b.depth||h.length<(b.depth||1))){if(b.watchedOnly&&!f.watchable&&f!=a)return;if((b.enabled===!1||b.enabled===!0)&&(f.watchable=b.enabled),f.watchable===!1)return;if(b.seal===!0&&(f.watchable=!1),f===g||ko.utils.arrayIndexOf(h,f)>-1)return;var k=[].concat(h,g&&g!==a?g:[]);if(b.hide&&("object"==typeof b.hide?b.hide===f:ko.utils.arrayIndexOf(b.hide,f)>-1))return;if(ko.isSubscribable(f)){if(c&&(i===!0||!b.beforeWatch||b.beforeWatch.call(d,k,f)!==!1)){if("function"==typeof f.pop)return f.subscribe(function(a){ko.utils.arrayForEach(a,function(a){var b=c.call(d,k,f,a);void 0!==b&&d(b),a.moved||setTimeout(function(){e(a.value,j?null:f,k,"deleted"===a.status)},0)})},void 0,"arrayChange"),e(f(),j?null:f,k,i,!0),!0;if(i===!0){var l=f.H||f._subscriptions;if(l){if(l.change)for(var m=l.change.length-1;m>=0;m--)l.change[m]._watcher===d&&l.change[m].dispose();if(l.beforeChange&&b.keepOldValues>0)for(var m=l.beforeChange.length-1;m>=0;m--)l.beforeChange[m]._watcher===d&&l.beforeChange[m].dispose()}e(f(),j?null:f,k,!0,!0)}else{if(b.enabled===!0&&f.watchable===!1)return;(b.keepOldValues>0||b.mutable)&&(f.subscribe(function(a){if(b.keepOldValues>0){var c=f.oldValues?f.oldValues:f.oldValues=[];for(c.unshift(a);c.length>b.keepOldValues;)c.pop()}b.mutable&&"object"==typeof a&&e(a,j?null:f,k,!0,!0)},null,"beforeChange")._watcher=d),f.subscribe(function(){if(f.watchable!==!1){var a=c.call(d,k,f);void 0!==a&&d(a),b.mutable&&"object"==typeof f()&&e(f(),j?null:f,k)}},null,"change")._watcher=d,b.hideWrappedValues!==!0&&e(f(),j?null:f,k)}}}else switch(Object.prototype.toString.call(f)){case"[object Object]":return ko.utils.objectForEach(f,function(a,c){if(b.wrap){var g=Object.prototype.toString.call(c);"[object Function]"!==g&&"[object Object]"!==g&&(b.beforeWrap&&b.beforeWrap.call(d,k,f,c)===!1||(c=f[a]="[object Array]"===g?ko.observableArray(c):ko.observable(c)))}var h=e(c,j?null:f,k,i);h&&b.tagParentsWithName===!0&&!c._fieldName&&(c._fieldName=a)}),!0;case"[object Array]":if(b.hideArrays!==!0)for(var m=0;m<f.length;m++)e(f[m],j?null:f,k,i);return!0}}}return"function"==typeof b&&(d=d||c,c=b,b={}),d=d||this,"function"!=typeof a||ko.isSubscribable(a)?void e(a,null,[]):ko.computed(a,c,b)};window.foo = "1.3.0";6ko.subscribable.fn.watch=function(a,b,c,d){var e=typeof a;return"boolean"===e||"undefined"===e?ko.watch(this,{enabled:a!==!1}):"function"!==e||ko.isSubscribable(a)?ko.watch(a,b,c,d||this):ko.watch(this,b||{},a,d||this),this},ko.watch=function(a,b,c,d){function e(f,g,h,i,j){if(f&&0!==b.depth&&(-1===b.depth||h.length<(b.depth||1))){if(b.watchedOnly&&!f.watchable&&f!=a)return;if((b.enabled===!1||b.enabled===!0)&&(f.watchable=b.enabled),f.watchable===!1)return;if(b.seal===!0&&(f.watchable=!1),f===g||ko.utils.arrayIndexOf(h,f)>-1)return;var k=[].concat(h,g&&g!==a?g:[]);if(b.hide&&("object"==typeof b.hide?b.hide===f:ko.utils.arrayIndexOf(b.hide,f)>-1))return;if(ko.isSubscribable(f)){if(c&&(i===!0||!b.beforeWatch||b.beforeWatch.call(d,k,f)!==!1)){if("function"==typeof f.pop)return f.subscribe(function(a){ko.utils.arrayForEach(a,function(a){var b=c.call(d,k,f,a);void 0!==b&&d(b),a.moved||setTimeout(function(){e(a.value,j?null:f,k,"deleted"===a.status)},0)})},void 0,"arrayChange"),e(f(),j?null:f,k,i,!0),!0;if(i===!0){var l=f.H||f._subscriptions;if(l){if(l.change)for(var m=l.change.length-1;m>=0;m--)l.change[m]._watcher===d&&l.change[m].dispose();if(l.beforeChange&&b.keepOldValues>0)for(var m=l.beforeChange.length-1;m>=0;m--)l.beforeChange[m]._watcher===d&&l.beforeChange[m].dispose()}e(f(),j?null:f,k,!0,!0)}else{if(b.enabled===!0&&f.watchable===!1)return;(b.keepOldValues>0||b.mutable)&&(f.subscribe(function(a){if(b.keepOldValues>0){var c=f.oldValues?f.oldValues:f.oldValues=[];for(c.unshift(a);c.length>b.keepOldValues;)c.pop()}b.mutable&&"object"==typeof a&&e(a,j?null:f,k,!0,!0)},null,"beforeChange")._watcher=d),f.subscribe(function(){if(f.watchable!==!1){var a=c.call(d,k,f);void 0!==a&&d(a),b.mutable&&"object"==typeof f()&&e(f(),j?null:f,k)}},null,"change")._watcher=d,b.hideWrappedValues!==!0&&e(f(),j?null:f,k)}}}else switch(Object.prototype.toString.call(f)){case"[object Object]":return ko.utils.objectForEach(f,function(a,c){if(b.wrap){var g=Object.prototype.toString.call(c);"[object Function]"!==g&&"[object Object]"!==g&&(b.beforeWrap&&b.beforeWrap.call(d,k,f,c)===!1||(c=f[a]="[object Array]"===g?ko.observableArray(c):ko.observable(c)))}var h=e(c,j?null:f,k,i);h&&b.tagParentsWithName===!0&&!c._fieldName&&(c._fieldName=a)}),!0;case"[object Array]":if(b.hideArrays!==!0)for(var m=0;m<f.length;m++)e(f[m],j?null:f,k,i);return!0}}}return"function"==typeof b&&(d=d||c,c=b,b={}),d=d||this,"function"!=typeof a||ko.isSubscribable(a)?void e(a,null,[]):ko.computed(a,c,b)};window.foo = "1.3.0";
Source/Libraries/GSF.Web/Model/Scripts/ko.observableDictionary.js+6−1
@@ -1,4 +1,9 @@
1// Knockout Observable Dictionary1/******************************************************************************************************\
2 !WARNING: The location for this resource has been moved!
3 New resource path is "/@GSF/Web/Shared/Scripts/", please update scripts to use this new location
4\******************************************************************************************************/
5
6// Knockout Observable Dictionary
2// (c) James Foster7// (c) James Foster
3// License: MIT (http://www.opensource.org/licenses/mit-license.php)8// License: MIT (http://www.opensource.org/licenses/mit-license.php)
4 9
Source/Libraries/GSF.Web/Model/Scripts/pagedViewModel.js+16−0
@@ -25,6 +25,22 @@ · 24 unmodified lines
25// Paged view model base class scripts25// Paged view model base class scripts
26"use strict";26"use strict";
27 27
28if (typeof jQuery === "undefined") {
29 throw new Error("pageViewModel script requires jQuery - make sure jquery.js is loaded first");
30}
31
32if (typeof Cookies.get === "undefined") {
33 throw new Error("pageViewModel script requires js.cookie - make sure js.cookie.js is loaded first");
34}
35
36if (typeof ko === "undefined") {
37 throw new Error("pageViewModel script requires knockout - make sure knockout.js is loaded first");
38}
39
40if (typeof Dictionary === "undefined") {
41 throw new Error("pageViewModel script requires gsf.web.client - make sure gsf.web.client.js is loaded first");
42}
43
28var RecordMode = {44var RecordMode = {
29 View: 0,45 View: 0,
30 Edit: 1,46 Edit: 1,
Source/Libraries/GSF.Web/Model/Views/PagedViewModel.cshtml+18−14
@@ -42,6 +42,12 @@ · 41 unmodified lines
42 ViewBag.PageControlScripts = new StringBuilder();42 ViewBag.PageControlScripts = new StringBuilder();
43 }43 }
44 44
45 bool includeScripts = false;
46
47 if (ViewBag.IncludeScripts != null) {
48 includeScripts = (bool)ViewBag.IncludeScripts;
49 }
50
45 // Setup page control scripts51 // Setup page control scripts
46 StringBuilder pageControlScripts = ViewBag.PageControlScripts;52 StringBuilder pageControlScripts = ViewBag.PageControlScripts;
47 53
@@ -53,22 +59,20 @@ · 5 unmodified lines
53 ViewBag.RecordsTitle ?? "Records"59 ViewBag.RecordsTitle ?? "Records"
54 );60 );
55 61
56 pageControlScripts.Append(string.Format(@"62 if (includeScripts) {
57 <script src=""{0}""></script>63 pageControlScripts.Append(@"
58 <script src=""{1}""></script>64 <script src=""/@GSF/Web/Shared/Scripts/knockout.js""></script>
59 <script src=""{2}""></script>65 <script src=""/@GSF/Web/Shared/Scripts/knockout.mapping.js""></script>
60 ",66 <script src=""/@GSF/Web/Shared/Scripts/knockout.validation.js""></script>
61 Url.Content("~/Scripts/knockout-3.4.0.js"),67 <script src=""/@GSF/Web/Shared/Scripts/knockout.reactor.min.js""></script>
62 Url.Content("~/Scripts/knockout.mapping-latest.js"),68 <script src=""/@GSF/Web/Shared/Scripts/knockout.observableDictionary.js""></script>
63 Url.Content("~/Scripts/knockout.validation.min.js"))69 <script src=""/@GSF/Web/Shared/Scripts/js.cookie.js""></script>
64 );70 <script src=""/@GSF/Web/Shared/Scripts/gsf.web.knockout.js""></script>
71 <script src=""/@GSF/Web/Model/Scripts/pagedViewModel.js""></script>
72 ");
73 }
65 74
66 pageControlScripts.Append(@"75 pageControlScripts.Append(@"
67 <script src=""/@GSF/Web/Model/Scripts/ko-reactor.min.js""></script>
68 <script src=""/@GSF/Web/Model/Scripts/ko.observableDictionary.js""></script>
69 <script src=""/@GSF/Web/Model/Scripts/js.cookie.js""></script>
70 <script src=""/@GSF/Web/Model/Scripts/gsf.web.knockout.js""></script>
71 <script src=""/@GSF/Web/Model/Scripts/pagedViewModel.js""></script>
72 <script>76 <script>
73 $(function() {77 $(function() {
74 $(""#clearSortOrder"").click(function() {78 $(""#clearSortOrder"").click(function() {
Source/Libraries/GSF.Web/Shared/Resources.cs+26−21
@@ -28,30 +28,35 @@ namespace GSF.Web.Shared · 27 unmodified lines
28 /// </summary>28 /// </summary>
29 public static class Resources29 public static class Resources
30 {30 {
31 /// <summary>
32 /// Defines embedded resource root path.
33 /// </summary>
34 public const string Root = "/@GSF/Web";
35
31 /// <summary>36 /// <summary>
32 /// Gets common set of icon resources that can be included in web page header.37 /// Gets common set of icon resources that can be included in web page header.
33 /// </summary>38 /// </summary>
34 public static readonly string HeaderIcons = @"39 public static readonly string HeaderIcons = $@"
35 <link rel=""apple-touch-icon-precomposed"" sizes=""57x57"" href=""/@GSF/Web/Shared/Images/Icons/apple-touch-icon-57x57.png"" />40 <link rel=""apple-touch-icon-precomposed"" sizes=""57x57"" href=""{Root}/Shared/Images/Icons/apple-touch-icon-57x57.png"" />
36 <link rel=""apple-touch-icon-precomposed"" sizes=""114x114"" href=""/@GSF/Web/Shared/Images/Icons/apple-touch-icon-114x114.png"" />41 <link rel=""apple-touch-icon-precomposed"" sizes=""114x114"" href=""{Root}/Shared/Images/Icons/apple-touch-icon-114x114.png"" />
37 <link rel=""apple-touch-icon-precomposed"" sizes=""72x72"" href=""/@GSF/Web/Shared/Images/Icons/apple-touch-icon-72x72.png"" />42 <link rel=""apple-touch-icon-precomposed"" sizes=""72x72"" href=""{Root}/Shared/Images/Icons/apple-touch-icon-72x72.png"" />
38 <link rel=""apple-touch-icon-precomposed"" sizes=""144x144"" href=""/@GSF/Web/Shared/Images/Icons/apple-touch-icon-144x144.png"" />43 <link rel=""apple-touch-icon-precomposed"" sizes=""144x144"" href=""{Root}/Shared/Images/Icons/apple-touch-icon-144x144.png"" />
39 <link rel=""apple-touch-icon-precomposed"" sizes=""60x60"" href=""/@GSF/Web/Shared/Images/Icons/apple-touch-icon-60x60.png"" />44 <link rel=""apple-touch-icon-precomposed"" sizes=""60x60"" href=""{Root}/Shared/Images/Icons/apple-touch-icon-60x60.png"" />
40 <link rel=""apple-touch-icon-precomposed"" sizes=""120x120"" href=""/@GSF/Web/Shared/Images/Icons/apple-touch-icon-120x120.png"" />45 <link rel=""apple-touch-icon-precomposed"" sizes=""120x120"" href=""{Root}/Shared/Images/Icons/apple-touch-icon-120x120.png"" />
41 <link rel=""apple-touch-icon-precomposed"" sizes=""76x76"" href=""/@GSF/Web/Shared/Images/Icons/apple-touch-icon-76x76.png"" />46 <link rel=""apple-touch-icon-precomposed"" sizes=""76x76"" href=""{Root}/Shared/Images/Icons/apple-touch-icon-76x76.png"" />
42 <link rel=""apple-touch-icon-precomposed"" sizes=""152x152"" href=""/@GSF/Web/Shared/Images/Icons/apple-touch-icon-152x152.png"" />47 <link rel=""apple-touch-icon-precomposed"" sizes=""152x152"" href=""{Root}/Shared/Images/Icons/apple-touch-icon-152x152.png"" />
43 <link rel=""icon"" type=""image/png"" href=""/@GSF/Web/Shared/Images/Icons/favicon-196x196.png"" sizes=""196x196"" />48 <link rel=""icon"" type=""image/png"" href=""{Root}/Shared/Images/Icons/favicon-196x196.png"" sizes=""196x196"" />
44 <link rel=""icon"" type=""image/png"" href=""/@GSF/Web/Shared/Images/Icons/favicon-96x96.png"" sizes=""96x96"" />49 <link rel=""icon"" type=""image/png"" href=""{Root}/Shared/Images/Icons/favicon-96x96.png"" sizes=""96x96"" />
45 <link rel=""icon"" type=""image/png"" href=""/@GSF/Web/Shared/Images/Icons/favicon-32x32.png"" sizes=""32x32"" />50 <link rel=""icon"" type=""image/png"" href=""{Root}/Shared/Images/Icons/favicon-32x32.png"" sizes=""32x32"" />
46 <link rel=""icon"" type=""image/png"" href=""/@GSF/Web/Shared/Images/Icons/favicon-16x16.png"" sizes=""16x16"" />51 <link rel=""icon"" type=""image/png"" href=""{Root}/Shared/Images/Icons/favicon-16x16.png"" sizes=""16x16"" />
47 <link rel=""icon"" type=""image/png"" href=""/@GSF/Web/Shared/Images/Icons/favicon-128.png"" sizes=""128x128"" />52 <link rel=""icon"" type=""image/png"" href=""{Root}/Shared/Images/Icons/favicon-128.png"" sizes=""128x128"" />
48 <link rel=""shortcut icon"" href=""/@GSF/Web/Shared/Images/Icons/favicon.ico"" />53 <link rel=""shortcut icon"" href=""{Root}/Shared/Images/Icons/favicon.ico"" />
49 <meta name=""msapplication-TileColor"" content=""#AABBAA"" />54 <meta name=""msapplication-TileColor"" content=""#AABBAA"" />
50 <meta name=""msapplication-TileImage"" content=""/@GSF/Web/Shared/Images/Icons/mstile-144x144.png"" />55 <meta name=""msapplication-TileImage"" content=""{Root}/Shared/Images/Icons/mstile-144x144.png"" />
51 <meta name=""msapplication-square70x70logo"" content=""/@GSF/Web/Shared/Images/Icons/mstile-70x70.png"" />56 <meta name=""msapplication-square70x70logo"" content=""{Root}/Shared/Images/Icons/mstile-70x70.png"" />
52 <meta name=""msapplication-square150x150logo"" content=""/@GSF/Web/Shared/Images/Icons/mstile-150x150.png"" />57 <meta name=""msapplication-square150x150logo"" content=""{Root}/Shared/Images/Icons/mstile-150x150.png"" />
53 <meta name=""msapplication-wide310x150logo"" content=""/@GSF/Web/Shared/Images/Icons/mstile-310x150.png"" />58 <meta name=""msapplication-wide310x150logo"" content=""{Root}/Shared/Images/Icons/mstile-310x150.png"" />
54 <meta name=""msapplication-square310x310logo"" content=""/@GSF/Web/Shared/Images/Icons/mstile-310x310.png""/>59 <meta name=""msapplication-square310x310logo"" content=""{Root}/Shared/Images/Icons/mstile-310x310.png""/>
55 ".FixForwardSpacing();60 ".FixForwardSpacing(0);
56 }61 }
57}62}
Source/Libraries/GSF.Web/Shared/Scripts/gsf.web.client.js+895−0
@@ -0,0 +1,895 @@
1//******************************************************************************************************
2// gsf.web.client.js - Gbtc
3//
4// Copyright © 2016, Grid Protection Alliance. All Rights Reserved.
5//
6// Licensed to the Grid Protection Alliance (GPA) under one or more contributor license agreements. See
7// the NOTICE file distributed with this work for additional information regarding copyright ownership.
8// The GPA licenses this file to you under the MIT License (MIT), the "License"; you may
9// not use this file except in compliance with the License. You may obtain a copy of the License at:
10//
11// http://opensource.org/licenses/MIT
12//
13// Unless agreed to in writing, the subject software distributed under the License is distributed on an
14// "AS-IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. Refer to the
15// License for the specific language governing permissions and limitations.
16//
17// Code Modification History:
18// ----------------------------------------------------------------------------------------------------
19// 01/26/2016 - J. Ritchie Carroll
20// Generated original version of source code.
21//
22//******************************************************************************************************
23// ReSharper disable NativeTypePrototypeExtending
24// ReSharper disable UndeclaredGlobalVariableUsing
25
26// Grid Solutions Framework Core Web Client Script Functions
27"use strict";
28
29if (typeof jQuery === "undefined") {
30 throw new Error("gsf.web.client script requires jQuery - make sure jquery.js is loaded first");
31}
32
33const isIE = detectIE();
34var textMetrics;
35
36$(function() {
37 // Create a canvas object that will be used for text metrics calculations
38 $("<canvas id=\"textMetricsCanvas\" height=\"1px\" width=\"1px\" style=\"visibility: hidden\"></canvas>").appendTo("body");
39
40 // Get text metrics canvas context
41 textMetrics = document.getElementById("textMetricsCanvas").getContext("2d");
42});
43
44// Miscellaneous functions
45function notNull(value, nonNullValue) {
46 return value || (nonNullValue || "");
47}
48
49function detectIE() {
50 const ua = window.navigator.userAgent;
51 const msie = ua.indexOf("MSIE ");
52
53 if (msie > 0) {
54 // IE 10 or older => return version number
55 return parseInt(ua.substring(msie + 5, ua.indexOf(".", msie)), 10);
56 }
57
58 const trident = ua.indexOf("Trident/");
59
60 if (trident > 0) {
61 // IE 11 => return version number
62 const rv = ua.indexOf("rv:");
63 return parseInt(ua.substring(rv + 3, ua.indexOf(".", rv)), 10);
64 }
65
66 const edge = ua.indexOf("Edge/");
67
68 if (edge > 0) {
69 // Edge (IE 12+) => return version number
70 return parseInt(ua.substring(edge + 5, ua.indexOf(".", edge)), 10);
71 }
72
73 // Other browser
74 return false;
75}
76
77function isNumber(val) {
78 return !isNaN(parseFloat(val)) && isFinite(val);
79}
80
81function toHex(val, leftPadding) {
82 if (leftPadding === undefined)
83 leftPadding = 4;
84
85 const isNegative = val < 0;
86 return (isNegative ? "-" : "") + "0x" + Math.abs(val).toString(16).padLeft(leftPadding, "0").toUpperCase();
87}
88
89function isBool(val) {
90 if (typeof value === "boolean")
91 return true;
92
93 const lval = val.toString().toLowerCase();
94 return lval === "true" || lval === "false";
95}
96
97function getBool(val) {
98 const num = +val;
99 return !isNaN(num) ? !!num : !!String(val).toLowerCase().replace(false, "");
100}
101
102function getParameterByName(name, url) {
103 if (!url)
104 url = window.location.href;
105
106 name = name.replace(/[\[\]]/g, "\\$&");
107
108 const regex = new RegExp("[?&]" + name + "(=([^&#]*)|&|#|$)"), results = regex.exec(url);
109
110 if (!results)
111 return null;
112
113 if (!results[2])
114 return "";
115
116 return decodeURIComponent(results[2].replace(/\+/g, " "));
117}
118
119function clearCachedCredentials(securedUrl, successCallback) {
120 if (isIE) {
121 document.execCommand("ClearAuthenticationCache", "false");
122
123 if (successCallback)
124 successCallback(true);
125 } else {
126 const xhr = new XMLHttpRequest();
127
128 xhr.open("GET", securedUrl, true);
129
130 xhr.setRequestHeader("Content-type", "application/json");
131
132 // Send in an invalid set of credentials, i.e., base64 encoded _logout:_logout:
133 xhr.setRequestHeader("Authorization", "Basic X2xvZ291dDpfbG9nb3V0");
134
135 if (successCallback)
136 xhr.onreadystatechange = function() {
137 if (this.readyState === XMLHttpRequest.DONE)
138 successCallback(this.status === 401);
139 };
140
141 xhr.send();
142 }
143}
144
145// Number functions
146Number.prototype.truncate = function() {
147 if (typeof Math.trunc != "function")
148 return parseInt(this.toString());
149
150 return Math.trunc(this);
151}
152
153Number.prototype.padLeft = function(totalWidth, paddingChar) {
154 return this.truncate().toString().padLeft(totalWidth, paddingChar || "0");
155}
156
157Number.prototype.padRight = function(totalWidth, paddingChar) {
158 return this.truncate().toString().padRight(totalWidth, paddingChar || "0");
159}
160
161// Array functions
162
163// Combines a dictionary of key-value pairs into a string. Values will be escaped within startValueDelimiter and endValueDelimiter
164// to contain nested key/value pair expressions like the following: "normalKVP=-1; nestedKVP={p1=true; p2=0.001}" when either the
165// parameterDelimiter or the keyValueDelimiter are detected in the value of the key/value pair.
166function joinKeyValuePairs (source, parameterDelimiter, keyValueDelimiter, startValueDelimiter, endValueDelimiter) {
167 if (!parameterDelimiter)
168 parameterDelimiter = ";";
169
170 if (!keyValueDelimiter)
171 keyValueDelimiter = "=";
172
173 if (!startValueDelimiter)
174 startValueDelimiter = "{";
175
176 if (!endValueDelimiter)
177 endValueDelimiter = "}";
178
179 const values = [];
180
181 for (let key in source) {
182 if (source.hasOwnProperty(key)) {
183 let value = source[key];
184
185 if (isBool(value))
186 value = value.toString().toLowerCase();
187 else
188 value = value ? value.toString() : "";
189
190 if (value.indexOf(parameterDelimiter) >= 0 || value.indexOf(keyValueDelimiter) >= 0)
191 value = startValueDelimiter + value + endValueDelimiter;
192
193 values.push(key + keyValueDelimiter + value);
194 }
195 }
196
197 return values.join(parameterDelimiter + " ");
198}
199
200Array.prototype.joinKeyValuePairs = function(parameterDelimiter, keyValueDelimiter, startValueDelimiter, endValueDelimiter) {
201 return joinKeyValuePairs(this, parameterDelimiter, keyValueDelimiter, startValueDelimiter, endValueDelimiter);
202};
203
204Array.prototype.forEachWithDelay = function(iterationCallback, timeout, completedCallback, thisArg) {
205 var index = 0,
206 count = this.length,
207 self = this,
208 next = function() {
209 if (self[index])
210 iterationCallback.call(thisArg || self, self[index], index, self);
211
212 index++;
213
214 if (index < count)
215 setTimeout(next, timeout);
216 else
217 completedCallback.call(thisArg || self, self);
218 };
219
220 next();
221};
222
223if (!Array.prototype.any) {
224
225 if (Array.prototype.some) {
226 Array.prototype.any = Array.prototype.some;
227 }
228 else {
229 Array.prototype.any = function(callback, thisArg) {
230 var args;
231
232 if (this == null)
233 throw new TypeError("this is null or not defined");
234
235 const array = Object(this);
236 const length = array.length >>> 0; // Convert array length to positive integer
237
238 if (typeof callback !== "function")
239 throw new TypeError();
240
241 if (arguments.length > 1)
242 args = thisArg;
243 else
244 args = undefined;
245
246 var index = 0;
247
248 while (index < length) {
249 if (index in array) {
250 const element = array[index];
251
252 if (callback.call(args, element, index, array))
253 return true;
254 }
255
256 index++;
257 }
258
259 return false;
260 }
261 }
262}
263
264// Represents a dictionary style class with case-insensitive keys
265function Dictionary(source) {
266 const self = this;
267
268 if (!source)
269 source = [];
270
271 self._keys = [];
272 self._values = [];
273
274 self.count = function() {
275 var size = 0;
276
277 for (let property in self._values) {
278 if (self._values.hasOwnProperty(property))
279 size++;
280 }
281
282 return size;
283 }
284
285 self.keys = function() {
286 const keys = [];
287
288 for (let property in self._keys) {
289 if (self._keys.hasOwnProperty(property))
290 keys.push(self._keys[property]);
291 }
292
293 return keys;
294 }
295
296 self.values = function() {
297 const values = [];
298
299 for (let property in self._values) {
300 if (self._keys.hasOwnProperty(property))
301 values.push(self._values[property]);
302 }
303
304 return values;
305 }
306
307 self.get = function(key) {
308 return self._values[String(key).toLowerCase()];
309 }
310
311 self.set = function(key, value) {
312 const lkey = String(key).toLowerCase();
313
314 if (!self._keys[lkey] || self._keys[lkey].toLowerCase() !== key)
315 self._keys[lkey] = key;
316
317 if (isBool(value))
318 self._values[lkey] = getBool(value);
319 else
320 self._values[lkey] = value;
321 }
322
323 self.remove = function(key) {
324 const lkey = String(key).toLowerCase();
325 delete self._keys[lkey];
326 delete self._values[lkey];
327 }
328
329 self.containsKey = function(key) {
330 const lkey = String(key).toLowerCase();
331
332 for (let property in self._values) {
333 if (self._values.hasOwnProperty(property) && property === lkey)
334 return true;
335 }
336
337 return false;
338 }
339
340 self.containsValue = function(value) {
341 for (let property in self._values) {
342 if (self._values.hasOwnProperty(property) && self._values[property] === value)
343 return true;
344 }
345
346 return false;
347 }
348
349 self.clear = function() {
350 self._keys = [];
351 self._values = [];
352 }
353
354 self.joinKeyValuePairs = function(parameterDelimiter, keyValueDelimiter, startValueDelimiter, endValueDelimiter) {
355 const keyValuePairs = [];
356
357 for (let property in self._values) {
358 if (self._values.hasOwnProperty(property))
359 keyValuePairs[self._keys[property]] = self._values[property];
360 }
361
362 return keyValuePairs.joinKeyValuePairs(parameterDelimiter, keyValueDelimiter, startValueDelimiter, endValueDelimiter);
363 }
364
365 self.pushAll = function(source) {
366 for (let property in source)
367 if (source.hasOwnProperty(property))
368 self.set(property, source[property]);
369 }
370
371 self.toObservableDictionary = function(useLowerKeys) {
372 // See ko.observableDictionary.js
373 const observableDictionary = new ko.observableDictionary();
374
375 for (let property in self._values) {
376 if (self._values.hasOwnProperty(property))
377 observableDictionary.push(useLowerKeys ? property : self._keys[property], self._values[property]);
378 }
379
380 return observableDictionary;
381 }
382
383 self.updateObservableDictionary = function(observableDictionary, useLowerKeys) {
384 for (let property in self._values) {
385 if (self._values.hasOwnProperty(property))
386 observableDictionary.set(useLowerKeys ? property : self._keys[property], self._values[property]);
387 }
388 }
389
390 // Construction
391 if (source instanceof Dictionary) {
392 for (let property in source._values)
393 if (source._values.hasOwnProperty(property))
394 self.set(source._keys[property], source._values[property]);
395 }
396 else {
397 for (let property in source) {
398 if (source.hasOwnProperty(property))
399 self.set(property, source[property]);
400 }
401 }
402}
403
404Dictionary.fromObservableDictionary = function(observableDictionary) {
405 const dictionary = new Dictionary();
406 dictionary.pushAll(observableDictionary.toJSON());
407 return dictionary;
408}
409
410// String functions
411function isEmpty(str) {
412 return !str || String(str).length === 0;
413}
414
415String.prototype.trimLeft = String.prototype.trimLeft || function () {
416 return this.replace(/^\s+/,"");
417}
418
419String.prototype.truncate = function(limit) {
420 const text = this.trim();
421
422 if (text.length > limit)
423 return text.substr(0, limit - 3) + "...";
424
425 return text;
426}
427
428String.prototype.replaceAll = function(findText, replaceWith, ignoreCase) {
429 return this.replace(
430 new RegExp(findText.replace(/([\/\,\!\\\^\$\{\}\[\]\(\)\.\*\+\?\|\<\>\-\&])/g, "\\$&"), (ignoreCase ? "gi" : "g")),
431 (typeof replaceWith == "string") ? replaceWith.replace(/\$/g, "$$$$") : replaceWith);
432}
433
434if (!String.prototype.endsWith) {
435 String.prototype.endsWith = function(searchString, position) {
436 const subjectString = this.toString() ;
437
438 if (typeof position !== 'number' || !isFinite(position) || Math.floor(position) !== position || position > subjectString.length)
439 position = subjectString.length;
440
441 position -= searchString.length;
442
443 const lastIndex = subjectString.lastIndexOf(searchString, position);
444
445 return lastIndex !== -1 && lastIndex === position;
446 };
447}
448
449if (!String.format) {
450 String.format = function(format) {
451 var text = format;
452
453 for (let i = 1; i < arguments.length; i++)
454 text = text.replaceAll("{" + (i - 1) + "}", arguments[i]);
455
456 return text;
457 };
458}
459
460String.prototype.padLeft = function(totalWidth, paddingChar) {
461 if (totalWidth > this.length)
462 return Array(totalWidth - this.length + 1).join(paddingChar || " ") + this;
463
464 return this;
465}
466
467String.prototype.padRight = function(totalWidth, paddingChar) {
468 if (totalWidth > this.length)
469 return this + Array(totalWidth - this.length + 1).join(paddingChar || " ");
470
471 return this;
472}
473
474String.prototype.countOccurrences = function(searchString) {
475 return (this.split(searchString).length - 1);
476}
477
478if (!String.prototype.startsWith) {
479 String.prototype.startsWith = function(searchString, position) {
480 position = position || 0;
481 return this.substr(position, searchString.length) === searchString;
482 }
483}
484
485String.prototype.regexEncode = function() {
486 return "\\u" + this.charCodeAt(0).toString(16).padLeft(4, "0");
487}
488
489// Returns a Dictionary of the parsed key/value pair expressions from a string. Parameter pairs are delimited by keyValueDelimiter
490// and multiple pairs separated by parameterDelimiter. Supports encapsulated nested expressions.
491String.prototype.parseKeyValuePairs = function(parameterDelimiter, keyValueDelimiter, startValueDelimiter, endValueDelimiter, ignoreDuplicateKeys) {
492 if (!parameterDelimiter)
493 parameterDelimiter = ";";
494
495 if (!keyValueDelimiter)
496 keyValueDelimiter = "=";
497
498 if (!startValueDelimiter)
499 startValueDelimiter = "{";
500
501 if (!endValueDelimiter)
502 endValueDelimiter = "}";
503
504 if (ignoreDuplicateKeys === undefined)
505 ignoreDuplicateKeys = true;
506
507 if (parameterDelimiter === keyValueDelimiter ||
508 parameterDelimiter === startValueDelimiter ||
509 parameterDelimiter === endValueDelimiter ||
510 keyValueDelimiter === startValueDelimiter ||
511 keyValueDelimiter === endValueDelimiter ||
512 startValueDelimiter === endValueDelimiter)
513 throw "All delimiters must be unique";
514
515 const escapedParameterDelimiter = parameterDelimiter.regexEncode();
516 const escapedKeyValueDelimiter = keyValueDelimiter.regexEncode();
517 const escapedStartValueDelimiter = startValueDelimiter.regexEncode();
518 const escapedEndValueDelimiter = endValueDelimiter.regexEncode();
519 const backslashDelimiter = "\\".regexEncode();
520
521 var keyValuePairs = new Dictionary();
522 var escapedValue = [];
523 var valueEscaped = false;
524 var delimiterDepth = 0;
525
526 // Escape any parameter or key/value delimiters within tagged value sequences
527 // For example, the following string:
528 // "normalKVP=-1; nestedKVP={p1=true; p2=false}")
529 // would be encoded as:
530 // "normalKVP=-1; nestedKVP=p1\\u003dtrue\\u003b p2\\u003dfalse")
531 for (let i = 0; i < this.length; i++) {
532 var character = this[i];
533
534 if (character === startValueDelimiter) {
535 if (!valueEscaped) {
536 valueEscaped = true;
537 continue; // Don't add tag start delimiter to final value
538 }
539
540 // Handle nested delimiters
541 delimiterDepth++;
542 }
543
544 if (character === endValueDelimiter) {
545 if (valueEscaped) {
546 if (delimiterDepth > 0) {
547 // Handle nested delimiters
548 delimiterDepth--;
549 }
550 else {
551 valueEscaped = false;
552 continue; // Don't add tag stop delimiter to final value
553 }
554 }
555 else {
556 throw "Failed to parse key/value pairs: invalid delimiter mismatch. Encountered end value delimiter \"" + endValueDelimiter + "\" before start value delimiter \"" + startValueDelimiter + "\".";
557 }
558 }
559
560 if (valueEscaped) {
561 // Escape any delimiter characters inside nested key/value pair
562 if (character === parameterDelimiter)
563 escapedValue.push(escapedParameterDelimiter);
564 else if (character === keyValueDelimiter)
565 escapedValue.push(escapedKeyValueDelimiter);
566 else if (character === startValueDelimiter)
567 escapedValue.push(escapedStartValueDelimiter);
568 else if (character === endValueDelimiter)
569 escapedValue.push(escapedEndValueDelimiter);
570 else if (character === "\\")
571 escapedValue.push(backslashDelimiter);
572 else
573 escapedValue.push(character);
574 }
575 else {
576 if (character === "\\")
577 escapedValue.push(backslashDelimiter);
578 else
579 escapedValue.push(character);
580 }
581 }
582
583 if (delimiterDepth !== 0 || valueEscaped) {
584 // If value is still escaped, tagged expression was not terminated
585 if (valueEscaped)
586 delimiterDepth = 1;
587
588 throw "Failed to parse key/value pairs: invalid delimiter mismatch. Encountered more " +
589 (delimiterDepth > 0 ? "start value delimiters \"" + startValueDelimiter + "\"" : "end value delimiters \"" + endValueDelimiter + "\"") + " than " +
590 (delimiterDepth < 0 ? "start value delimiters \"" + startValueDelimiter + "\"" : "end value delimiters \"" + endValueDelimiter + "\"") + ".";
591 }
592
593 // Parse key/value pairs from escaped value
594 var pairs = escapedValue.join("").split(parameterDelimiter);
595
596 for (let i = 0; i < pairs.length; i++) {
597 // Separate key from value
598 var elements = pairs[i].split(keyValueDelimiter);
599
600 if (elements.length === 2) {
601 // Get key
602 var key = elements[0].trim();
603
604 // Get unescaped value
605 var unescapedValue = elements[1].trim().
606 replaceAll(escapedParameterDelimiter, parameterDelimiter).
607 replaceAll(escapedKeyValueDelimiter, keyValueDelimiter).
608 replaceAll(escapedStartValueDelimiter, startValueDelimiter).
609 replaceAll(escapedEndValueDelimiter, endValueDelimiter).
610 replaceAll(backslashDelimiter, "\\");
611
612 // Add key/value pair to dictionary
613 if (ignoreDuplicateKeys) {
614 // Add or replace key elements with unescaped value
615 keyValuePairs.set(key, unescapedValue);
616 }
617 else {
618 // Add key elements with unescaped value throwing an exception for encountered duplicate keys
619 if (keyValuePairs.containsKey(key))
620 throw "Failed to parse key/value pairs: duplicate key encountered. Key \"" + key + "\" is not unique within the string: \"" + this + "\"";
621
622 keyValuePairs.set(key, unescapedValue);
623 }
624 }
625 }
626
627 return keyValuePairs;
628}
629
630// Renders URLs and e-mail addresses as clickable links
631function renderHotLinks(sourceText, target) {
632 if (target === undefined)
633 target = "_blank";
634
635 sourceText = sourceText.toString();
636
637 var replacedText;
638
639 // URLs starting with http://, https://, or ftp://
640 const replacePattern1 = /(\b(https?|ftp):\/\/[-A-Z0-9+&@#\/%?=~_|!:,.;]*[-A-Z0-9+&@#\/%=~_|])/gim;
641 replacedText = sourceText.replace(replacePattern1, "<a href=\"$1\" target=\"" + target + "\">$1</a>");
642
643 // URLs starting with "www." without // before it
644 const replacePattern2 = /(^|[^\/])(www\.[\S]+(\b|$))/gim;
645 replacedText = replacedText.replace(replacePattern2, "$1<a href=\"http://$2\" target=\"" + target + "\">$2</a>");
646
647 // Change e-mail addresses to mailto: links
648 const replacePattern3 = /(([a-zA-Z0-9\-\_\.])+@[a-zA-Z\_]+?(\.[a-zA-Z]{2,6})+)/gim;
649 replacedText = replacedText.replace(replacePattern3, "<a href=\"mailto:$1\">$1</a>");
650
651 return replacedText;
652}
653
654// Date Functions
655Date.prototype.addDays = function(days) {
656 return new Date(this.setDate(this.getDate() + days));
657}
658
659Date.prototype.toUTC = function() {
660 this.setMinutes(this.getMinutes() - this.getTimezoneOffset());
661 return this;
662}
663
664Date.prototype.daysBetween = function(startDate) {
665 const millisecondsPerDay = 24 * 60 * 60 * 1000;
666 return (this.toUTC() - startDate.toUTC()) / millisecondsPerDay;
667}
668
669String.prototype.toDate = function() {
670 return new Date(Date.parse(this));
671};
672
673String.prototype.formatDate = function(format, utc) {
674 return formatDate(this.toDate(), format, utc);
675};
676
677Date.prototype.formatDate = function(format, utc) {
678 return formatDate(this, format, utc);
679};
680
681function formatDate(date, format, utc) {
682 if (typeof date === "string")
683 return formatDate(date.toDate(), format, utc);
684
685 if (format === undefined && DateTimeFormat != undefined)
686 format = DateTimeFormat;
687
688 if (utc === undefined)
689 utc = true;
690
691 var MMMM = ["\x00", "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"];
692 var MMM = ["\x01", "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
693 var dddd = ["\x02", "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"];
694 var ddd = ["\x03", "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
695
696 function ii(i, len) {
697 var ss = i + "";
698 len = len || 2;
699 while (ss.length < len) ss = "0" + ss;
700 return ss;
701 }
702
703 var y = utc ? date.getUTCFullYear() : date.getFullYear();
704 format = format.replace(/(^|[^\\])yyyy+/g, "$1" + y);
705 format = format.replace(/(^|[^\\])yy/g, "$1" + y.toString().substr(2, 2));
706 format = format.replace(/(^|[^\\])y/g, "$1" + y);
707
708 var M = (utc ? date.getUTCMonth() : date.getMonth()) + 1;
709 format = format.replace(/(^|[^\\])MMMM+/g, "$1" + MMMM[0]);
710 format = format.replace(/(^|[^\\])MMM/g, "$1" + MMM[0]);
711 format = format.replace(/(^|[^\\])MM/g, "$1" + ii(M));
712 format = format.replace(/(^|[^\\])M/g, "$1" + M);
713
714 var d = utc ? date.getUTCDate() : date.getDate();
715 format = format.replace(/(^|[^\\])dddd+/g, "$1" + dddd[0]);
716 format = format.replace(/(^|[^\\])ddd/g, "$1" + ddd[0]);
717 format = format.replace(/(^|[^\\])dd/g, "$1" + ii(d));
718 format = format.replace(/(^|[^\\])d/g, "$1" + d);
719
720 var H = utc ? date.getUTCHours() : date.getHours();
721 format = format.replace(/(^|[^\\])HH+/g, "$1" + ii(H));
722 format = format.replace(/(^|[^\\])H/g, "$1" + H);
723
724 var h = H > 12 ? H - 12 : H === 0 ? 12 : H;
725 format = format.replace(/(^|[^\\])hh+/g, "$1" + ii(h));
726 format = format.replace(/(^|[^\\])h/g, "$1" + h);
727
728 var m = utc ? date.getUTCMinutes() : date.getMinutes();
729 format = format.replace(/(^|[^\\])mm+/g, "$1" + ii(m));
730 format = format.replace(/(^|[^\\])m/g, "$1" + m);
731
732 var s = utc ? date.getUTCSeconds() : date.getSeconds();
733 format = format.replace(/(^|[^\\])ss+/g, "$1" + ii(s));
734 format = format.replace(/(^|[^\\])s/g, "$1" + s);
735
736 var f = utc ? date.getUTCMilliseconds() : date.getMilliseconds();
737 format = format.replace(/(^|[^\\])fff+/g, "$1" + ii(f, 3));
738 f = Math.round(f / 10);
739 format = format.replace(/(^|[^\\])ff/g, "$1" + ii(f));
740 f = Math.round(f / 10);
741 format = format.replace(/(^|[^\\])f/g, "$1" + f);
742
743 var T = H < 12 ? "AM" : "PM";
744 format = format.replace(/(^|[^\\])TT+/g, "$1" + T);
745 format = format.replace(/(^|[^\\])T/g, "$1" + T.charAt(0));
746
747 var t = T.toLowerCase();
748 format = format.replace(/(^|[^\\])tt+/g, "$1" + t);
749 format = format.replace(/(^|[^\\])t/g, "$1" + t.charAt(0));
750
751 var tz = -date.getTimezoneOffset();
752 var K = utc || !tz ? "Z" : tz > 0 ? "+" : "-";
753 if (!utc) {
754 tz = Math.abs(tz);
755 var tzHrs = Math.floor(tz / 60);
756 var tzMin = tz % 60;
757 K += ii(tzHrs) + ":" + ii(tzMin);
758 }
759
760 format = format.replace(/(^|[^\\])K/g, "$1" + K);
761 var day = (utc ? date.getUTCDay() : date.getDay()) + 1;
762
763 format = format.replace(new RegExp(dddd[0], "g"), dddd[day]);
764 format = format.replace(new RegExp(ddd[0], "g"), ddd[day]);
765
766 format = format.replace(new RegExp(MMMM[0], "g"), MMMM[M]);
767 format = format.replace(new RegExp(MMM[0], "g"), MMM[M]);
768
769 format = format.replace(/\\(.)/g, "$1");
770
771 return format;
772};
773
774// jQuery extensions
775$.fn.enable = function() {
776 return this.each(function() {
777 this.disabled = false;
778 });
779}
780
781$.fn.disable = function() {
782 return this.each(function() {
783 this.disabled = true;
784 });
785}
786
787$.fn.visible = function() {
788 return this.each(function() {
789 this.css("visibility", "visible");
790 });
791}
792
793$.fn.invisible = function() {
794 return this.each(function() {
795 this.css("visibility", "hidden");
796 });
797}
798
799$.fn.readCookie = function(name) {
800 this.val(Cookies.get(name));
801}
802
803$.fn.writeCookie = function(name, expiration) {
804 if (expiration) // Date or number of days
805 Cookies.set(name, this.val(), { expires: expiration });
806 else
807 Cookies.set(name, this.val());
808}
809
810$.fn.removeCookie = function(name) {
811 Cookies.remove(name);
812}
813
814$.fn.paddingHeight = function() {
815 return this.outerHeight(true) - this.height();
816}
817
818$.fn.paddingWidth = function() {
819 return this.outerWidth(true) - this.width();
820}
821
822// Call function once at start-up to auto-size drop-down to selected option contents
823$.fn.autoSizeSelect = function() {
824 return this.each(function() {
825 $(this).change(function() {
826 const arrowWidth = 30;
827 const $this = $(this);
828
829 // Create test element
830 const text = $this.find("option:selected").text();
831 const $test = $("<span>").html(text);
832
833 // Add to body, get width, and get out
834 $test.appendTo("body");
835 const width = $test.width();
836 $test.remove();
837
838 // Set select width
839 $this.width(width + arrowWidth);
840 }).change();
841 });
842}
843
844// Cell truncations should only be used with .table-cell-hard-wrap style
845$.fn.truncateToWidth = function(text, rows) {
846 if (isEmpty(text))
847 return "";
848
849 if (!rows)
850 rows = 1;
851
852 textMetrics.font = this.css("font");
853
854 var targetWidth = this.innerWidth();
855
856 if (rows > 1)
857 targetWidth *= ((isIE ? 0.45 : 0.75) * rows);
858
859 if (targetWidth > 30) {
860 let textWidth = textMetrics.measureText(text).width;
861 let limit = Math.min(text.length, Math.ceil(targetWidth / (textWidth / text.length)));
862
863 while (textWidth > targetWidth && limit > 3) {
864 limit--;
865 text = text.truncate(limit);
866 textWidth = textMetrics.measureText(text).width;
867 }
868 }
869 else {
870 text = text.charAt(0) + "...";
871 }
872
873 return text;
874}
875
876// The following target arrays of promises
877$.fn.whenAny = function() {
878 var finish = $.Deferred();
879
880 if (this.length === 0)
881 finish.resolve();
882 else
883 $.each(this, function(index, deferred) {
884 deferred.done(finish.resolve);
885 });
886
887 return finish.promise();
888}
889
890$.fn.whenAll = function() {
891 if (this.length > 0)
892 return $.when.apply($, this);
893
894 return $.Deferred().resolve().promise();
895}
Source/Libraries/GSF.Web/Shared/Scripts/gsf.web.knockout.js+167−0
@@ -0,0 +1,167 @@
1//******************************************************************************************************
2// gsf.web.knockout.js - Gbtc
3//
4// Copyright © 2016, Grid Protection Alliance. All Rights Reserved.
5//
6// Licensed to the Grid Protection Alliance (GPA) under one or more contributor license agreements. See
7// the NOTICE file distributed with this work for additional information regarding copyright ownership.
8// The GPA licenses this file to you under the MIT License (MIT), the "License"; you may not use this
9// file except in compliance with the License. You may obtain a copy of the License at:
10//
11// http://opensource.org/licenses/MIT
12//
13// Unless agreed to in writing, the subject software distributed under the License is distributed on an
14// "AS-IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. Refer to the
15// License for the specific language governing permissions and limitations.
16//
17// Code Modification History:
18// ----------------------------------------------------------------------------------------------------
19// 07/22/2016 - Ritchie Carroll
20// Generated original version of source code.
21//
22//******************************************************************************************************
23// ReSharper disable UndeclaredGlobalVariableUsing
24
25// Grid Solutions Framework Core Web Knockout Extension Script Functions
26"use strict";
27
28// Field functions
29function fieldHasValue(field, required) {
30 if (field == null)
31 return true;
32
33 if (required === undefined)
34 required = false;
35
36 var isValid = true;
37
38 if (required) {
39 if (typeof field == "function")
40 isValid = !isEmpty(field());
41 else
42 isValid = !isEmpty(field);
43 }
44
45 return isValid;
46}
47
48function fieldIsValid(field, required) {
49 var isValid = fieldHasValue(field, required);
50
51 if (isValid && typeof field.isValid == "function")
52 isValid = field.isValid();
53
54 return isValid;
55}
56
57// Custom knockout binding handers
58ko.bindingHandlers.numeric = {
59 init: function(element, valueAccessor, allBindings, viewModel, bindingContext) {
60 $(element).on("keydown", function(event) {
61 // Allow backspace, delete, tab, escape, and enter
62 if (event.keyCode === 46 || event.keyCode === 8 || event.keyCode === 9 || event.keyCode === 27 || event.keyCode === 13 ||
63 // Ctrl+A
64 (event.keyCode === 65 && event.ctrlKey) ||
65 // E + - . ,
66 (event.keyCode === 69 || event.keyCode === 107 || event.keyCode === 109 || event.keyCode === 110 || event.keyCode === 190 || event.keyCode === 188) ||
67 // home, end, left, right, up, down
68 (event.keyCode >= 35 && event.keyCode <= 40)) {
69 return;
70 }
71 else {
72 // If value is not a number, stop key-press
73 if (event.shiftKey || (event.keyCode < 48 || event.keyCode > 57) && (event.keyCode < 96 || event.keyCode > 105)) {
74 event.preventDefault();
75 }
76 }
77 });
78 }
79};
80
81ko.bindingHandlers.integer = {
82 init: function(element, valueAccessor, allBindings, viewModel, bindingContext) {
83 $(element).on("keydown", function(event) {
84 // Allow backspace, delete, tab, escape, and enter
85 if (event.keyCode === 46 || event.keyCode === 8 || event.keyCode === 9 || event.keyCode === 27 || event.keyCode === 13 ||
86 // Ctrl+A
87 (event.keyCode === 65 && event.ctrlKey) ||
88 // home, end, left, right, up, down
89 (event.keyCode >= 35 && event.keyCode <= 40)) {
90 return;
91 }
92 else {
93 // If value is not a number, stop key-press
94 if (event.shiftKey || (event.keyCode < 48 || event.keyCode > 57) && (event.keyCode < 96 || event.keyCode > 105)) {
95 event.preventDefault();
96 }
97 }
98 });
99 }
100};
101
102// Restrict binding to upper case letters - binding target specification required
103ko.bindingHandlers.upperCase = {
104 init: function(element, valueAccessor, allBindings, viewModel, bindingContext) {
105 $(element).on("input", function(event) {
106 const start = this.selectionStart;
107 const end = this.selectionEnd;
108 valueAccessor()(this.value = this.value.toUpperCase());
109 this.setSelectionRange(start, end);
110 });
111 }
112};
113
114// Restrict binding to lower case letters - binding target specification required
115ko.bindingHandlers.lowerCase = {
116 init: function(element, valueAccessor, allBindings, viewModel, bindingContext) {
117 $(element).on("input", function(event) {
118 const start = this.selectionStart;
119 const end = this.selectionEnd;
120 valueAccessor()(this.value = this.value.toLowerCase());
121 this.setSelectionRange(start, end);
122 });
123 }
124};
125
126// Restrict binding to upper case letters, numbers, '!', '-', '@', '#', '_' , '.' or '$' - binding target specification required
127ko.bindingHandlers.acronym = {
128 init: function(element, valueAccessor, allBindings, viewModel, bindingContext) {
129 $(element).on("keydown", function(event) {
130 // Allow backspace, delete, tab, escape, and enter
131 if (event.keyCode === 46 || event.keyCode === 8 || event.keyCode === 9 || event.keyCode === 27 || event.keyCode === 13 ||
132 // Ctrl+A
133 (event.keyCode === 65 && event.ctrlKey) ||
134 // home, end, left, right, up, down
135 (event.keyCode >= 35 && event.keyCode <= 40)) {
136 return;
137 }
138 else {
139 // If value is not a number or letter, stop key-press
140 if ((event.keyCode < 48 || event.keyCode > 57) && (event.keyCode < 96 || event.keyCode > 105) && (event.keyCode < 65 || event.keyCode > 90) && (event.keyCode < 189 || event.keyCode > 190)) {
141 event.preventDefault();
142 }
143 }
144 }).on("input", function(event) {
145 const start = this.selectionStart;
146 const end = this.selectionEnd;
147 valueAccessor()(this.value = this.value.toUpperCase());
148 this.setSelectionRange(start, end);
149 });
150 }
151};
152
153// Handle special validation error message location when using bootstrap add-on fields
154ko.bindingHandlers.validationCore = {
155 init: function(element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
156 const parent = $(element).parent().closest(".input-group");
157 const message = document.createElement("em");
158 message.className = "validationMessage small";
159
160 if (parent.length > 0)
161 $(parent).after(message);
162 else
163 $(element).after(message);
164
165 ko.applyBindingsToNode(message, { validationMessage: valueAccessor() });
166 }
167};
Source/Libraries/GSF.Web/Shared/Scripts/gsf.web.primeui.js+110−0
@@ -0,0 +1,110 @@
1//******************************************************************************************************
2// gsf.web.primeui.js - Gbtc
3//
4// Copyright © 2016, Grid Protection Alliance. All Rights Reserved.
5//
6// Licensed to the Grid Protection Alliance (GPA) under one or more contributor license agreements. See
7// the NOTICE file distributed with this work for additional information regarding copyright ownership.
8// The GPA licenses this file to you under the MIT License (MIT), the "License"; you may not use this
9// file except in compliance with the License. You may obtain a copy of the License at:
10//
11// http://opensource.org/licenses/MIT
12//
13// Unless agreed to in writing, the subject software distributed under the License is distributed on an
14// "AS-IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. Refer to the
15// License for the specific language governing permissions and limitations.
16//
17// Code Modification History:
18// ----------------------------------------------------------------------------------------------------
19// 04/08/2016 - J. Ritchie Carroll
20// Generated original version of source code.
21//
22//******************************************************************************************************
23// ReSharper disable UndeclaredGlobalVariableUsing
24
25// Grid Solutions Framework Core Web PrimeUI Extension Script Functions
26"use strict";
27
28// Get access to PrimeUI widget, e.g.: var autoCompletePanel = getPrimeUIWidget($("#myInputText"), "puiautocomplete").panel
29function getPrimeUIWidget(jqueryElement, widgetType) {
30 return jqueryElement.data("primeui-" + widgetType);
31}
32
33// Adds auto-complete lookup functionality to an input field element associated with a paged-view model
34function initializeAutoCompleteLookupField(fieldName, loadRecordsHubFunction, isObservable, addInputGroup, addShowAllDropDown, limit) {
35 if (isObservable === undefined)
36 isObservable = true;
37
38 if (addInputGroup === undefined)
39 addInputGroup = true;
40
41 if (addShowAllDropDown === undefined)
42 addShowAllDropDown = true;
43
44 if (limit === undefined)
45 limit = 50;
46
47 const inputFieldID = "input" + fieldName;
48 const inputField = $("#" + inputFieldID);
49
50 // Turn off browser remembered field values when doing auto-complete lookups
51 inputField.attr("autocomplete", "off");
52
53 if (addInputGroup)
54 inputField.wrap("<div class=\"input-group\"></div>");
55
56 if (addShowAllDropDown && viewModel.canEdit()) {
57 // Insert drop-down button after input field
58 inputField.after("<span id=\"" + inputFieldID + "ShowAll\" class=\"input-group-addon\" data-bind=\"style: {'cursor': ($root.recordMode()===RecordMode.View ? 'not-allowed' : 'pointer')}, visible: $root.recordMode()!==RecordMode.View\"><i class=\"glyphicon glyphicon-triangle-bottom\"></i></span>");
59
60 const inputFieldDropDown = $("#" + inputFieldID + "ShowAll");
61
62 // Set on-click handler for drop-down button
63 inputFieldDropDown.click(function() {
64 // Search all records when user clicks drop-down button
65 if (viewModel.dataHubIsConnected() && viewModel.recordMode() !== RecordMode.View)
66 inputField.puiautocomplete("search", "");
67 });
68
69 // Apply data bindings to newly added drop-down button
70 ko.applyBindings(viewModel, inputFieldDropDown[0]);
71 }
72
73 inputField.puiautocomplete({
74 effect: "fade",
75 effectSpeed: "fast",
76 forceSelection: true,
77 completeSource: function(request, response) {
78 const self = this;
79
80 if (viewModel.dataHubIsConnected()) {
81 loadRecordsHubFunction(request.query, limit).done(function(records) {
82 if (limit > 0 && records.length >= limit) {
83 records.push({ id: null, label: "Search results truncated..." });
84 }
85
86 response.call(self, records);
87
88 // Set z-index of panel to be above pop-up dialog
89 self.panel.css("z-index", $("#addNewEditDialog").css("z-index") + 1);
90
91 // Auto-size panel to full width of input field area (including input group buttons)
92 setTimeout(function() { self.panel.width(inputField.parent().parent().width()); }, 25);
93 });
94 }
95 }
96 });
97
98 if (isObservable) {
99 inputField.puiautocomplete({
100 select: function(event, item) {
101 // Make sure knockout sees any selection - it can't always pickup non-user initiated changes
102 if (item)
103 viewModel.currentRecord()[fieldName](item.text());
104 }
105 });
106 }
107
108 // Override jQuery UI display style added by puiautocomplete (messes with bootstrap form-element formatting)
109 inputField.parent().css("display", "inline");
110}
Source/Libraries/GSF.Web/Shared/Scripts/js.cookie.js+145−0
@@ -0,0 +1,145 @@
1/*!
2 * JavaScript Cookie v2.1.0
3 * https://github.com/js-cookie/js-cookie
4 *
5 * Copyright 2006, 2015 Klaus Hartl & Fagner Brack
6 * Released under the MIT license
7 */
8(function (factory) {
9 if (typeof define === 'function' && define.amd) {
10 define(factory);
11 } else if (typeof exports === 'object') {
12 module.exports = factory();
13 } else {
14 var _OldCookies = window.Cookies;
15 var api = window.Cookies = factory();
16 api.noConflict = function () {
17 window.Cookies = _OldCookies;
18 return api;
19 };
20 }
21}(function () {
22 function extend() {
23 var i = 0;
24 var result = {};
25 for (; i < arguments.length; i++) {
26 var attributes = arguments[i];
27 for (var key in attributes) {
28 result[key] = attributes[key];
29 }
30 }
31 return result;
32 }
33
34 function init(converter) {
35 function api(key, value, attributes) {
36 var result;
37
38 // Write
39
40 if (arguments.length > 1) {
41 attributes = extend({
42 path: '/'
43 }, api.defaults, attributes);
44
45 if (typeof attributes.expires === 'number') {
46 var expires = new Date();
47 expires.setMilliseconds(expires.getMilliseconds() + attributes.expires * 864e+5);
48 attributes.expires = expires;
49 }
50
51 try {
52 result = JSON.stringify(value);
53 if (/^[\{\[]/.test(result)) {
54 value = result;
55 }
56 } catch (e) { }
57
58 if (!converter.write) {
59 value = encodeURIComponent(String(value))
60 .replace(/%(23|24|26|2B|3A|3C|3E|3D|2F|3F|40|5B|5D|5E|60|7B|7D|7C)/g, decodeURIComponent);
61 } else {
62 value = converter.write(value, key);
63 }
64
65 key = encodeURIComponent(String(key));
66 key = key.replace(/%(23|24|26|2B|5E|60|7C)/g, decodeURIComponent);
67 key = key.replace(/[\(\)]/g, escape);
68
69 return (document.cookie = [
70 key, '=', value,
71 attributes.expires && '; expires=' + attributes.expires.toUTCString(), // use expires attribute, max-age is not supported by IE
72 attributes.path && '; path=' + attributes.path,
73 attributes.domain && '; domain=' + attributes.domain,
74 attributes.secure ? '; secure' : ''
75 ].join(''));
76 }
77
78 // Read
79
80 if (!key) {
81 result = {};
82 }
83
84 // To prevent the for loop in the first place assign an empty array
85 // in case there are no cookies at all. Also prevents odd result when
86 // calling "get()"
87 var cookies = document.cookie ? document.cookie.split('; ') : [];
88 var rdecode = /(%[0-9A-Z]{2})+/g;
89 var i = 0;
90
91 for (; i < cookies.length; i++) {
92 var parts = cookies[i].split('=');
93 var name = parts[0].replace(rdecode, decodeURIComponent);
94 var cookie = parts.slice(1).join('=');
95
96 if (cookie.charAt(0) === '"') {
97 cookie = cookie.slice(1, -1);
98 }
99
100 try {
101 cookie = converter.read ?
102 converter.read(cookie, name) : converter(cookie, name) ||
103 cookie.replace(rdecode, decodeURIComponent);
104
105 if (this.json) {
106 try {
107 cookie = JSON.parse(cookie);
108 } catch (e) { }
109 }
110
111 if (key === name) {
112 result = cookie;
113 break;
114 }
115
116 if (!key) {
117 result[name] = cookie;
118 }
119 } catch (e) { }
120 }
121
122 return result;
123 }
124
125 api.get = api.set = api;
126 api.getJSON = function () {
127 return api.apply({
128 json: true
129 }, [].slice.call(arguments));
130 };
131 api.defaults = {};
132
133 api.remove = function (key, attributes) {
134 api(key, '', extend(attributes, {
135 expires: -1
136 }));
137 };
138
139 api.withConverter = init;
140
141 return api;
142 }
143
144 return init(function () { });
145}));
Source/Libraries/GSF.Web/Shared/Scripts/knockout.mapping.js+21−0
@@ -0,0 +1,21 @@
1/// Knockout Mapping plugin v2.4.0
2/// (c) 2013 Steven Sanderson, Roy Jacobs - http://knockoutjs.com/
3/// License: MIT (http://www.opensource.org/licenses/mit-license.php)
4(function(d){"function"===typeof require&&"object"===typeof exports&&"object"===typeof module?d(require("knockout"),exports):"function"===typeof define&&define.amd?define(["knockout","exports"],d):d(ko,ko.mapping={})})(function(d,f){function y(b,c){var a,e;for(e in c)if(c.hasOwnProperty(e)&&c[e])if(a=f.getType(b[e]),e&&b[e]&&"array"!==a&&"string"!==a)y(b[e],c[e]);else if("array"===f.getType(b[e])&&"array"===f.getType(c[e])){a=b;for(var d=e,l=b[e],n=c[e],t={},g=l.length-1;0<=g;--g)t[l[g]]=l[g];for(g=
5n.length-1;0<=g;--g)t[n[g]]=n[g];l=[];n=void 0;for(n in t)l.push(t[n]);a[d]=l}else b[e]=c[e]}function E(b,c){var a={};y(a,b);y(a,c);return a}function z(b,c){for(var a=E({},b),d=L.length-1;0<=d;d--){var f=L[d];a[f]&&(a[""]instanceof Object||(a[""]={}),a[""][f]=a[f],delete a[f])}c&&(a.ignore=h(c.ignore,a.ignore),a.include=h(c.include,a.include),a.copy=h(c.copy,a.copy),a.observe=h(c.observe,a.observe));a.ignore=h(a.ignore,j.ignore);a.include=h(a.include,j.include);a.copy=h(a.copy,j.copy);a.observe=h(a.observe,
6j.observe);a.mappedProperties=a.mappedProperties||{};a.copiedProperties=a.copiedProperties||{};return a}function h(b,c){"array"!==f.getType(b)&&(b="undefined"===f.getType(b)?[]:[b]);"array"!==f.getType(c)&&(c="undefined"===f.getType(c)?[]:[c]);return d.utils.arrayGetDistinctValues(b.concat(c))}function F(b,c,a,e,k,l,n){var t="array"===f.getType(d.utils.unwrapObservable(c));l=l||"";if(f.isMapped(b)){var g=d.utils.unwrapObservable(b)[p];a=E(g,a)}var j=n||k,h=function(){return a[e]&&a[e].create instanceof
7Function},x=function(b){var f=G,g=d.dependentObservable;d.dependentObservable=function(a,b,c){c=c||{};a&&"object"==typeof a&&(c=a);var e=c.deferEvaluation,M=!1;c.deferEvaluation=!0;a=new H(a,b,c);if(!e){var g=a,e=d.dependentObservable;d.dependentObservable=H;a=d.isWriteableObservable(g);d.dependentObservable=e;a=H({read:function(){M||(d.utils.arrayRemoveItem(f,g),M=!0);return g.apply(g,arguments)},write:a&&function(a){return g(a)},deferEvaluation:!0});f.push(a)}return a};d.dependentObservable.fn=
8H.fn;d.computed=d.dependentObservable;b=d.utils.unwrapObservable(k)instanceof Array?a[e].create({data:b||c,parent:j,skip:N}):a[e].create({data:b||c,parent:j});d.dependentObservable=g;d.computed=d.dependentObservable;return b},u=function(){return a[e]&&a[e].update instanceof Function},v=function(b,f){var g={data:f||c,parent:j,target:d.utils.unwrapObservable(b)};d.isWriteableObservable(b)&&(g.observable=b);return a[e].update(g)};if(n=I.get(c))return n;e=e||"";if(t){var t=[],s=!1,m=function(a){return a};
9a[e]&&a[e].key&&(m=a[e].key,s=!0);d.isObservable(b)||(b=d.observableArray([]),b.mappedRemove=function(a){var c="function"==typeof a?a:function(b){return b===m(a)};return b.remove(function(a){return c(m(a))})},b.mappedRemoveAll=function(a){var c=C(a,m);return b.remove(function(a){return-1!=d.utils.arrayIndexOf(c,m(a))})},b.mappedDestroy=function(a){var c="function"==typeof a?a:function(b){return b===m(a)};return b.destroy(function(a){return c(m(a))})},b.mappedDestroyAll=function(a){var c=C(a,m);return b.destroy(function(a){return-1!=
10d.utils.arrayIndexOf(c,m(a))})},b.mappedIndexOf=function(a){var c=C(b(),m);a=m(a);return d.utils.arrayIndexOf(c,a)},b.mappedCreate=function(a){if(-1!==b.mappedIndexOf(a))throw Error("There already is an object with the key that you specified.");var c=h()?x(a):a;u()&&(a=v(c,a),d.isWriteableObservable(c)?c(a):c=a);b.push(c);return c});n=C(d.utils.unwrapObservable(b),m).sort();g=C(c,m);s&&g.sort();s=d.utils.compareArrays(n,g);n={};var J,A=d.utils.unwrapObservable(c),y={},z=!0,g=0;for(J=A.length;g<J;g++){var r=
11m(A[g]);if(void 0===r||r instanceof Object){z=!1;break}y[r]=A[g]}var A=[],B=0,g=0;for(J=s.length;g<J;g++){var r=s[g],q,w=l+"["+g+"]";switch(r.status){case "added":var D=z?y[r.value]:K(d.utils.unwrapObservable(c),r.value,m);q=F(void 0,D,a,e,b,w,k);h()||(q=d.utils.unwrapObservable(q));w=O(d.utils.unwrapObservable(c),D,n);q===N?B++:A[w-B]=q;n[w]=!0;break;case "retained":D=z?y[r.value]:K(d.utils.unwrapObservable(c),r.value,m);q=K(b,r.value,m);F(q,D,a,e,b,w,k);w=O(d.utils.unwrapObservable(c),D,n);A[w]=
12q;n[w]=!0;break;case "deleted":q=K(b,r.value,m)}t.push({event:r.status,item:q})}b(A);a[e]&&a[e].arrayChanged&&d.utils.arrayForEach(t,function(b){a[e].arrayChanged(b.event,b.item)})}else if(P(c)){b=d.utils.unwrapObservable(b);if(!b){if(h())return s=x(),u()&&(s=v(s)),s;if(u())return v(s);b={}}u()&&(b=v(b));I.save(c,b);if(u())return b;Q(c,function(e){var f=l.length?l+"."+e:e;if(-1==d.utils.arrayIndexOf(a.ignore,f))if(-1!=d.utils.arrayIndexOf(a.copy,f))b[e]=c[e];else if("object"!=typeof c[e]&&"array"!=
13typeof c[e]&&0<a.observe.length&&-1==d.utils.arrayIndexOf(a.observe,f))b[e]=c[e],a.copiedProperties[f]=!0;else{var g=I.get(c[e]),k=F(b[e],c[e],a,e,b,f,b),g=g||k;if(0<a.observe.length&&-1==d.utils.arrayIndexOf(a.observe,f))b[e]=g(),a.copiedProperties[f]=!0;else{if(d.isWriteableObservable(b[e]))b[e](d.utils.unwrapObservable(g));else g=void 0===b[e]?g:d.utils.unwrapObservable(g),b[e]=g;a.mappedProperties[f]=!0}}})}else switch(f.getType(c)){case "function":u()?d.isWriteableObservable(c)?(c(v(c)),b=c):
14b=v(c):b=c;break;default:if(d.isWriteableObservable(b))return q=u()?v(b):d.utils.unwrapObservable(c),b(q),q;h()||u();b=h()?x():d.observable(d.utils.unwrapObservable(c));u()&&b(v(b))}return b}function O(b,c,a){for(var e=0,d=b.length;e<d;e++)if(!0!==a[e]&&b[e]===c)return e;return null}function R(b,c){var a;c&&(a=c(b));"undefined"===f.getType(a)&&(a=b);return d.utils.unwrapObservable(a)}function K(b,c,a){b=d.utils.unwrapObservable(b);for(var e=0,f=b.length;e<f;e++){var l=b[e];if(R(l,a)===c)return l}throw Error("When calling ko.update*, the key '"+
15c+"' was not found!");}function C(b,c){return d.utils.arrayMap(d.utils.unwrapObservable(b),function(a){return c?R(a,c):a})}function Q(b,c){if("array"===f.getType(b))for(var a=0;a<b.length;a++)c(a);else for(a in b)c(a)}function P(b){var c=f.getType(b);return("object"===c||"array"===c)&&null!==b}function T(){var b=[],c=[];this.save=function(a,e){var f=d.utils.arrayIndexOf(b,a);0<=f?c[f]=e:(b.push(a),c.push(e))};this.get=function(a){a=d.utils.arrayIndexOf(b,a);return 0<=a?c[a]:void 0}}function S(){var b=
16{},c=function(a){var c;try{c=a}catch(d){c="$$$"}a=b[c];void 0===a&&(a=new T,b[c]=a);return a};this.save=function(a,b){c(a).save(a,b)};this.get=function(a){return c(a).get(a)}}var p="__ko_mapping__",H=d.dependentObservable,B=0,G,I,L=["create","update","key","arrayChanged"],N={},x={include:["_destroy"],ignore:[],copy:[],observe:[]},j=x;f.isMapped=function(b){return(b=d.utils.unwrapObservable(b))&&b[p]};f.fromJS=function(b){if(0==arguments.length)throw Error("When calling ko.fromJS, pass the object you want to convert.");
17try{B++||(G=[],I=new S);var c,a;2==arguments.length&&(arguments[1][p]?a=arguments[1]:c=arguments[1]);3==arguments.length&&(c=arguments[1],a=arguments[2]);a&&(c=E(c,a[p]));c=z(c);var e=F(a,b,c);a&&(e=a);if(!--B)for(;G.length;){var d=G.pop();d&&d()}e[p]=E(e[p],c);return e}catch(f){throw B=0,f;}};f.fromJSON=function(b){var c=d.utils.parseJson(b);arguments[0]=c;return f.fromJS.apply(this,arguments)};f.updateFromJS=function(){throw Error("ko.mapping.updateFromJS, use ko.mapping.fromJS instead. Please note that the order of parameters is different!");
18};f.updateFromJSON=function(){throw Error("ko.mapping.updateFromJSON, use ko.mapping.fromJSON instead. Please note that the order of parameters is different!");};f.toJS=function(b,c){j||f.resetDefaultOptions();if(0==arguments.length)throw Error("When calling ko.mapping.toJS, pass the object you want to convert.");if("array"!==f.getType(j.ignore))throw Error("ko.mapping.defaultOptions().ignore should be an array.");if("array"!==f.getType(j.include))throw Error("ko.mapping.defaultOptions().include should be an array.");
19if("array"!==f.getType(j.copy))throw Error("ko.mapping.defaultOptions().copy should be an array.");c=z(c,b[p]);return f.visitModel(b,function(a){return d.utils.unwrapObservable(a)},c)};f.toJSON=function(b,c){var a=f.toJS(b,c);return d.utils.stringifyJson(a)};f.defaultOptions=function(){if(0<arguments.length)j=arguments[0];else return j};f.resetDefaultOptions=function(){j={include:x.include.slice(0),ignore:x.ignore.slice(0),copy:x.copy.slice(0)}};f.getType=function(b){if(b&&"object"===typeof b){if(b.constructor===
20Date)return"date";if(b.constructor===Array)return"array"}return typeof b};f.visitModel=function(b,c,a){a=a||{};a.visitedObjects=a.visitedObjects||new S;var e,k=d.utils.unwrapObservable(b);if(P(k))a=z(a,k[p]),c(b,a.parentName),e="array"===f.getType(k)?[]:{};else return c(b,a.parentName);a.visitedObjects.save(b,e);var l=a.parentName;Q(k,function(b){if(!(a.ignore&&-1!=d.utils.arrayIndexOf(a.ignore,b))){var j=k[b],g=a,h=l||"";"array"===f.getType(k)?l&&(h+="["+b+"]"):(l&&(h+="."),h+=b);g.parentName=h;
21if(!(-1===d.utils.arrayIndexOf(a.copy,b)&&-1===d.utils.arrayIndexOf(a.include,b)&&k[p]&&k[p].mappedProperties&&!k[p].mappedProperties[b]&&k[p].copiedProperties&&!k[p].copiedProperties[b]&&"array"!==f.getType(k)))switch(f.getType(d.utils.unwrapObservable(j))){case "object":case "array":case "undefined":g=a.visitedObjects.get(j);e[b]="undefined"!==f.getType(g)?g:f.visitModel(j,c,a);break;default:e[b]=c(j,a.parentName)}}});return e}});
Source/Libraries/GSF.Web/Shared/Scripts/knockout.observableDictionary.js+224−0
@@ -0,0 +1,224 @@
1// Knockout Observable Dictionary
2// (c) James Foster
3// License: MIT (http://www.opensource.org/licenses/mit-license.php)
4
5(function () {
6 function DictionaryItem(key, value, dictionary) {
7 var observableKey = new ko.observable(key);
8
9 this.value = new ko.observable(value);
10 this.key = new ko.computed({
11 read: observableKey,
12 write: function (newKey) {
13 var current = observableKey();
14
15 if (current == newKey) return;
16
17 // no two items are allowed to share the same key.
18 dictionary.remove(newKey);
19
20 observableKey(newKey);
21 }
22 });
23 }
24
25 ko.observableDictionary = function (dictionary, keySelector, valueSelector) {
26 var result = {};
27
28 result.items = new ko.observableArray();
29
30 result._wrappers = {};
31 result._keySelector = keySelector || function (value, key) { return key; };
32 result._valueSelector = valueSelector || function (value) { return value; };
33
34 if (typeof keySelector == 'string') result._keySelector = function (value) { return value[keySelector]; };
35 if (typeof valueSelector == 'string') result._valueSelector = function (value) { return value[valueSelector]; };
36
37 ko.utils.extend(result, ko.observableDictionary['fn']);
38
39 result.pushAll(dictionary);
40
41 return result;
42 };
43
44 ko.observableDictionary['fn'] = {
45 remove: function (valueOrPredicate) {
46 var predicate = valueOrPredicate;
47
48 if (valueOrPredicate instanceof DictionaryItem) {
49 predicate = function (item) {
50 return item.key() === valueOrPredicate.key();
51 };
52 }
53 else if (typeof valueOrPredicate != "function") {
54 predicate = function (item) {
55 return item.key() === valueOrPredicate;
56 };
57 }
58
59 ko.observableArray['fn'].remove.call(this.items, predicate);
60 },
61
62 push: function (key, value) {
63 var item = null;
64
65 if (key instanceof DictionaryItem) {
66 // handle the case where only a DictionaryItem is passed in
67 item = key;
68 value = key.value();
69 key = key.key();
70 }
71
72 if (value === undefined) {
73 value = this._valueSelector(key);
74 key = this._keySelector(value);
75 }
76 else {
77 value = this._valueSelector(value);
78 }
79
80 var current = this.get(key, false);
81 if (current) {
82 // update existing value
83 current(value);
84 return current;
85 }
86
87 if (!item) {
88 item = new DictionaryItem(key, value, this);
89 }
90
91 ko.observableArray['fn'].push.call(this.items, item);
92
93 return value;
94 },
95
96 pushAll: function (dictionary) {
97 var self = this;
98 var items = self.items();
99
100 if (dictionary instanceof Array) {
101 $.each(dictionary, function (index, item) {
102 var key = self._keySelector(item, index);
103 var value = self._valueSelector(item);
104 items.push(new DictionaryItem(key, value, self));
105 });
106 }
107 else {
108 for (var prop in dictionary) {
109 if (dictionary.hasOwnProperty(prop)) {
110 var item = dictionary[prop];
111 var key = self._keySelector(item, prop);
112 var value = self._valueSelector(item);
113 items.push(new DictionaryItem(key, value, self));
114 }
115 }
116 }
117
118 self.items.valueHasMutated();
119 },
120
121 sort: function (method) {
122 if (method === undefined) {
123 method = function (a, b) {
124 return defaultComparison(a.key(), b.key());
125 };
126 }
127
128 return ko.observableArray['fn'].sort.call(this.items, method);
129 },
130
131 indexOf: function (key) {
132 if (key instanceof DictionaryItem) {
133 return ko.observableArray['fn'].indexOf.call(this.items, key);
134 }
135
136 var underlyingArray = this.items();
137 for (var index = 0; index < underlyingArray.length; index++) {
138 if (underlyingArray[index].key() == key)
139 return index;
140 }
141 return -1;
142 },
143
144 get: function (key, wrap) {
145 if (wrap == false)
146 return getValue(key, this.items());
147
148 var wrapper = this._wrappers[key];
149
150 if (wrapper == null) {
151 wrapper = this._wrappers[key] = new ko.computed({
152 read: function () {
153 var value = getValue(key, this.items());
154 return value ? value() : null;
155 },
156 write: function (newValue) {
157 var value = getValue(key, this.items());
158
159 if (value)
160 value(newValue);
161 else
162 this.push(key, newValue);
163 }
164 }, this);
165 }
166
167 return wrapper;
168 },
169
170 set: function (key, value) {
171 return this.push(key, value);
172 },
173
174 keys: function () {
175 return ko.utils.arrayMap(this.items(), function (item) { return item.key(); });
176 },
177
178 values: function () {
179 return ko.utils.arrayMap(this.items(), function (item) { return item.value(); });
180 },
181
182 removeAll: function () {
183 this.items.removeAll();
184 },
185
186 toJSON: function () {
187 var result = {};
188 var items = ko.utils.unwrapObservable(this.items);
189
190 ko.utils.arrayForEach(items, function (item) {
191 var key = ko.utils.unwrapObservable(item.key);
192 var value = ko.utils.unwrapObservable(item.value);
193
194 result[key] = value;
195 });
196
197 return result;
198 }
199 };
200
201 function getValue(key, items) {
202 var found = ko.utils.arrayFirst(items, function (item) {
203 return item.key() == key;
204 });
205 return found ? found.value : null;
206 }
207})();
208
209
210// Utility methods
211// ---------------------------------------------
212function isNumeric(n) {
213 return !isNaN(parseFloat(n)) && isFinite(n);
214}
215
216function defaultComparison(a, b) {
217 if (isNumeric(a) && isNumeric(b)) return a - b;
218
219 a = a.toString();
220 b = b.toString();
221
222 return a == b ? 0 : (a < b ? -1 : 1);
223}
224// ---------------------------------------------
Source/Libraries/GSF.Web/Shared/Scripts/knockout.reactor.js+247−0
@@ -0,0 +1,247 @@
1// Deep observer plugin for Knockout http://knockoutjs.com/
2// (c) Ziad Jeeroburkhan
3// License: MIT (http://www.opensource.org/licenses/mit-license.php)
4// Version 1.3.0
5
6ko.subscribable.fn['watch'] = function (targetOrEvaluatorCallback, options, evaluatorCallback, context) {
7 /// <summary>
8 /// Track and manage changes within the chained observable down to any given level.
9 /// </summary>
10 /// <param name="options" type="object">
11 /// false -> Disables tracking on the chained observable.
12 /// { depth: 2 } -> Track all nested subscribables down to the 2nd level(default is 1).<br/>
13 /// { depth: -1 } -> Track all nested subscribables.<br/>
14 /// { hide: [...] } -> Property or array of properties to be ignored.<br/>
15 /// { hideArrays: true } -> Ignore all nested arrays.<br/>
16 /// { hideWrappedValues: true } -> Ignore observables wrapped under yet another parent observable.<br/>
17 /// { mutable: true } -> Dynamically adapt to changes made to the target structure through its own subscribables.<br/>
18 /// { watchedOnly: true } -> Watch only subscribables tagged with .watch().<br/>
19 /// { beforeWatch: function(parents, child) {...} } -> Function called prior to creating a subscription. Returning false aborts the operation and ignores its children.<br/>
20 /// { wrap: true } -> Wrap all fields into observables. This happens on the fly for new array items(or child objects when mutable is set to true).<br/>
21 /// { beforeWrap: function(parents, field, value) {...} } -> Function called prior to wrapping a value into an observable. Returning false leaves it as it is.<br/>
22 /// { tagParentsWithName: true } -> Add the property '_fieldName' under each parent for easy identification.<br/>
23 /// { keepOldValues: 3 } -> Keep the last three values for each subscribable under the property 'oldValues'.<br/>
24 /// { seal: true } -> Prevent any subsequent watcher from watching down the target tree.<br/>
25 /// </param>
26 /// <param name="evaluatorCallback" type="function">
27 /// The callback function called during changes. Any return value is assigned to the chained observable.
28 /// </param>
29
30 var targetType = typeof targetOrEvaluatorCallback;
31
32 if (targetType === 'boolean' || targetType === 'undefined') {
33 // Turn on or off the watcher for the specified target along with any of its children.
34 ko.watch(this, { enabled: targetOrEvaluatorCallback !== false });
35 } else if (targetType === 'function' && !ko.isSubscribable(targetOrEvaluatorCallback)) {
36 // Target the chained subscribable itself if no target subscribable or object was passed.
37 ko.watch(this, options || {}, targetOrEvaluatorCallback, context || this);
38 } else {
39 ko.watch(targetOrEvaluatorCallback, options, evaluatorCallback, context || this);
40 }
41
42 return this;
43};
44
45ko['watch'] = function (target, options, evaluatorCallback, context) {
46 /// <summary>
47 /// Track and manage changes within a specific target object down to any given level.
48 /// </summary>
49 /// <param name="target">
50 /// An object or function containing targeted subscribables or the subscribable itself.
51 /// </param>
52 /// <param name="options" type="object">
53 /// { depth: 2 } -> Track all nested subscribables down to the 2nd level(default is 1).<br/>
54 /// { depth: -1 } -> Track all nested subscribables.<br/>
55 /// { hide: [...] } -> Property or array of properties to be ignored.<br/>
56 /// { hideArrays: true } -> Ignore all nested arrays.<br/>
57 /// { hideWrappedValues: true } -> Ignore observables wrapped under yet another parent observable.<br/>
58 /// { mutable: true } -> Dynamically adapt to changes made to the target structure through its own subscribables.<br/>
59 /// { watchedOnly: true } -> Watch only subscribables tagged with .watch().<br/>
60 /// { beforeWatch: function(parents, child) {...} } -> Function called prior to creating a subscription. Returning false aborts the operation and ignores its children.<br/>
61 /// { wrap: true } -> Wrap all fields into observables. This happens on the fly for new array items(or child objects when mutable is set to true).<br/>
62 /// { beforeWrap: function(parents, field, value) {...} } -> Function called prior to wrapping a value into an observable. Returning false leaves it as it is.<br/>
63 /// { tagParentsWithName: true } -> Add the property '_fieldName' under each parent for easy identification.<br/>
64 /// { keepOldValues: 3 } -> Keep the last three values for each subscribable under the property 'oldValues'.<br/>
65 /// { seal: true } -> Prevent any subsequent watcher from watching down the target tree.<br/>
66 /// </param>
67 /// <param name="evaluatorCallback" type="function">
68 /// The callback function called during changes.
69 /// </param>
70
71 if (typeof options === 'function') {
72 context = context || evaluatorCallback;
73 evaluatorCallback = options;
74 options = {};
75 }
76
77 context = context || this;
78
79 function watchChildren(child, parent, grandParents, unwatch, keepOffParentList) {
80
81 if (child && options.depth !== 0 && (options.depth === -1 || grandParents.length < (options.depth || 1))) {
82
83 // Proceed on watched children only when in watched-only mode.
84 if (options.watchedOnly && !child.watchable && child != target)
85 return;
86
87 // Setting the target as false prevents it from being watched later on.
88 if (options.enabled === false || options.enabled === true)
89 child.watchable = options.enabled;
90
91 // Ignore watch-disabled objects.
92 if (child.watchable === false)
93 return;
94
95 // Prevent subsequent watchers from watching the target when sealed.
96 if (options.seal === true)
97 child.watchable = false;
98
99 // Bypass circular references.
100 if (child === parent || ko.utils.arrayIndexOf(grandParents, child) > -1)
101 return;
102
103 // Merge parents. Using a fresh array so it is not referenced in the next recursion if any.
104 var parents = [].concat(grandParents, parent && parent !== target ? parent : []);
105
106 // Ignore hidden objects. Also applies to any of their children.
107 if (options.hide)
108 if (typeof options.hide === 'object'
109 ? options.hide === child
110 : ko.utils.arrayIndexOf(options.hide, child) > -1)
111 return;
112
113 if (ko.isSubscribable(child)) {
114 if (evaluatorCallback) {
115 if (unwatch === true || !options.beforeWatch || options.beforeWatch.call(context, parents, child) !== false) {
116 if (typeof child.pop === 'function') {
117 // Child is an observable array. Watch all item changes within it.
118 child.subscribe(function (changes) {
119 ko.utils.arrayForEach(changes, function (item) {
120
121 var returnValue = evaluatorCallback.call(context, parents, child, item);
122 if (returnValue !== undefined)
123 context(returnValue);
124
125 if (!item.moved) {
126 // Deleted or brand new item. Unwatch or watch it.
127 setTimeout(function () {
128 watchChildren(item.value, (keepOffParentList ? null : child), parents, item.status === 'deleted');
129 }, 0);
130 }
131 });
132 }, undefined, 'arrayChange');
133
134 watchChildren(child(), (keepOffParentList ? null : child), parents, unwatch, true);
135
136 return true;
137
138 } else {
139
140 if (unwatch === true) {
141 // A subscribable was removed from an array or mutable object.
142 // Clean up all its change subscriptions through either H or _subscriptions
143 // depending on whether the Knockout code is minified or not.
144 var subsc = child.H || child._subscriptions;
145
146 if (subsc) {
147 if (subsc.change)
148 for (var i = subsc.change.length - 1; i >= 0; i--)
149 if (subsc.change[i]._watcher === context)
150 subsc.change[i].dispose();
151
152 if (subsc.beforeChange && options.keepOldValues > 0)
153 // Also clean up any before-change subscriptions used for tracking old values.
154 for (var i = subsc.beforeChange.length - 1; i >= 0; i--)
155 if (subsc.beforeChange[i]._watcher === context)
156 subsc.beforeChange[i].dispose();
157 }
158
159 watchChildren(child(), (keepOffParentList ? null : child), parents, true, true);
160
161 } else {
162
163 if (options.enabled === true && child.watchable === false)
164 // Only waking up an existing watcher. Let's not add another.
165 return;
166
167 if (options.keepOldValues > 0 || options.mutable) {
168 child.subscribe(function (oldValue) {
169 if (options.keepOldValues > 0) {
170 // Add old value to history list before every update.
171 var values = (child['oldValues']
172 ? child['oldValues']
173 : child['oldValues'] = []);
174
175 values.unshift(oldValue);
176
177 while (values.length > options.keepOldValues)
178 values.pop();
179 }
180
181 if (options.mutable && typeof oldValue === 'object')
182 // Clean up all subscriptions for the old child object.
183 watchChildren(oldValue, (keepOffParentList ? null : child), parents, true, true);
184
185 }, null, 'beforeChange')._watcher = context;
186 }
187
188 child.subscribe(function () {
189 if (child.watchable !== false) {
190 var returnValue = evaluatorCallback.call(context, parents, child);
191
192 if (returnValue !== undefined)
193 context(returnValue);
194
195 if (options.mutable && typeof child() === 'object')
196 // Watch the new comer object.
197 watchChildren(child(), (keepOffParentList ? null : child), parents);
198 }
199
200 }, null, 'change')._watcher = context;
201
202 if (options.hideWrappedValues !== true)
203 watchChildren(child(), (keepOffParentList ? null : child), parents);
204 }
205 }
206 }
207 }
208
209 } else {
210 switch (Object.prototype.toString.call(child)) {
211 case '[object Object]':
212 ko.utils.objectForEach(child, function (property, sub) {
213 if (options.wrap) {
214 // Wrap simple objects and arrays into observables.
215 var type = Object.prototype.toString.call(sub);
216 if (type !== '[object Function]' && type !== '[object Object]') {
217 if (!options.beforeWrap || options.beforeWrap.call(context, parents, child, sub) !== false) {
218 sub = child[property] = type === '[object Array]'
219 ? ko.observableArray(sub)
220 : ko.observable(sub);
221 }
222 }
223 }
224
225 var subHasChildren = watchChildren(sub, (keepOffParentList ? null : child), parents, unwatch);
226
227 if (subHasChildren && options.tagParentsWithName === true && !sub['_fieldName'])
228 sub['_fieldName'] = property;
229 });
230 return true;
231
232 case '[object Array]':
233 if (options.hideArrays !== true)
234 for (var i = 0; i < child.length; i++)
235 watchChildren(child[i], (keepOffParentList ? null : child), parents, unwatch);
236 return true;
237 }
238 }
239 }
240 }
241
242 // Use a computed when targeting a non-watchable function.
243 if (typeof target === 'function' && !ko.isSubscribable(target))
244 return ko.computed(target, evaluatorCallback, options);
245
246 watchChildren(target, null, []);
247};

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t