Unified · Split
| @@ -263,6 +263,13 @@ · 262 unmodified lines | ||
| 263 | 263 | <None Include="PostBuildSetup.bat" /> |
| 264 | 264 | </ItemGroup> |
| 265 | 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 | 273 | <EmbeddedResource Include="Security\Scripts\core.js" /> |
| 267 | 274 | <EmbeddedResource Include="Security\Scripts\enc-base64.js" /> |
| 268 | 275 | <EmbeddedResource Include="Security\Scripts\ntlm.js" /> |
| @@ -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 | 7 | * @license AngularJS v1.5.8 |
| 3 | 8 | * (c) 2010-2016 Google, Inc. http://angularjs.org |
| @@ -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 | 7 | // gsf.web.client.js - Gbtc |
| 3 | 8 | // |
| 4 | 9 | // Copyright © 2016, Grid Protection Alliance. All Rights Reserved. |
| @@ -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 | 7 | // gsf.web.knockout.js - Gbtc |
| 3 | 8 | // |
| 4 | 9 | // Copyright © 2016, Grid Protection Alliance. All Rights Reserved. |
| @@ -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 | 7 | // gsf.web.primeui.js - Gbtc |
| 3 | 8 | // |
| 4 | 9 | // Copyright © 2016, Grid Protection Alliance. All Rights Reserved. |
| @@ -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 | 7 | * JavaScript Cookie v2.1.0 |
| 3 | 8 | * https://github.com/js-cookie/js-cookie |
| 4 | 9 | * |
| @@ -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 | // Deep observer plugin for Knockout http://knockoutjs.com/ |
| 2 | 7 | // (c) Ziad Jeeroburkhan |
| 3 | 8 | // License: MIT (http://www.opensource.org/licenses/mit-license.php) |
| @@ -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 | + | |
| 1 | 6 | ko.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"; |
| @@ -1,4 +1,9 @@ | ||
| 1 | −// Knockout Observable Dictionary | |
| 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 | +// Knockout Observable Dictionary | |
| 2 | 7 | // (c) James Foster |
| 3 | 8 | // License: MIT (http://www.opensource.org/licenses/mit-license.php) |
| 4 | 9 | |
| @@ -25,6 +25,22 @@ · 24 unmodified lines | ||
| 25 | 25 | // Paged view model base class scripts |
| 26 | 26 | "use strict"; |
| 27 | 27 | |
| 28 | +if (typeof jQuery === "undefined") { | |
| 29 | + throw new Error("pageViewModel script requires jQuery - make sure jquery.js is loaded first"); | |
| 30 | +} | |
| 31 | + | |
| 32 | +if (typeof Cookies.get === "undefined") { | |
| 33 | + throw new Error("pageViewModel script requires js.cookie - make sure js.cookie.js is loaded first"); | |
| 34 | +} | |
| 35 | + | |
| 36 | +if (typeof ko === "undefined") { | |
| 37 | + throw new Error("pageViewModel script requires knockout - make sure knockout.js is loaded first"); | |
| 38 | +} | |
| 39 | + | |
| 40 | +if (typeof Dictionary === "undefined") { | |
| 41 | + throw new Error("pageViewModel script requires gsf.web.client - make sure gsf.web.client.js is loaded first"); | |
| 42 | +} | |
| 43 | + | |
| 28 | 44 | var RecordMode = { |
| 29 | 45 | View: 0, |
| 30 | 46 | Edit: 1, |
| @@ -42,6 +42,12 @@ · 41 unmodified lines | ||
| 42 | 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 | 51 | // Setup page control scripts |
| 46 | 52 | StringBuilder pageControlScripts = ViewBag.PageControlScripts; |
| 47 | 53 | |
| @@ -53,22 +59,20 @@ · 5 unmodified lines | ||
| 53 | 59 | ViewBag.RecordsTitle ?? "Records" |
| 54 | 60 | ); |
| 55 | 61 | |
| 56 | − pageControlScripts.Append(string.Format(@" | |
| 57 | − <script src=""{0}""></script> | |
| 58 | − <script src=""{1}""></script> | |
| 59 | − <script src=""{2}""></script> | |
| 60 | − ", | |
| 61 | − Url.Content("~/Scripts/knockout-3.4.0.js"), | |
| 62 | − Url.Content("~/Scripts/knockout.mapping-latest.js"), | |
| 63 | − Url.Content("~/Scripts/knockout.validation.min.js")) | |
| 64 | − ); | |
| 62 | + if (includeScripts) { | |
| 63 | + pageControlScripts.Append(@" | |
| 64 | + <script src=""/@GSF/Web/Shared/Scripts/knockout.js""></script> | |
| 65 | + <script src=""/@GSF/Web/Shared/Scripts/knockout.mapping.js""></script> | |
| 66 | + <script src=""/@GSF/Web/Shared/Scripts/knockout.validation.js""></script> | |
| 67 | + <script src=""/@GSF/Web/Shared/Scripts/knockout.reactor.min.js""></script> | |
| 68 | + <script src=""/@GSF/Web/Shared/Scripts/knockout.observableDictionary.js""></script> | |
| 69 | + <script src=""/@GSF/Web/Shared/Scripts/js.cookie.js""></script> | |
| 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 | 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 | 76 | <script> |
| 73 | 77 | $(function() { |
| 74 | 78 | $(""#clearSortOrder"").click(function() { |
| @@ -28,30 +28,35 @@ namespace GSF.Web.Shared · 27 unmodified lines | ||
| 28 | 28 | /// </summary> |
| 29 | 29 | 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 | 36 | /// <summary> |
| 32 | 37 | /// Gets common set of icon resources that can be included in web page header. |
| 33 | 38 | /// </summary> |
| 34 | − 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"" /> | |
| 36 | − <link rel=""apple-touch-icon-precomposed"" sizes=""114x114"" href=""/@GSF/Web/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"" /> | |
| 38 | − <link rel=""apple-touch-icon-precomposed"" sizes=""144x144"" href=""/@GSF/Web/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"" /> | |
| 40 | − <link rel=""apple-touch-icon-precomposed"" sizes=""120x120"" href=""/@GSF/Web/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"" /> | |
| 42 | − <link rel=""apple-touch-icon-precomposed"" sizes=""152x152"" href=""/@GSF/Web/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"" /> | |
| 44 | − <link rel=""icon"" type=""image/png"" href=""/@GSF/Web/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"" /> | |
| 46 | − <link rel=""icon"" type=""image/png"" href=""/@GSF/Web/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"" /> | |
| 48 | − <link rel=""shortcut icon"" href=""/@GSF/Web/Shared/Images/Icons/favicon.ico"" /> | |
| 39 | + public static readonly string HeaderIcons = $@" | |
| 40 | + <link rel=""apple-touch-icon-precomposed"" sizes=""57x57"" href=""{Root}/Shared/Images/Icons/apple-touch-icon-57x57.png"" /> | |
| 41 | + <link rel=""apple-touch-icon-precomposed"" sizes=""114x114"" href=""{Root}/Shared/Images/Icons/apple-touch-icon-114x114.png"" /> | |
| 42 | + <link rel=""apple-touch-icon-precomposed"" sizes=""72x72"" href=""{Root}/Shared/Images/Icons/apple-touch-icon-72x72.png"" /> | |
| 43 | + <link rel=""apple-touch-icon-precomposed"" sizes=""144x144"" href=""{Root}/Shared/Images/Icons/apple-touch-icon-144x144.png"" /> | |
| 44 | + <link rel=""apple-touch-icon-precomposed"" sizes=""60x60"" href=""{Root}/Shared/Images/Icons/apple-touch-icon-60x60.png"" /> | |
| 45 | + <link rel=""apple-touch-icon-precomposed"" sizes=""120x120"" href=""{Root}/Shared/Images/Icons/apple-touch-icon-120x120.png"" /> | |
| 46 | + <link rel=""apple-touch-icon-precomposed"" sizes=""76x76"" href=""{Root}/Shared/Images/Icons/apple-touch-icon-76x76.png"" /> | |
| 47 | + <link rel=""apple-touch-icon-precomposed"" sizes=""152x152"" href=""{Root}/Shared/Images/Icons/apple-touch-icon-152x152.png"" /> | |
| 48 | + <link rel=""icon"" type=""image/png"" href=""{Root}/Shared/Images/Icons/favicon-196x196.png"" sizes=""196x196"" /> | |
| 49 | + <link rel=""icon"" type=""image/png"" href=""{Root}/Shared/Images/Icons/favicon-96x96.png"" sizes=""96x96"" /> | |
| 50 | + <link rel=""icon"" type=""image/png"" href=""{Root}/Shared/Images/Icons/favicon-32x32.png"" sizes=""32x32"" /> | |
| 51 | + <link rel=""icon"" type=""image/png"" href=""{Root}/Shared/Images/Icons/favicon-16x16.png"" sizes=""16x16"" /> | |
| 52 | + <link rel=""icon"" type=""image/png"" href=""{Root}/Shared/Images/Icons/favicon-128.png"" sizes=""128x128"" /> | |
| 53 | + <link rel=""shortcut icon"" href=""{Root}/Shared/Images/Icons/favicon.ico"" /> | |
| 49 | 54 | <meta name=""msapplication-TileColor"" content=""#AABBAA"" /> |
| 50 | − <meta name=""msapplication-TileImage"" content=""/@GSF/Web/Shared/Images/Icons/mstile-144x144.png"" /> | |
| 51 | − <meta name=""msapplication-square70x70logo"" content=""/@GSF/Web/Shared/Images/Icons/mstile-70x70.png"" /> | |
| 52 | − <meta name=""msapplication-square150x150logo"" content=""/@GSF/Web/Shared/Images/Icons/mstile-150x150.png"" /> | |
| 53 | − <meta name=""msapplication-wide310x150logo"" content=""/@GSF/Web/Shared/Images/Icons/mstile-310x150.png"" /> | |
| 54 | − <meta name=""msapplication-square310x310logo"" content=""/@GSF/Web/Shared/Images/Icons/mstile-310x310.png""/> | |
| 55 | − ".FixForwardSpacing(); | |
| 55 | + <meta name=""msapplication-TileImage"" content=""{Root}/Shared/Images/Icons/mstile-144x144.png"" /> | |
| 56 | + <meta name=""msapplication-square70x70logo"" content=""{Root}/Shared/Images/Icons/mstile-70x70.png"" /> | |
| 57 | + <meta name=""msapplication-square150x150logo"" content=""{Root}/Shared/Images/Icons/mstile-150x150.png"" /> | |
| 58 | + <meta name=""msapplication-wide310x150logo"" content=""{Root}/Shared/Images/Icons/mstile-310x150.png"" /> | |
| 59 | + <meta name=""msapplication-square310x310logo"" content=""{Root}/Shared/Images/Icons/mstile-310x310.png""/> | |
| 60 | + ".FixForwardSpacing(0); | |
| 56 | 61 | } |
| 57 | 62 | } |
| @@ -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 | + | |
| 29 | +if (typeof jQuery === "undefined") { | |
| 30 | + throw new Error("gsf.web.client script requires jQuery - make sure jquery.js is loaded first"); | |
| 31 | +} | |
| 32 | + | |
| 33 | +const isIE = detectIE(); | |
| 34 | +var 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 | |
| 45 | +function notNull(value, nonNullValue) { | |
| 46 | + return value || (nonNullValue || ""); | |
| 47 | +} | |
| 48 | + | |
| 49 | +function 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 | + | |
| 77 | +function isNumber(val) { | |
| 78 | + return !isNaN(parseFloat(val)) && isFinite(val); | |
| 79 | +} | |
| 80 | + | |
| 81 | +function 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 | + | |
| 89 | +function 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 | + | |
| 97 | +function getBool(val) { | |
| 98 | + const num = +val; | |
| 99 | + return !isNaN(num) ? !!num : !!String(val).toLowerCase().replace(false, ""); | |
| 100 | +} | |
| 101 | + | |
| 102 | +function 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 | + | |
| 119 | +function 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 | |
| 146 | +Number.prototype.truncate = function() { | |
| 147 | + if (typeof Math.trunc != "function") | |
| 148 | + return parseInt(this.toString()); | |
| 149 | + | |
| 150 | + return Math.trunc(this); | |
| 151 | +} | |
| 152 | + | |
| 153 | +Number.prototype.padLeft = function(totalWidth, paddingChar) { | |
| 154 | + return this.truncate().toString().padLeft(totalWidth, paddingChar || "0"); | |
| 155 | +} | |
| 156 | + | |
| 157 | +Number.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. | |
| 166 | +function 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 | + | |
| 200 | +Array.prototype.joinKeyValuePairs = function(parameterDelimiter, keyValueDelimiter, startValueDelimiter, endValueDelimiter) { | |
| 201 | + return joinKeyValuePairs(this, parameterDelimiter, keyValueDelimiter, startValueDelimiter, endValueDelimiter); | |
| 202 | +}; | |
| 203 | + | |
| 204 | +Array.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 | + | |
| 223 | +if (!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 | |
| 265 | +function 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 | + | |
| 404 | +Dictionary.fromObservableDictionary = function(observableDictionary) { | |
| 405 | + const dictionary = new Dictionary(); | |
| 406 | + dictionary.pushAll(observableDictionary.toJSON()); | |
| 407 | + return dictionary; | |
| 408 | +} | |
| 409 | + | |
| 410 | +// String functions | |
| 411 | +function isEmpty(str) { | |
| 412 | + return !str || String(str).length === 0; | |
| 413 | +} | |
| 414 | + | |
| 415 | +String.prototype.trimLeft = String.prototype.trimLeft || function () { | |
| 416 | + return this.replace(/^\s+/,""); | |
| 417 | +} | |
| 418 | + | |
| 419 | +String.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 | + | |
| 428 | +String.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 | + | |
| 434 | +if (!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 | + | |
| 449 | +if (!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 | + | |
| 460 | +String.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 | + | |
| 467 | +String.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 | + | |
| 474 | +String.prototype.countOccurrences = function(searchString) { | |
| 475 | + return (this.split(searchString).length - 1); | |
| 476 | +} | |
| 477 | + | |
| 478 | +if (!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 | + | |
| 485 | +String.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. | |
| 491 | +String.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 | |
| 631 | +function 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 | |
| 655 | +Date.prototype.addDays = function(days) { | |
| 656 | + return new Date(this.setDate(this.getDate() + days)); | |
| 657 | +} | |
| 658 | + | |
| 659 | +Date.prototype.toUTC = function() { | |
| 660 | + this.setMinutes(this.getMinutes() - this.getTimezoneOffset()); | |
| 661 | + return this; | |
| 662 | +} | |
| 663 | + | |
| 664 | +Date.prototype.daysBetween = function(startDate) { | |
| 665 | + const millisecondsPerDay = 24 * 60 * 60 * 1000; | |
| 666 | + return (this.toUTC() - startDate.toUTC()) / millisecondsPerDay; | |
| 667 | +} | |
| 668 | + | |
| 669 | +String.prototype.toDate = function() { | |
| 670 | + return new Date(Date.parse(this)); | |
| 671 | +}; | |
| 672 | + | |
| 673 | +String.prototype.formatDate = function(format, utc) { | |
| 674 | + return formatDate(this.toDate(), format, utc); | |
| 675 | +}; | |
| 676 | + | |
| 677 | +Date.prototype.formatDate = function(format, utc) { | |
| 678 | + return formatDate(this, format, utc); | |
| 679 | +}; | |
| 680 | + | |
| 681 | +function 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 | +} | |
| @@ -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 | |
| 29 | +function 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 | + | |
| 48 | +function 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 | |
| 58 | +ko.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 | + | |
| 81 | +ko.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 | |
| 103 | +ko.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 | |
| 115 | +ko.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 | |
| 127 | +ko.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 | |
| 154 | +ko.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 | +}; | |
| @@ -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 | |
| 29 | +function 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 | |
| 34 | +function 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 | +} | |
| @@ -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 | +})); | |
| @@ -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= | |
| 5 | +n.length-1;0<=g;--g)t[n[g]]=n[g];l=[];n=void 0;for(n in t)l.push(t[n]);a[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, | |
| 6 | +j.observe);a.mappedProperties=a.mappedProperties||{};a.copiedProperties=a.copiedProperties||{};return a}function h(b,c){"array"!==f.getType(b)&&(b="undefined"===f.getType(b)?[]:[b]);"array"!==f.getType(c)&&(c="undefined"===f.getType(c)?[]:[c]);return 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 | |
| 7 | +Function},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= | |
| 8 | +H.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}; | |
| 9 | +a[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!= | |
| 10 | +d.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= | |
| 11 | +m(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]= | |
| 12 | +q;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"!= | |
| 13 | +typeof 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): | |
| 14 | +b=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 '"+ | |
| 15 | +c+"' 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."); | |
| 17 | +try{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."); | |
| 19 | +if("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=== | |
| 20 | +Date)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; | |
| 21 | +if(!(-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}}); | |
| @@ -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 | +// --------------------------------------------- | |
| 212 | +function isNumeric(n) { | |
| 213 | + return !isNaN(parseFloat(n)) && isFinite(n); | |
| 214 | +} | |
| 215 | + | |
| 216 | +function 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 | +// --------------------------------------------- | |
| @@ -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 | + | |
| 6 | +ko.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 | + | |
| 45 | +ko['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 | +}; | |