├── .gitignore
├── README.md
└── src
├── .vs
└── config
│ └── applicationhost.config
├── Vso.StateModelVisualization.sln
└── Vso.StateModelVisualization
├── App.html
├── License.txt
├── PrintGraph.html
├── Properties
└── AssemblyInfo.cs
├── StateDiagramDialog.html
├── StateDiagramWitButton.html
├── ThirdPartyNotice.txt
├── Vso.StateModelVisualization.csproj
├── Web.Debug.config
├── Web.Release.config
├── Web.config
├── css
└── app.css
├── images
├── Export.png
├── FitTo.png
├── Refresh.png
├── Screen1-small.png
├── Screen2-small.png
├── StateModelShortIcon-16x16.png
├── StateModelShortIcon.png
├── StateModelWideIcon.png
├── WVizDemo.png
├── ZoomZoomIn.png
└── ZoomZoomOut.png
├── overview.md
├── package.json
├── scripts
├── app
│ ├── AppInsightPageViewsTelemetry.js
│ ├── MainMenu.js
│ ├── PrintGraph.js
│ ├── StateModelGraph.js
│ ├── StateModelVisualization.js
│ └── TelemetryClient.js
├── cytoscape
│ ├── LGPL-LICENSE.txt
│ ├── cytoscape-dagre.js
│ ├── cytoscape.js
│ ├── cytoscape.min.js
│ └── dagre.js
└── lib
│ ├── VSS.SDK.js
│ ├── VSS.SDK.min.js
│ ├── ai.0.22.9-build00167.js
│ └── ai.0.22.9-build00167.min.js
├── vss-extension.internal.json
└── vss-extension.json
/.gitignore:
--------------------------------------------------------------------------------
1 | ## Ignore Visual Studio temporary files, build results, and
2 | ## files generated by popular Visual Studio add-ons.
3 |
4 | # User-specific files
5 | *.suo
6 | *.user
7 | *.userosscache
8 | *.sln.docstates
9 |
10 | # User-specific files (MonoDevelop/Xamarin Studio)
11 | *.userprefs
12 |
13 | # Build results
14 | [Dd]ebug/
15 | [Dd]ebugPublic/
16 | [Rr]elease/
17 | [Rr]eleases/
18 | x64/
19 | x86/
20 | build/
21 | bld/
22 | [Bb]in/
23 | [Oo]bj/
24 |
25 | # Visual Studio 2015 cache/options directory
26 | .vs/
27 |
28 | # MSTest test Results
29 | [Tt]est[Rr]esult*/
30 | [Bb]uild[Ll]og.*
31 |
32 | # NUNIT
33 | *.VisualState.xml
34 | TestResult.xml
35 |
36 | # Build Results of an ATL Project
37 | [Dd]ebugPS/
38 | [Rr]eleasePS/
39 | dlldata.c
40 |
41 | # DNX
42 | project.lock.json
43 | artifacts/
44 |
45 | *_i.c
46 | *_p.c
47 | *_i.h
48 | *.ilk
49 | *.meta
50 | *.obj
51 | *.pch
52 | *.pdb
53 | *.pgc
54 | *.pgd
55 | *.rsp
56 | *.sbr
57 | *.tlb
58 | *.tli
59 | *.tlh
60 | *.tmp
61 | *.tmp_proj
62 | *.log
63 | *.vspscc
64 | *.vssscc
65 | .builds
66 | *.pidb
67 | *.svclog
68 | *.scc
69 |
70 | # Chutzpah Test files
71 | _Chutzpah*
72 |
73 | # Visual C++ cache files
74 | ipch/
75 | *.aps
76 | *.ncb
77 | *.opensdf
78 | *.sdf
79 | *.cachefile
80 |
81 | # Visual Studio profiler
82 | *.psess
83 | *.vsp
84 | *.vspx
85 |
86 | # TFS 2012 Local Workspace
87 | $tf/
88 |
89 | # Guidance Automation Toolkit
90 | *.gpState
91 |
92 | # ReSharper is a .NET coding add-in
93 | _ReSharper*/
94 | *.[Rr]e[Ss]harper
95 | *.DotSettings.user
96 |
97 | # JustCode is a .NET coding add-in
98 | .JustCode
99 |
100 | # TeamCity is a build add-in
101 | _TeamCity*
102 |
103 | # DotCover is a Code Coverage Tool
104 | *.dotCover
105 |
106 | # NCrunch
107 | _NCrunch_*
108 | .*crunch*.local.xml
109 |
110 | # MightyMoose
111 | *.mm.*
112 | AutoTest.Net/
113 |
114 | # Web workbench (sass)
115 | .sass-cache/
116 |
117 | # Installshield output folder
118 | [Ee]xpress/
119 |
120 | # DocProject is a documentation generator add-in
121 | DocProject/buildhelp/
122 | DocProject/Help/*.HxT
123 | DocProject/Help/*.HxC
124 | DocProject/Help/*.hhc
125 | DocProject/Help/*.hhk
126 | DocProject/Help/*.hhp
127 | DocProject/Help/Html2
128 | DocProject/Help/html
129 |
130 | # Click-Once directory
131 | publish/
132 |
133 | # Publish Web Output
134 | *.[Pp]ublish.xml
135 | *.azurePubxml
136 | ## TODO: Comment the next line if you want to checkin your
137 | ## web deploy settings but do note that will include unencrypted
138 | ## passwords
139 | #*.pubxml
140 |
141 | *.publishproj
142 |
143 | # NuGet Packages
144 | *.nupkg
145 | # The packages folder can be ignored because of Package Restore
146 | **/packages/*
147 | # except build/, which is used as an MSBuild target.
148 | !**/packages/build/
149 | # Uncomment if necessary however generally it will be regenerated when needed
150 | #!**/packages/repositories.config
151 |
152 | # Windows Azure Build Output
153 | csx/
154 | *.build.csdef
155 |
156 | # Windows Store app package directory
157 | AppPackages/
158 |
159 | # Visual Studio cache files
160 | # files ending in .cache can be ignored
161 | *.[Cc]ache
162 | # but keep track of directories ending in .cache
163 | !*.[Cc]ache/
164 |
165 | # Others
166 | ClientBin/
167 | [Ss]tyle[Cc]op.*
168 | ~$*
169 | *~
170 | *.dbmdl
171 | *.dbproj.schemaview
172 | *.pfx
173 | *.publishsettings
174 | node_modules/
175 | orleans.codegen.cs
176 |
177 | # RIA/Silverlight projects
178 | Generated_Code/
179 |
180 | # Backup & report files from converting an old project file
181 | # to a newer Visual Studio version. Backup files are not needed,
182 | # because we have git ;-)
183 | _UpgradeReport_Files/
184 | Backup*/
185 | UpgradeLog*.XML
186 | UpgradeLog*.htm
187 |
188 | # SQL Server files
189 | *.mdf
190 | *.ldf
191 |
192 | # Business Intelligence projects
193 | *.rdl.data
194 | *.bim.layout
195 | *.bim_*.settings
196 |
197 | # Microsoft Fakes
198 | FakesAssemblies/
199 |
200 | # Node.js Tools for Visual Studio
201 | .ntvs_analysis.dat
202 |
203 | # Visual Studio 6 build log
204 | *.plg
205 |
206 | # Visual Studio 6 workspace options file
207 | *.opt
208 |
209 | # LightSwitch generated files
210 | GeneratedArtifacts/
211 | _Pvt_Extensions/
212 | ModelManifest.xml
213 | *.vsix
214 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Work Item State Model Visualization Extension for Visual Studio Team Services
2 |
3 | ## The Gist
4 | This VSTS extension is intended to offer users the ability to visualize the work item state model incl its transitions.
5 | ## Features
6 | - Visualize the work item state model with transitions
7 | - Export the visualization to image
8 | - Zoom in, Zoom out, Zoom to Fit, panning
9 |
10 | ## Roadmap
11 | - Show transition reasons, when API allows
12 | - Allow to jump to visualization from work item form or show it on work item form
13 |
14 | ## Extensibility API Features Used
15 | - MessageArea Control
16 | - TreeView Control
17 | - Work Item Tracking HttpClient
18 | - Core HttpClient
19 | - Toolbars and menus
20 |
--------------------------------------------------------------------------------
/src/Vso.StateModelVisualization.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio 14
4 | VisualStudioVersion = 14.0.22310.1
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Vso.StateModelVisualization", "Vso.StateModelVisualization\Vso.StateModelVisualization.csproj", "{63EE87F2-52C8-4BA1-8DF5-8845337EE750}"
7 | EndProject
8 | Global
9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
10 | Debug|Any CPU = Debug|Any CPU
11 | Release|Any CPU = Release|Any CPU
12 | EndGlobalSection
13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
14 | {63EE87F2-52C8-4BA1-8DF5-8845337EE750}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
15 | {63EE87F2-52C8-4BA1-8DF5-8845337EE750}.Debug|Any CPU.Build.0 = Debug|Any CPU
16 | {63EE87F2-52C8-4BA1-8DF5-8845337EE750}.Release|Any CPU.ActiveCfg = Release|Any CPU
17 | {63EE87F2-52C8-4BA1-8DF5-8845337EE750}.Release|Any CPU.Build.0 = Release|Any CPU
18 | EndGlobalSection
19 | GlobalSection(SolutionProperties) = preSolution
20 | HideSolutionNode = FALSE
21 | EndGlobalSection
22 | EndGlobal
23 |
--------------------------------------------------------------------------------
/src/Vso.StateModelVisualization/App.html:
--------------------------------------------------------------------------------
1 |
16 |
17 |
18 |
19 |
20 | State Diagram in State Visualizer
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
36 |
37 |
38 |
39 |
State Model Visualization
40 |
41 |
42 |
51 |
52 |
53 |
54 |
67 |
68 |
69 |
--------------------------------------------------------------------------------
/src/Vso.StateModelVisualization/License.txt:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/melborp/StateModelVisualization/2165dd00690464aba86e7733f0b53c802d397c4a/src/Vso.StateModelVisualization/License.txt
--------------------------------------------------------------------------------
/src/Vso.StateModelVisualization/PrintGraph.html:
--------------------------------------------------------------------------------
1 |
16 |
17 |
18 |
19 |
20 | Export State Diagram Visualization
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
33 |
44 |
45 |
46 |
--------------------------------------------------------------------------------
/src/Vso.StateModelVisualization/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.CompilerServices;
3 | using System.Runtime.InteropServices;
4 |
5 | // General Information about an assembly is controlled through the following
6 | // set of attributes. Change these attribute values to modify the information
7 | // associated with an assembly.
8 | [assembly: AssemblyTitle("WebApplication1")]
9 | [assembly: AssemblyDescription("")]
10 | [assembly: AssemblyConfiguration("")]
11 | [assembly: AssemblyCompany("")]
12 | [assembly: AssemblyProduct("WebApplication1")]
13 | [assembly: AssemblyCopyright("Copyright © 2015")]
14 | [assembly: AssemblyTrademark("")]
15 | [assembly: AssemblyCulture("")]
16 |
17 | // Setting ComVisible to false makes the types in this assembly not visible
18 | // to COM components. If you need to access a type in this assembly from
19 | // COM, set the ComVisible attribute to true on that type.
20 | [assembly: ComVisible(false)]
21 |
22 | // The following GUID is for the ID of the typelib if this project is exposed to COM
23 | [assembly: Guid("63ee87f2-52c8-4ba1-8df5-8845337ee750")]
24 |
25 | // Version information for an assembly consists of the following four values:
26 | //
27 | // Major Version
28 | // Minor Version
29 | // Build Number
30 | // Revision
31 | //
32 | // You can specify all the values or you can default the Revision and Build Numbers
33 | // by using the '*' as shown below:
34 | [assembly: AssemblyVersion("1.0.0.0")]
35 | [assembly: AssemblyFileVersion("1.0.0.0")]
36 |
--------------------------------------------------------------------------------
/src/Vso.StateModelVisualization/StateDiagramDialog.html:
--------------------------------------------------------------------------------
1 |
16 |
17 |
18 |
19 |
20 | State Diagram in Modal
21 |
22 |
23 |
24 |
25 |
26 |
27 |
43 |
56 |
57 |
58 |
59 |
60 |
61 |
--------------------------------------------------------------------------------
/src/Vso.StateModelVisualization/StateDiagramWitButton.html:
--------------------------------------------------------------------------------
1 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
66 |
67 |
68 |
--------------------------------------------------------------------------------
/src/Vso.StateModelVisualization/ThirdPartyNotice.txt:
--------------------------------------------------------------------------------
1 | THIRD-PARTY SOFTWARE NOTICES AND INFORMATION
2 | Do Not Translate or Localize
3 |
4 | This file is based on or incorporates material from the projects listed below (Third Party IP).
5 | The original copyright notice and the license under which Taavi Koosaar received such Third Party IP,
6 | are set forth below. Such licenses and notices are provided for informational purposes only.
7 | Taavi Koosaar licenses the Third Party IP to you under the licensing terms for the State Model Visualization Extension product.
8 | Taavi Koosaar reserves all other rights not expressly granted under this agreement, whether by implication, estoppel or otherwise.
9 |
10 | Provided for Informational Purposes Only
11 |
12 | ----------------------
13 |
14 | Dagre (https://github.com/cpettitt/dagre/)
15 |
16 | Copyright (c) 2012-2014 Chris Pettitt
17 |
18 | Permission is hereby granted, free of charge, to any person obtaining a copy
19 | of this software and associated documentation files (the "Software"), to deal
20 | in the Software without restriction, including without limitation the rights
21 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
22 | copies of the Software, and to permit persons to whom the Software is
23 | furnished to do so, subject to the following conditions:
24 |
25 | The above copyright notice and this permission notice shall be included in
26 | all copies or substantial portions of the Software.
27 |
28 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
29 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
30 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
31 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
32 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
33 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
34 | THE SOFTWARE.
35 |
36 | ----------------------
37 |
38 | Cytoscape.js (https://github.com/cytoscape/cytoscape.js)
39 |
40 | GNU LESSER GENERAL PUBLIC LICENSE
41 | Version 3, 29 June 2007
42 |
43 | Copyright (C) 2007 Free Software Foundation, Inc.
44 | Everyone is permitted to copy and distribute verbatim copies
45 | of this license document, but changing it is not allowed.
46 |
47 |
48 | This version of the GNU Lesser General Public License incorporates
49 | the terms and conditions of version 3 of the GNU General Public
50 | License, supplemented by the additional permissions listed below.
51 |
52 | 0. Additional Definitions.
53 |
54 | As used herein, "this License" refers to version 3 of the GNU Lesser
55 | General Public License, and the "GNU GPL" refers to version 3 of the GNU
56 | General Public License.
57 |
58 | "The Library" refers to a covered work governed by this License,
59 | other than an Application or a Combined Work as defined below.
60 |
61 | An "Application" is any work that makes use of an interface provided
62 | by the Library, but which is not otherwise based on the Library.
63 | Defining a subclass of a class defined by the Library is deemed a mode
64 | of using an interface provided by the Library.
65 |
66 | A "Combined Work" is a work produced by combining or linking an
67 | Application with the Library. The particular version of the Library
68 | with which the Combined Work was made is also called the "Linked
69 | Version".
70 |
71 | The "Minimal Corresponding Source" for a Combined Work means the
72 | Corresponding Source for the Combined Work, excluding any source code
73 | for portions of the Combined Work that, considered in isolation, are
74 | based on the Application, and not on the Linked Version.
75 |
76 | The "Corresponding Application Code" for a Combined Work means the
77 | object code and/or source code for the Application, including any data
78 | and utility programs needed for reproducing the Combined Work from the
79 | Application, but excluding the System Libraries of the Combined Work.
80 |
81 | 1. Exception to Section 3 of the GNU GPL.
82 |
83 | You may convey a covered work under sections 3 and 4 of this License
84 | without being bound by section 3 of the GNU GPL.
85 |
86 | 2. Conveying Modified Versions.
87 |
88 | If you modify a copy of the Library, and, in your modifications, a
89 | facility refers to a function or data to be supplied by an Application
90 | that uses the facility (other than as an argument passed when the
91 | facility is invoked), then you may convey a copy of the modified
92 | version:
93 |
94 | a) under this License, provided that you make a good faith effort to
95 | ensure that, in the event an Application does not supply the
96 | function or data, the facility still operates, and performs
97 | whatever part of its purpose remains meaningful, or
98 |
99 | b) under the GNU GPL, with none of the additional permissions of
100 | this License applicable to that copy.
101 |
102 | 3. Object Code Incorporating Material from Library Header Files.
103 |
104 | The object code form of an Application may incorporate material from
105 | a header file that is part of the Library. You may convey such object
106 | code under terms of your choice, provided that, if the incorporated
107 | material is not limited to numerical parameters, data structure
108 | layouts and accessors, or small macros, inline functions and templates
109 | (ten or fewer lines in length), you do both of the following:
110 |
111 | a) Give prominent notice with each copy of the object code that the
112 | Library is used in it and that the Library and its use are
113 | covered by this License.
114 |
115 | b) Accompany the object code with a copy of the GNU GPL and this license
116 | document.
117 |
118 | 4. Combined Works.
119 |
120 | You may convey a Combined Work under terms of your choice that,
121 | taken together, effectively do not restrict modification of the
122 | portions of the Library contained in the Combined Work and reverse
123 | engineering for debugging such modifications, if you also do each of
124 | the following:
125 |
126 | a) Give prominent notice with each copy of the Combined Work that
127 | the Library is used in it and that the Library and its use are
128 | covered by this License.
129 |
130 | b) Accompany the Combined Work with a copy of the GNU GPL and this license
131 | document.
132 |
133 | c) For a Combined Work that displays copyright notices during
134 | execution, include the copyright notice for the Library among
135 | these notices, as well as a reference directing the user to the
136 | copies of the GNU GPL and this license document.
137 |
138 | d) Do one of the following:
139 |
140 | 0) Convey the Minimal Corresponding Source under the terms of this
141 | License, and the Corresponding Application Code in a form
142 | suitable for, and under terms that permit, the user to
143 | recombine or relink the Application with a modified version of
144 | the Linked Version to produce a modified Combined Work, in the
145 | manner specified by section 6 of the GNU GPL for conveying
146 | Corresponding Source.
147 |
148 | 1) Use a suitable shared library mechanism for linking with the
149 | Library. A suitable mechanism is one that (a) uses at run time
150 | a copy of the Library already present on the user's computer
151 | system, and (b) will operate properly with a modified version
152 | of the Library that is interface-compatible with the Linked
153 | Version.
154 |
155 | e) Provide Installation Information, but only if you would otherwise
156 | be required to provide such information under section 6 of the
157 | GNU GPL, and only to the extent that such information is
158 | necessary to install and execute a modified version of the
159 | Combined Work produced by recombining or relinking the
160 | Application with a modified version of the Linked Version. (If
161 | you use option 4d0, the Installation Information must accompany
162 | the Minimal Corresponding Source and Corresponding Application
163 | Code. If you use option 4d1, you must provide the Installation
164 | Information in the manner specified by section 6 of the GNU GPL
165 | for conveying Corresponding Source.)
166 |
167 | 5. Combined Libraries.
168 |
169 | You may place library facilities that are a work based on the
170 | Library side by side in a single library together with other library
171 | facilities that are not Applications and are not covered by this
172 | License, and convey such a combined library under terms of your
173 | choice, if you do both of the following:
174 |
175 | a) Accompany the combined library with a copy of the same work based
176 | on the Library, uncombined with any other library facilities,
177 | conveyed under the terms of this License.
178 |
179 | b) Give prominent notice with the combined library that part of it
180 | is a work based on the Library, and explaining where to find the
181 | accompanying uncombined form of the same work.
182 |
183 | 6. Revised Versions of the GNU Lesser General Public License.
184 |
185 | The Free Software Foundation may publish revised and/or new versions
186 | of the GNU Lesser General Public License from time to time. Such new
187 | versions will be similar in spirit to the present version, but may
188 | differ in detail to address new problems or concerns.
189 |
190 | Each version is given a distinguishing version number. If the
191 | Library as you received it specifies that a certain numbered version
192 | of the GNU Lesser General Public License "or any later version"
193 | applies to it, you have the option of following the terms and
194 | conditions either of that published version or of any later version
195 | published by the Free Software Foundation. If the Library as you
196 | received it does not specify a version number of the GNU Lesser
197 | General Public License, you may choose any version of the GNU Lesser
198 | General Public License ever published by the Free Software Foundation.
199 |
200 | If the Library as you received it specifies that a proxy can decide
201 | whether future versions of the GNU Lesser General Public License shall
202 | apply, that proxy's public statement of acceptance of any version is
203 | permanent authorization for you to choose that version for the
204 | Library.
205 |
206 | ----------------------
207 |
208 |
209 |
--------------------------------------------------------------------------------
/src/Vso.StateModelVisualization/Vso.StateModelVisualization.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | AnyCPU
7 |
8 |
9 | 2.0
10 | {63EE87F2-52C8-4BA1-8DF5-8845337EE750}
11 | {349c5851-65df-11da-9384-00065b846f21};{fae04ec0-301f-11d3-bf4b-00c04f79efbc}
12 | Library
13 | Properties
14 | WebApplication1
15 | WebApplication1
16 | v4.5
17 | true
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 | true
26 | full
27 | false
28 | bin\
29 | DEBUG;TRACE
30 | prompt
31 | 4
32 |
33 |
34 | pdbonly
35 | true
36 | bin\
37 | TRACE
38 | prompt
39 | 4
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 | Web.config
87 |
88 |
89 | Web.config
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 | 10.0
114 | $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 | True
124 | True
125 | 57453
126 | /
127 | http://localhost:57453/
128 | False
129 | False
130 |
131 |
132 | False
133 |
134 |
135 |
136 |
137 |
144 |
--------------------------------------------------------------------------------
/src/Vso.StateModelVisualization/Web.Debug.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
17 |
18 |
29 |
30 |
--------------------------------------------------------------------------------
/src/Vso.StateModelVisualization/Web.Release.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
17 |
18 |
19 |
30 |
31 |
--------------------------------------------------------------------------------
/src/Vso.StateModelVisualization/Web.config:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/src/Vso.StateModelVisualization/css/app.css:
--------------------------------------------------------------------------------
1 | /*---------------------------------------------------------------------
2 | //
3 | // This code is licensed under the MIT License.
4 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
5 | // ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
6 | // TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
7 | // PARTICULAR PURPOSE AND NONINFRINGEMENT.
8 | //
9 | //
10 | // Part of the State Model Visualization VSTS extension.
11 | // This file defines CSS styling for the application.
12 | //
13 | //---------------------------------------------------------------------*/
14 |
15 | .icon-zoom-100-smv {
16 | background-image: url("../images/Refresh.png");
17 | background-repeat: no-repeat !important;
18 | background-size: 16px 16px;
19 | }
20 |
21 | .icon-zoom-out-smv {
22 | background-image: url("../images/ZoomZoomOut.png");
23 | background-repeat: no-repeat !important;
24 | background-size: 16px 16px;
25 | }
26 |
27 | .icon-zoom-in-smv {
28 | background-image: url("../images/ZoomZoomIn.png");
29 | background-repeat: no-repeat !important;
30 | background-size: 16px 16px;
31 | }
32 |
33 | .icon-fit-to-smv {
34 | background-image: url("../images/FitTo.png");
35 | background-repeat: no-repeat !important;
36 | background-size: 16px 16px;
37 | }
38 |
39 | .icon-export-smv {
40 | background-image: url("../images/Export.png");
41 | background-repeat: no-repeat !important;
42 | background-size: 16px 16px;
43 | }
44 |
45 | #cy {
46 | height: 95%;
47 | width: 100%;
48 | position: absolute;
49 | left: 0;
50 | /*top: 100px;*/
51 | }
52 |
--------------------------------------------------------------------------------
/src/Vso.StateModelVisualization/images/Export.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/melborp/StateModelVisualization/2165dd00690464aba86e7733f0b53c802d397c4a/src/Vso.StateModelVisualization/images/Export.png
--------------------------------------------------------------------------------
/src/Vso.StateModelVisualization/images/FitTo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/melborp/StateModelVisualization/2165dd00690464aba86e7733f0b53c802d397c4a/src/Vso.StateModelVisualization/images/FitTo.png
--------------------------------------------------------------------------------
/src/Vso.StateModelVisualization/images/Refresh.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/melborp/StateModelVisualization/2165dd00690464aba86e7733f0b53c802d397c4a/src/Vso.StateModelVisualization/images/Refresh.png
--------------------------------------------------------------------------------
/src/Vso.StateModelVisualization/images/Screen1-small.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/melborp/StateModelVisualization/2165dd00690464aba86e7733f0b53c802d397c4a/src/Vso.StateModelVisualization/images/Screen1-small.png
--------------------------------------------------------------------------------
/src/Vso.StateModelVisualization/images/Screen2-small.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/melborp/StateModelVisualization/2165dd00690464aba86e7733f0b53c802d397c4a/src/Vso.StateModelVisualization/images/Screen2-small.png
--------------------------------------------------------------------------------
/src/Vso.StateModelVisualization/images/StateModelShortIcon-16x16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/melborp/StateModelVisualization/2165dd00690464aba86e7733f0b53c802d397c4a/src/Vso.StateModelVisualization/images/StateModelShortIcon-16x16.png
--------------------------------------------------------------------------------
/src/Vso.StateModelVisualization/images/StateModelShortIcon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/melborp/StateModelVisualization/2165dd00690464aba86e7733f0b53c802d397c4a/src/Vso.StateModelVisualization/images/StateModelShortIcon.png
--------------------------------------------------------------------------------
/src/Vso.StateModelVisualization/images/StateModelWideIcon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/melborp/StateModelVisualization/2165dd00690464aba86e7733f0b53c802d397c4a/src/Vso.StateModelVisualization/images/StateModelWideIcon.png
--------------------------------------------------------------------------------
/src/Vso.StateModelVisualization/images/WVizDemo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/melborp/StateModelVisualization/2165dd00690464aba86e7733f0b53c802d397c4a/src/Vso.StateModelVisualization/images/WVizDemo.png
--------------------------------------------------------------------------------
/src/Vso.StateModelVisualization/images/ZoomZoomIn.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/melborp/StateModelVisualization/2165dd00690464aba86e7733f0b53c802d397c4a/src/Vso.StateModelVisualization/images/ZoomZoomIn.png
--------------------------------------------------------------------------------
/src/Vso.StateModelVisualization/images/ZoomZoomOut.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/melborp/StateModelVisualization/2165dd00690464aba86e7733f0b53c802d397c4a/src/Vso.StateModelVisualization/images/ZoomZoomOut.png
--------------------------------------------------------------------------------
/src/Vso.StateModelVisualization/overview.md:
--------------------------------------------------------------------------------
1 | ## Visualize work item type states and transitions##
2 |
3 | [](https://channel9.msdn.com/Series/Visual-Studio-ALM-Rangers-Demos/VS-Team-Services-State-Model-Visualization-Extension) Every work item type in Visual Studio Team Services has a states, transitions and reasons defined. With this extension you can visualize those states and transitions for regular and hidden work item types.
4 |
5 | 
6 |
7 | ### Export for offline viewing ###
8 |
9 | Export your chart visualization for offline viewing or printing.
10 |
11 | 
12 |
13 | ## Quick steps to get started ##
14 |
15 | - **Visualize**
16 | 1. Navigate to your team project.
17 | 1. Select **WORK** hub group.
18 | 1. Navigate to a **State Visualizer** hub.
19 | 1. Use the left tree view to select work item type and the states and transitions are visualized on the right side.
20 | 1. Click on `Zoom In`, `Zoom Out`, `Zoom to original size` or `Fit To icons` on the toolbar to re-size.
21 | - **State Diagram from Work Item**
22 | 1. Open any work item
23 | - If you are using the classic WI item form, select `State Diagram` on the toolbar.
24 | - Otherwise click on `...` and select `State Diagram`.
25 | - State Diagram Visualization dialog will open up for the selected work item type.
26 | - **Export**
27 | 1. Export the visualization in any browser.
28 |
29 |
30 | ## Planned features ##
31 |
32 | - Showing reasons for transitions.
33 |
34 |
35 | ## Feedback ##
36 |
37 | If you like this extension, please leave a review and feedback. If you'd have suggestions or an issue, please [file an issue to give me a chance to fix it](https://github.com/melborp/StateModelVisualization/issues).
38 |
39 | ## Release History ##
40 |
41 | ### v1.3 ###
42 |
43 | - **Bug fixes**
44 |
45 | 1. Fixed issue with exporting visualization on TFS on-premis (#12)
46 | 1. Fixed issue where Fit To Screen didnt quite fit (#11)
47 | 1. Moved extension to Plan and Track category, added application insight, upgraded VSS SDK (#14, #10, #8)
48 | 1. Fixed script paths to be same everywhere (case sensitive in CDN) (#7)
49 |
50 | ## Learn more ##
51 |
52 | The source to this extension is available on GitHub: [StateModelVisualization](https://github.com/melborp/StateModelVisualization).
53 |
54 | To learn more about developing an extension for Visual Studio Team Services, see the [overview of extensions](https://www.visualstudio.com/en-us/integrate/extensions/overview).
55 |
56 | [Third Party Notice](https://marketplace.visualstudio.com/_apis/public/gallery/publisher/taavi-koosaar/extension/StateModelVisualization/latest/assetbyname/ThirdPartyNotice.txt).
57 |
--------------------------------------------------------------------------------
/src/Vso.StateModelVisualization/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": "1.0.0",
3 | "name": "VstsStateModelVisualization",
4 | "private": true,
5 | "devDependencies": {
6 | "vss-web-extension-sdk": "~1.102.0"
7 | }
8 | }
--------------------------------------------------------------------------------
/src/Vso.StateModelVisualization/scripts/app/AppInsightPageViewsTelemetry.js:
--------------------------------------------------------------------------------
1 | var appInsights=window.appInsights||function(config){ function r(config){t[config]=function(){var i=arguments;t.queue.push(function(){t[config].apply(t,i)})}}var t={config:config},u=document,e=window,o="script",s=u.createElement(o),i,f;s.src=config.url||"//az416426.vo.msecnd.net/scripts/a/ai.0.js";u.getElementsByTagName(o)[0].parentNode.appendChild(s);try{t.cookie=u.cookie}catch(h){}for(t.queue=[],i=["Event","Exception","Metric","PageView","Trace","Dependency"];i.length;)r("track"+i.pop());return r("setAuthenticatedUserContext"),r("clearAuthenticatedUserContext"),config.disableExceptionTracking||(i="onerror",r("_"+i),f=e[i],e[i]=function(config,r,u,e,o){var s=f&&f(config,r,u,e,o);return s!==!0&&t["_"+i](config,r,u,e,o),s}),t }({ instrumentationKey:"43786d2b-25eb-4eaf-96c7-ec7f5ccba5a0" }); window.appInsights=appInsights; appInsights.trackPageView();
--------------------------------------------------------------------------------
/src/Vso.StateModelVisualization/scripts/app/MainMenu.js:
--------------------------------------------------------------------------------
1 | /*---------------------------------------------------------------------
2 | //
3 | // This code is licensed under the MIT License.
4 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
5 | // ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
6 | // TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
7 | // PARTICULAR PURPOSE AND NONINFRINGEMENT.
8 | //
9 | //
10 | // Part of the State Model Visualization VSTS extension.
11 | // This class defines the MainMenu creation in the VSTS Hub.
12 | //
13 | //---------------------------------------------------------------------*/
14 |
15 | var __extends = (this && this.__extends) || function (d, b) {
16 | for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
17 | function __() { this.constructor = d; }
18 | d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
19 | };
20 |
21 | define(["require", "exports", "VSS/Utils/Core",
22 | "VSS/Controls", "VSS/Controls/Menus", "scripts/app/StateModelGraph", "scripts/app/TelemetryClient"],
23 | function (require, exports, Core, Controls, MenuControls, StateModelGraph, TelemetryClient) {
24 |
25 | var ItemsView = (function (_super) {
26 | __extends(ItemsView, _super);
27 |
28 | function ItemsView(options) {
29 | _super.call(this, options);
30 | this._menu = null;
31 | this._graph = StateModelGraph.graph;
32 | }
33 |
34 |
35 | /*
36 | * Initialize will be called when this control is created. This will setup the UI,
37 | * attach to events, etc.
38 | */
39 | ItemsView.prototype.initialize = function () {
40 | _super.prototype.initialize.call(this);
41 |
42 | this._createToolbar();
43 | };
44 |
45 | ItemsView.prototype._createToolbar = function () {
46 | this._menu = Controls.BaseControl.createIn(MenuControls.MenuBar, this._element.find(".hub-pivot-toolbar"), {
47 | items: this._createToolbarItems()
48 | });
49 | MenuControls.menuManager.attachExecuteCommand(Core.delegate(this, this._onToolbarItemClick));
50 | };
51 |
52 | /*
53 | * Create the actual toolbar items
54 | */
55 | ItemsView.prototype._createToolbarItems = function () {
56 | var items = [];
57 |
58 | items.push({ id: "zoom-in", text: "Zoom In", title: "Zoom In", showText: false, icon: "icon-zoom-in-smv", disabled: true });
59 | items.push({ id: "zoom-out", text: "Zoom Out", title: "Zoom Out", showText: false, icon: "icon-zoom-out-smv", disabled: true });
60 | items.push({ id: "zoom-100", text: "Zoom 100%", title: "Zoom to 100%", showText: false, icon: "icon-zoom-100-smv", disabled: true });
61 | items.push({ id: "fit-to", text: "Fit to screen", title: "Fit to screen", showText: false, icon: "icon-fit-to-smv", disabled: true });
62 |
63 | items.push({ separator: true });
64 |
65 | items.push({ id: "export-graph", text: "Export Graph", title: "Export Graph", showText: false, icon: "icon-export-smv", disabled: true });
66 |
67 | return items;
68 | };
69 |
70 | /*
71 | * Fit the graph to the current window size
72 | */
73 | ItemsView.prototype._fitTo = function () {
74 | TelemetryClient.getClient().trackEvent("Menu.FitTo");
75 | if (!$("#fit-to").hasClass("disabled")) {
76 | this._graph.fitTo();
77 | }
78 | };
79 |
80 | /*
81 | * Zoom the diagram in one unit
82 | */
83 | ItemsView.prototype._zoomIn = function () {
84 | TelemetryClient.getClient().trackEvent("Menu.ZoomIn");
85 | if (!$("#zoom-in").hasClass("disabled")) {
86 | this._graph.zoomIn();
87 | }
88 | };
89 |
90 | /*
91 | * Zoom the diagram out one unit
92 | */
93 | ItemsView.prototype._zoomOut = function () {
94 | TelemetryClient.getClient().trackEvent("Menu.ZoomOut");
95 | if (!$("#zoom-out").hasClass("disabled")) {
96 | this._graph.zoomOut();
97 | }
98 | };
99 |
100 | /*
101 | * Set the diagram to 100%
102 | */
103 | ItemsView.prototype._zoom100 = function () {
104 | TelemetryClient.getClient().trackEvent("Menu.ZoomTo100");
105 | if (!$("#zoom-100").hasClass("disabled")) {
106 | this._graph.zoomTo100();
107 | }
108 | };
109 |
110 | /*
111 | * Handle a button click on the toolbar
112 | */
113 | ItemsView.prototype._onToolbarItemClick = function (sender, args) {
114 | var command = args.get_commandName(), commandArgument = args.get_commandArgument(), that = this, result = false;
115 | switch (command) {
116 | case "zoom-in":
117 | this._zoomIn();
118 | break;
119 | case "zoom-out":
120 | this._zoomOut();
121 | break;
122 | case "zoom-100":
123 | this._zoom100();
124 | break;
125 | case "fit-to":
126 | this._fitTo();
127 | break;
128 | case "export-graph":
129 | this._exportGraph();
130 | break;
131 | default:
132 | result = true;
133 | break;
134 | }
135 | return result;
136 | };
137 |
138 | ItemsView.prototype._exportGraph = function () {
139 | TelemetryClient.getClient().trackEvent("Menu.ExportGraph");
140 | var png = this._graph.exportImage();
141 | var self = this;
142 |
143 | VSS.getService(VSS.ServiceIds.Dialog).then(function (dlg) {
144 | var printGraphDialog;
145 |
146 | //TODO: later make dialog same size as window and offer full screen option
147 | var opts = {
148 | width: window.screen.width,
149 | height: window.screen.height,
150 | title: "Export State Diagram Visualization",
151 | buttons: null
152 | };
153 |
154 | dlg.openDialog(VSS.getExtensionContext().publisherId + "." + VSS.getExtensionContext().extensionId + ".state-diagram-visualization-print-graph-dialog", opts).then(function (dialog) {
155 | dialog.getContributionInstance("state-diagram-visualization-print-graph-dialog").then(function (ci) {
156 | printGraphDialog = ci;
157 | printGraphDialog.start(png, self._graph.currentWitType);
158 | }, function (err) {
159 | alert(err.message);
160 | });
161 | });
162 | });
163 | };
164 |
165 | /*
166 | * Enables the toolbar menu items - to be called after the work item diagram is set initially
167 | */
168 | ItemsView.prototype.EnableToolbar = function() {
169 | this._menu.updateCommandStates([
170 | { id: "zoom-in", disabled: false },
171 | { id: "zoom-out", disabled: false },
172 | { id: "zoom-100", disabled: false },
173 | { id: "fit-to", disabled: false },
174 | { id: "export-graph", disabled: false }
175 | ]);
176 | }
177 |
178 |
179 | return ItemsView;
180 | })(Controls.BaseControl);
181 | exports.ItemsView = ItemsView;
182 |
183 | Controls.Enhancement.registerEnhancement(ItemsView, ".hub-view");
184 | });
--------------------------------------------------------------------------------
/src/Vso.StateModelVisualization/scripts/app/PrintGraph.js:
--------------------------------------------------------------------------------
1 | /*---------------------------------------------------------------------
2 | //
3 | // This code is licensed under the MIT License.
4 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
5 | // ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
6 | // TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
7 | // PARTICULAR PURPOSE AND NONINFRINGEMENT.
8 | //
9 | //
10 | // Part of the State Model Visualization VSO extension by the
11 | // ALM Rangers. The application logic for finding work item by id dialog view.
12 | //
13 | //---------------------------------------------------------------------*/
14 |
15 | var __extends = (this && this.__extends) || function (d, b) {
16 | for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
17 | function __() { this.constructor = d; }
18 | d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
19 | };
20 | define(
21 | ["require", "exports", "VSS/Controls/Dialogs"],
22 | function (require, exports, Dialogs) {
23 | var PrintGraph = (function (_super) {
24 | __extends(PrintGraph, _super);
25 | function PrintGraph(context) {
26 | _super.call(this);
27 | var self = this;
28 | self.context = context;
29 | }
30 | PrintGraph.prototype.start = function (img, witType) {
31 | var self = this;
32 |
33 | var d = new Date();
34 |
35 | $("#printTitle").text("Visualization of " + witType);
36 | $("#printDateTime").text("Generated " + d.toLocaleDateString());
37 | $("#graphImage").attr("src",img);
38 | };
39 |
40 | return PrintGraph;
41 | })(Dialogs.ModalDialog);
42 | exports.PrintGraph = PrintGraph;
43 | });
--------------------------------------------------------------------------------
/src/Vso.StateModelVisualization/scripts/app/StateModelGraph.js:
--------------------------------------------------------------------------------
1 | /*---------------------------------------------------------------------
2 | //
3 | // This code is licensed under the MIT License.
4 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
5 | // ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
6 | // TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
7 | // PARTICULAR PURPOSE AND NONINFRINGEMENT.
8 | //
9 | //
10 | // Part of the State Model Visualization VSTS extension.
11 | // This file defines the State Model Graph logic tied to Cytoscape.js.
12 | //
13 | //---------------------------------------------------------------------*/
14 |
15 | define(["require", "exports", "scripts/cytoscape/cytoscape", "scripts/cytoscape/dagre", "scripts/cytoscape/cytoscape-dagre", "scripts/app/TelemetryClient"], function (require, exports, cytoscape, dagre, cydagre, TelemetryClient) {
16 | var StateModelGraph = (function() {
17 |
18 | var zoomStepSize = 0.1;
19 | var zoom100 = 1;
20 |
21 | function StateModelGraph(container, cytoscape) {
22 | this.container = container;
23 | this.cy = null;
24 | this.currentWitType = "";
25 | this.cytoscape = cytoscape;
26 | cydagre(cytoscape, dagre);
27 | }
28 |
29 | StateModelGraph.prototype.create = function(witTypeName, callback, callbackData) {
30 | var self = this;
31 | self.currentWitType = witTypeName;
32 | self.cytoscape({
33 | container : self.container[0],
34 | style: self.cytoscape.stylesheet()
35 | .selector('node')
36 | .css({
37 | 'content': 'data(name)',
38 | 'text-valign': 'center',
39 | 'text-wrap':'wrap',
40 | 'color': 'black',
41 | 'width': '75px',
42 | 'height': '75px',
43 | 'background-color': 'rgba(254,199,0,1)',
44 | 'border-color': 'black',
45 | 'border-width': '0.25px',
46 | 'font-size': '12px',
47 | 'font-family': 'Segoe UI,Tahoma,Arial,Verdana'
48 | })
49 | .selector('node#Initial')
50 | .css({
51 | 'width': '8px',
52 | 'height': '8px',
53 | 'background-color': '#000000',
54 | 'border-color': 'black',
55 | 'border-width': '0.25px',
56 | })
57 | .selector('edge')
58 | .css({
59 | 'target-arrow-shape': 'triangle'
60 | }),
61 |
62 | layout: {
63 | name: 'dagre',
64 | fit: false,
65 | rankDir: 'TB'
66 | },
67 |
68 | // on graph initial layout done (could be async depending on layout...)
69 | ready: function() {
70 | window.cy = this;
71 | self.cy = this;
72 |
73 | // giddy up...
74 | cy.zoom(zoom100);
75 | cy.center();
76 |
77 | cy.minZoom(zoomStepSize);
78 | cy.maxZoom(10);
79 | cy.elements().unselectify();
80 | cy.userZoomingEnabled(false);
81 |
82 | callback(callbackData);
83 | }
84 | });
85 | }
86 |
87 | StateModelGraph.prototype.fitTo = function () {
88 | this.cy.fit();
89 | }
90 |
91 | StateModelGraph.prototype.zoomIn = function () {
92 | var currentZoom = this.cy.zoom();
93 | this.cy.zoom(currentZoom + zoomStepSize);
94 | //this.cy.center();
95 | }
96 |
97 | StateModelGraph.prototype.zoomOut = function () {
98 | var currentZoom = this.cy.zoom();
99 | this.cy.zoom(currentZoom - zoomStepSize);
100 | //this.cy.center();
101 | }
102 |
103 | StateModelGraph.prototype.zoomTo100 = function () {
104 | this.cy.zoom(zoom100);
105 | //this.cy.center();
106 | }
107 | StateModelGraph.prototype.exportImage = function () {
108 | var self = this;
109 | var png64 = self.cy.png({ full: true, scale : 1});
110 | return png64;
111 | }
112 | StateModelGraph.prototype.resize = function () {
113 | var self = this;
114 | if (self.cy == null)
115 | return;
116 | self.cy.resize();
117 | }
118 |
119 | StateModelGraph.prototype.getTestGrahpData = function() {
120 | var data = {
121 | nodes: [
122 | { data: { id: 'Initial', name: '' }, position: { x: 150, y: 0 } },
123 | { data: { id: 'a', name: 'To Do' }, position: { x: 150, y: 50 } },
124 | { data: { id: 'b', name: 'In Progress' }, position: { x: 75, y: 125 } },
125 | { data: { id: 'c', name: 'Done' }, position: { x: 150, y: 125 } },
126 | { data: { id: 'd', name: 'Removed' }, position: { x: 225, y: 125 } }
127 | ],
128 | edges: [
129 | { data: { source: 'Initial', target: 'a' } },
130 | { data: { source: 'a', target: 'b' } },
131 | { data: { source: 'a', target: 'c' } },
132 | { data: { source: 'a', target: 'd' } },
133 | { data: { source: 'b', target: 'a' } },
134 | { data: { source: 'b', target: 'c' } },
135 | { data: { source: 'b', target: 'd' } },
136 | { data: { source: 'c', target: 'a' } },
137 | { data: { source: 'c', target: 'b' } },
138 | { data: { source: 'd', target: 'a' } }
139 | ]
140 | };
141 | return data;
142 | }
143 |
144 | StateModelGraph.prototype.prepareVisualizationData = function(selectedWitName, allWits)
145 | {
146 | var selectedWit;
147 | //Find the selected wit
148 | for (var i = 0; i < allWits.length; i++) {
149 | if (allWits[i].name === selectedWitName) {
150 | selectedWit = allWits[i];
151 | }
152 | }
153 |
154 | if (selectedWit == undefined)
155 | return null;
156 |
157 | //Create nodes and edges for graph
158 | var states = new Array();
159 |
160 | var rank1Elements = new Array();
161 | var rank2Elements = new Array();
162 | var rank3Elements = new Array();
163 |
164 | var rankedArray = new Array();
165 |
166 | for (state in selectedWit.transitions) {
167 | states.push(state);
168 | }
169 |
170 | var initialNodeId = 'Initial';
171 | var initialNodeTargetId;
172 | var nodes = new Array();
173 | for (state in selectedWit.transitions) {
174 | var newNode;
175 | if (state === "") {
176 | newNode = { group: 'nodes', data: { id: initialNodeId, name: '' } };
177 | } else {
178 | newNode = { group: 'nodes', data: { id: state, name: state } };
179 | }
180 |
181 | nodes.push(newNode);
182 |
183 | var transitions = selectedWit.transitions[state];
184 |
185 | for (var j = 0; j < transitions.length; j++) {
186 | if (newNode.data.id === transitions[j].to)
187 | continue;
188 | //Check if the TO state is the State array, sometimes there's TO transitions into states that dont exist. not sure why.
189 | if ($.inArray(transitions[j].to, states) === -1)
190 | continue;
191 |
192 | var edge = { group: 'edges', data: { id: newNode.data.id + "-" + transitions[j].to, source: newNode.data.id, target: transitions[j].to } };
193 |
194 | if (newNode.data.id === initialNodeId) {
195 | initialNodeTargetId = transitions[j].to;
196 | rank2Elements.push(edge);//the edge connected to initial is ranked 2
197 | } else {
198 | rank3Elements.push(edge);//the other edges are ranked 3
199 | }
200 | }
201 | }
202 |
203 | //rank nodes
204 | nodes.forEach(function(node) {
205 | if (node.data.id === "Initial") {
206 | rank1Elements.push(node);
207 | }
208 | else if (initialNodeTargetId === node.data.id) {//the node connected to initial is ranked 2
209 | rank2Elements.push(node);
210 | } else {
211 | rank3Elements.push(node); //the other nodes are ranked 3
212 | }
213 | });
214 |
215 | rankedArray.push(rank1Elements);
216 | rankedArray.push(rank2Elements);
217 | rankedArray.push(rank3Elements);
218 |
219 | return rankedArray;
220 | }
221 |
222 | StateModelGraph.prototype.addElements = function (elements) {
223 | var self = this;
224 | var newElements = self.cy.collection();
225 |
226 | if (elements.length > 0) {
227 | newElements = self.cy.add(elements);
228 | self.refreshLayout();
229 | }
230 | return newElements;
231 | }
232 |
233 | StateModelGraph.prototype.refreshLayout = function () {
234 | var self = this;
235 | self.cy.layout(
236 | {
237 | name: 'dagre',
238 | rankDir: 'TB',
239 | //padding: 50,
240 | fit: true,
241 | minLen: 2,
242 | stop: function () { self.zoomTo100(); }
243 | });
244 | }
245 |
246 | return StateModelGraph;
247 | })();
248 | exports.graph = new StateModelGraph($("#cy"), cytoscape);
249 | });
--------------------------------------------------------------------------------
/src/Vso.StateModelVisualization/scripts/app/StateModelVisualization.js:
--------------------------------------------------------------------------------
1 | /*---------------------------------------------------------------------
2 | //
3 | // This code is licensed under the MIT License.
4 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
5 | // ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
6 | // TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
7 | // PARTICULAR PURPOSE AND NONINFRINGEMENT.
8 | //
9 | //
10 | // Part of the State Model Visualization VSTS extension.
11 | // The main application flow and logic.
12 | //
13 | //---------------------------------------------------------------------*/
14 |
15 | define(["require", "exports", "VSS/Controls", "VSS/Controls/TreeView", "VSS/Controls/Menus", "VSS/Service", "TFS/WorkItemTracking/RestClient",
16 | "scripts/app/MainMenu", "scripts/app/StateModelGraph", "scripts/app/TelemetryClient"],
17 | function (require, exports, Controls, TreeView, Menus, VssService, TfsWitRest, MainMenu, StateModelGraph, TelemetryClient) {
18 | var StateModelVisualization = (function() {
19 |
20 | var treeView;
21 | var allWits;
22 | var allCategories;
23 | var graph;
24 | var mainMenu;
25 |
26 | function StateModelVisualization() {
27 | }
28 |
29 | StateModelVisualization.prototype.start = function() {
30 | var self = this;
31 | var context = VSS.getWebContext();
32 |
33 | mainMenu = Controls.Enhancement.enhance(MainMenu.ItemsView, $(".hub-view"), {});
34 | graph = StateModelGraph.graph;
35 |
36 | // Get a WIT client to make REST calls to VSTS
37 | var witClient = VssService.getCollectionClient(TfsWitRest.WorkItemTrackingHttpClient);
38 |
39 | var configuration = VSS.getConfiguration().action;
40 |
41 | var afterGraphReady = function (callbackData) {
42 | var data = graph.prepareVisualizationData(callbackData.name, allWits);
43 | for (var i = 0; i < data.length; i++) {
44 | graph.addElements(data[i]);
45 | }
46 |
47 | //activate toolbar
48 | mainMenu.EnableToolbar();
49 | //hookup resize with cytoscape
50 | $(window).on("resize", graph.resize);
51 | }
52 |
53 | if (configuration) {
54 | TelemetryClient.getClient().trackEvent("Visualize.FromWorkItemForm");
55 | witClient.getWorkItem(configuration.workItemId, ["System.Id", "System.Title", "System.WorkItemType"]).then(function (wit) {
56 | var witTypeName = wit.fields["System.WorkItemType"];
57 | witClient.getWorkItemType(context.project.name, witTypeName).then(function (witType) {
58 | allWits = new Array();
59 | allWits.push(witType);
60 |
61 | graph.create(witType.name, afterGraphReady, { name: witType.name });
62 | });
63 | });
64 | } else {
65 | TelemetryClient.getClient().trackEvent("Visualize.FromStateVisualizer");
66 | witClient.getWorkItemTypes(context.project.name).then(function(wits) {
67 | witClient.getWorkItemTypeCategory(context.project.name).then(function(categories) {
68 | allWits = wits;
69 | allCategories = categories;
70 |
71 | var hiddenCategory = categories.value.filter(function(category) { return category.referenceName === "Microsoft.HiddenCategory"; })[0];
72 |
73 | var treeContainer = $(".work-item-type-tree-container");
74 | var firstNode;
75 |
76 | function convertToTreeNodes(items) {
77 | var workItems = new TreeView.TreeNode("Work Items");
78 | workItems.expanded = true;
79 | var hiddenWorkItems = new TreeView.TreeNode("Hidden Work Items");
80 | hiddenWorkItems.expanded = true;
81 |
82 | items.forEach(function(item) {
83 | var node = new TreeView.TreeNode(item.name);
84 | //default root
85 | var root = workItems;
86 |
87 | //double loop, not the best, but simplest. 8 items in hidden category.
88 | for (var i = 0; i < hiddenCategory.workItemTypes.length; i++) {
89 | if (hiddenCategory.workItemTypes[i].name === item.name) {
90 | root = hiddenWorkItems;
91 | break;
92 | }
93 | }
94 |
95 | root.add(node);
96 | if (item.name === wits[0].name) {
97 | firstNode = node;
98 | }
99 | });
100 |
101 | var sortChildren = function(a, b) {
102 | if (a.text > b.text) {
103 | return 1;
104 | }
105 | if (a.text < b.text) {
106 | return -1;
107 | }
108 | // a must be equal to b
109 | return 0;
110 | };
111 |
112 | workItems.children.sort(sortChildren);
113 | hiddenWorkItems.children.sort(sortChildren);
114 |
115 | var result = new Array();
116 | result.push(workItems);
117 | result.push(hiddenWorkItems);
118 | return result;
119 | }
120 |
121 | var treeViewOptions = { nodes: convertToTreeNodes(wits) };
122 | treeView = Controls.create(TreeView.TreeView, treeContainer, treeViewOptions);
123 |
124 |
125 |
126 | // Attach selectionchanged event using a DOM element containing treeview
127 | treeContainer.bind("selectionchanged", function (e, args) {
128 | TelemetryClient.getClient().trackEvent("Visualization.SelectedWorkItemType");
129 | treeView.TreeNode = args.selectedNode;
130 | var selectedNode = args.selectedNode;
131 | if (selectedNode && selectedNode.text !== "Work Items" && selectedNode.text !== "Hidden Work Items") {
132 | graph.create(selectedNode.text, afterGraphReady, { name: selectedNode.text });
133 | }
134 | });
135 |
136 | //show first WIT as default
137 | graph.create(wits[0].name, afterGraphReady, { name: wits[0].name });
138 |
139 | //select treenode
140 | treeView.TreeNode = firstNode;
141 | });
142 | });
143 | }
144 | }
145 | return StateModelVisualization;
146 | })();
147 |
148 | exports.smv = new StateModelVisualization();
149 | });
--------------------------------------------------------------------------------
/src/Vso.StateModelVisualization/scripts/app/TelemetryClient.js:
--------------------------------------------------------------------------------
1 | ///
2 | ///
3 |
4 | define(["require", "exports", "scripts/lib/ai.0.22.9-build00167"],
5 | function (require, exports, Microsoft2) {
6 |
7 | var telemetryClient;
8 | function getClient(){
9 | if (!this.telemetryClient) {
10 | this.telemetryClient = new TelemetryClient();
11 | this.telemetryClient.Init();
12 | }
13 |
14 | return this.telemetryClient;
15 | }
16 |
17 | var TelemetryClient = (function () {
18 | function TelemetryClient() {
19 | this.appInsightsClient = null;//: Microsoft.ApplicationInsights.AppInsights;
20 | }
21 |
22 | TelemetryClient.prototype.Init= function() {
23 | try {
24 | var snippet = {
25 | config: {
26 | instrumentationKey: "43786d2b-25eb-4eaf-96c7-ec7f5ccba5a0",
27 | }
28 | };
29 | var x = VSS.getExtensionContext();
30 |
31 | var init = new Microsoft.ApplicationInsights.Initialization(snippet);
32 | this.appInsightsClient = init.loadAppInsights();
33 |
34 | var webContext = VSS.getWebContext();
35 | this.appInsightsClient.setAuthenticatedUserContext(webContext.user.id, webContext.collection.id);
36 | }
37 | catch (e) {
38 | this.appInsightsClient = null;
39 | console.log(e);
40 | }
41 | }
42 |
43 | TelemetryClient.prototype.startTrackPageView= function(name){ //?: string) {
44 | try {
45 | if (this.appInsightsClient != null) {
46 | this.appInsightsClient.startTrackPage(name);
47 | }
48 | }
49 | catch (e) {
50 | console.log(e);
51 | }
52 | }
53 |
54 | TelemetryClient.prototype.stopTrackPageView = function(name){ //?: string) {
55 | try {
56 | if (this.appInsightsClient != null) {
57 | this.appInsightsClient.stopTrackPage(name);
58 | }
59 | }
60 | catch (e) {
61 | console.log(e);
62 | }
63 | }
64 |
65 | TelemetryClient.prototype.trackPageView= function(name, url, properties, measurements, duration) {
66 | try {
67 | if (this.appInsightsClient != null) {
68 | this.appInsightsClient.trackPageView( name, url, properties, measurements, duration);
69 | }
70 | }
71 | catch (e) {
72 | console.log(e);
73 | }
74 | }
75 |
76 | TelemetryClient.prototype.trackEvent = function(name, properties, measurements) {
77 | try {
78 | if (this.appInsightsClient != null) {
79 | this.appInsightsClient.trackEvent( name, properties, measurements);
80 | this.appInsightsClient.flush();
81 | }
82 | }
83 | catch (e) {
84 | console.log(e);
85 | }
86 | }
87 |
88 | TelemetryClient.prototype.trackException = function(exception, handledAt, properties, measurements) {
89 | try {
90 | if (this.appInsightsClient != null) {
91 | this.appInsightsClient.trackException(exception, handledAt, properties, measurements);
92 | this.appInsightsClient.flush();
93 | }
94 | }
95 | catch (e) {
96 | console.log(e);
97 | }
98 | }
99 |
100 | TelemetryClient.prototype.trackMetric = function(name, average, sampleCount, min, max, propertiesObject) {
101 | try {
102 | if (this.appInsightsClient != null) {
103 | this.appInsightsClient.trackMetric( name, average, sampleCount, min, max, properties);
104 | this.appInsightsClient.flush();
105 | }
106 | }
107 | catch (e) {
108 | console.log(e);
109 | }
110 | }
111 |
112 | return TelemetryClient;
113 | })();
114 | exports.TelemetryClient = TelemetryClient;
115 | exports.getClient = getClient;
116 | });
117 |
--------------------------------------------------------------------------------
/src/Vso.StateModelVisualization/scripts/cytoscape/LGPL-LICENSE.txt:
--------------------------------------------------------------------------------
1 | GNU LESSER GENERAL PUBLIC LICENSE
2 | Version 3, 29 June 2007
3 |
4 | Copyright (C) 2007 Free Software Foundation, Inc.
5 | Everyone is permitted to copy and distribute verbatim copies
6 | of this license document, but changing it is not allowed.
7 |
8 |
9 | This version of the GNU Lesser General Public License incorporates
10 | the terms and conditions of version 3 of the GNU General Public
11 | License, supplemented by the additional permissions listed below.
12 |
13 | 0. Additional Definitions.
14 |
15 | As used herein, "this License" refers to version 3 of the GNU Lesser
16 | General Public License, and the "GNU GPL" refers to version 3 of the GNU
17 | General Public License.
18 |
19 | "The Library" refers to a covered work governed by this License,
20 | other than an Application or a Combined Work as defined below.
21 |
22 | An "Application" is any work that makes use of an interface provided
23 | by the Library, but which is not otherwise based on the Library.
24 | Defining a subclass of a class defined by the Library is deemed a mode
25 | of using an interface provided by the Library.
26 |
27 | A "Combined Work" is a work produced by combining or linking an
28 | Application with the Library. The particular version of the Library
29 | with which the Combined Work was made is also called the "Linked
30 | Version".
31 |
32 | The "Minimal Corresponding Source" for a Combined Work means the
33 | Corresponding Source for the Combined Work, excluding any source code
34 | for portions of the Combined Work that, considered in isolation, are
35 | based on the Application, and not on the Linked Version.
36 |
37 | The "Corresponding Application Code" for a Combined Work means the
38 | object code and/or source code for the Application, including any data
39 | and utility programs needed for reproducing the Combined Work from the
40 | Application, but excluding the System Libraries of the Combined Work.
41 |
42 | 1. Exception to Section 3 of the GNU GPL.
43 |
44 | You may convey a covered work under sections 3 and 4 of this License
45 | without being bound by section 3 of the GNU GPL.
46 |
47 | 2. Conveying Modified Versions.
48 |
49 | If you modify a copy of the Library, and, in your modifications, a
50 | facility refers to a function or data to be supplied by an Application
51 | that uses the facility (other than as an argument passed when the
52 | facility is invoked), then you may convey a copy of the modified
53 | version:
54 |
55 | a) under this License, provided that you make a good faith effort to
56 | ensure that, in the event an Application does not supply the
57 | function or data, the facility still operates, and performs
58 | whatever part of its purpose remains meaningful, or
59 |
60 | b) under the GNU GPL, with none of the additional permissions of
61 | this License applicable to that copy.
62 |
63 | 3. Object Code Incorporating Material from Library Header Files.
64 |
65 | The object code form of an Application may incorporate material from
66 | a header file that is part of the Library. You may convey such object
67 | code under terms of your choice, provided that, if the incorporated
68 | material is not limited to numerical parameters, data structure
69 | layouts and accessors, or small macros, inline functions and templates
70 | (ten or fewer lines in length), you do both of the following:
71 |
72 | a) Give prominent notice with each copy of the object code that the
73 | Library is used in it and that the Library and its use are
74 | covered by this License.
75 |
76 | b) Accompany the object code with a copy of the GNU GPL and this license
77 | document.
78 |
79 | 4. Combined Works.
80 |
81 | You may convey a Combined Work under terms of your choice that,
82 | taken together, effectively do not restrict modification of the
83 | portions of the Library contained in the Combined Work and reverse
84 | engineering for debugging such modifications, if you also do each of
85 | the following:
86 |
87 | a) Give prominent notice with each copy of the Combined Work that
88 | the Library is used in it and that the Library and its use are
89 | covered by this License.
90 |
91 | b) Accompany the Combined Work with a copy of the GNU GPL and this license
92 | document.
93 |
94 | c) For a Combined Work that displays copyright notices during
95 | execution, include the copyright notice for the Library among
96 | these notices, as well as a reference directing the user to the
97 | copies of the GNU GPL and this license document.
98 |
99 | d) Do one of the following:
100 |
101 | 0) Convey the Minimal Corresponding Source under the terms of this
102 | License, and the Corresponding Application Code in a form
103 | suitable for, and under terms that permit, the user to
104 | recombine or relink the Application with a modified version of
105 | the Linked Version to produce a modified Combined Work, in the
106 | manner specified by section 6 of the GNU GPL for conveying
107 | Corresponding Source.
108 |
109 | 1) Use a suitable shared library mechanism for linking with the
110 | Library. A suitable mechanism is one that (a) uses at run time
111 | a copy of the Library already present on the user's computer
112 | system, and (b) will operate properly with a modified version
113 | of the Library that is interface-compatible with the Linked
114 | Version.
115 |
116 | e) Provide Installation Information, but only if you would otherwise
117 | be required to provide such information under section 6 of the
118 | GNU GPL, and only to the extent that such information is
119 | necessary to install and execute a modified version of the
120 | Combined Work produced by recombining or relinking the
121 | Application with a modified version of the Linked Version. (If
122 | you use option 4d0, the Installation Information must accompany
123 | the Minimal Corresponding Source and Corresponding Application
124 | Code. If you use option 4d1, you must provide the Installation
125 | Information in the manner specified by section 6 of the GNU GPL
126 | for conveying Corresponding Source.)
127 |
128 | 5. Combined Libraries.
129 |
130 | You may place library facilities that are a work based on the
131 | Library side by side in a single library together with other library
132 | facilities that are not Applications and are not covered by this
133 | License, and convey such a combined library under terms of your
134 | choice, if you do both of the following:
135 |
136 | a) Accompany the combined library with a copy of the same work based
137 | on the Library, uncombined with any other library facilities,
138 | conveyed under the terms of this License.
139 |
140 | b) Give prominent notice with the combined library that part of it
141 | is a work based on the Library, and explaining where to find the
142 | accompanying uncombined form of the same work.
143 |
144 | 6. Revised Versions of the GNU Lesser General Public License.
145 |
146 | The Free Software Foundation may publish revised and/or new versions
147 | of the GNU Lesser General Public License from time to time. Such new
148 | versions will be similar in spirit to the present version, but may
149 | differ in detail to address new problems or concerns.
150 |
151 | Each version is given a distinguishing version number. If the
152 | Library as you received it specifies that a certain numbered version
153 | of the GNU Lesser General Public License "or any later version"
154 | applies to it, you have the option of following the terms and
155 | conditions either of that published version or of any later version
156 | published by the Free Software Foundation. If the Library as you
157 | received it does not specify a version number of the GNU Lesser
158 | General Public License, you may choose any version of the GNU Lesser
159 | General Public License ever published by the Free Software Foundation.
160 |
161 | If the Library as you received it specifies that a proxy can decide
162 | whether future versions of the GNU Lesser General Public License shall
163 | apply, that proxy's public statement of acceptance of any version is
164 | permanent authorization for you to choose that version for the
165 | Library.
--------------------------------------------------------------------------------
/src/Vso.StateModelVisualization/scripts/cytoscape/cytoscape-dagre.js:
--------------------------------------------------------------------------------
1 | (function(){ 'use strict';
2 |
3 | // registers the extension on a cytoscape lib ref
4 | var register = function( cytoscape, dagre ){
5 | if( !cytoscape || !dagre ){ return; } // can't register if cytoscape unspecified
6 |
7 | var isFunction = function(o){ return typeof o === 'function'; };
8 |
9 | // default layout options
10 | var defaults = {
11 | // dagre algo options, uses default value on undefined
12 | nodeSep: undefined, // the separation between adjacent nodes in the same rank
13 | edgeSep: undefined, // the separation between adjacent edges in the same rank
14 | rankSep: undefined, // the separation between adjacent nodes in the same rank
15 | rankDir: undefined, // 'TB' for top to bottom flow, 'LR' for left to right
16 | minLen: function( edge ){ return 1; }, // number of ranks to keep between the source and target of the edge
17 | edgeWeight: function( edge ){ return 1; }, // higher weight edges are generally made shorter and straighter than lower weight edges
18 |
19 | // general layout options
20 | fit: true, // whether to fit to viewport
21 | padding: 30, // fit padding
22 | animate: false, // whether to transition the node positions
23 | animationDuration: 500, // duration of animation in ms if enabled
24 | animationEasing: undefined, // easing of animation if enabled
25 | boundingBox: undefined, // constrain layout bounds; { x1, y1, x2, y2 } or { x1, y1, w, h }
26 | ready: function(){}, // on layoutready
27 | stop: function(){} // on layoutstop
28 | };
29 |
30 | // constructor
31 | // options : object containing layout options
32 | function DagreLayout( options ){
33 | var opts = this.options = {};
34 | for( var i in defaults ){ opts[i] = defaults[i]; }
35 | for( var i in options ){ opts[i] = options[i]; }
36 | }
37 |
38 | // runs the layout
39 | DagreLayout.prototype.run = function(){
40 | var options = this.options;
41 | var layout = this;
42 |
43 | var cy = options.cy; // cy is automatically populated for us in the constructor
44 | var eles = options.eles;
45 |
46 | var getVal = function( ele, val ){
47 | return isFunction(val) ? val.apply( ele, [ ele ] ) : val;
48 | };
49 |
50 | var bb = options.boundingBox || { x1: 0, y1: 0, w: cy.width(), h: cy.height() };
51 | if( bb.x2 === undefined ){ bb.x2 = bb.x1 + bb.w; }
52 | if( bb.w === undefined ){ bb.w = bb.x2 - bb.x1; }
53 | if( bb.y2 === undefined ){ bb.y2 = bb.y1 + bb.h; }
54 | if( bb.h === undefined ){ bb.h = bb.y2 - bb.y1; }
55 |
56 | var g = new dagre.graphlib.Graph({
57 | multigraph: true,
58 | compound: true
59 | });
60 |
61 | var gObj = {};
62 | var setGObj = function( name, val ){
63 | if( val != null ){
64 | gObj[ name ] = val;
65 | }
66 | };
67 |
68 | setGObj( 'nodesep', options.nodeSep );
69 | setGObj( 'edgesep', options.edgeSep );
70 | setGObj( 'ranksep', options.rankSep );
71 | setGObj( 'rankdir', options.rankDir );
72 |
73 | g.setGraph( gObj );
74 |
75 | g.setDefaultEdgeLabel(function() { return {}; });
76 | g.setDefaultNodeLabel(function() { return {}; });
77 |
78 | // add nodes to dagre
79 | var nodes = eles.nodes();
80 | for( var i = 0; i < nodes.length; i++ ){
81 | var node = nodes[i];
82 | var nbb = node.boundingBox();
83 |
84 | g.setNode( node.id(), {
85 | width: nbb.w,
86 | height: nbb.h,
87 | name: node.id()
88 | } );
89 |
90 | // console.log( g.node(node.id()) );
91 | }
92 |
93 | // set compound parents
94 | for( var i = 0; i < nodes.length; i++ ){
95 | var node = nodes[i];
96 |
97 | if( node.isChild() ){
98 | g.setParent( node.id(), node.parent().id() );
99 | }
100 | }
101 |
102 | // add edges to dagre
103 | var edges = eles.edges().stdFilter(function( edge ){
104 | return !edge.source().isParent() && !edge.target().isParent(); // dagre can't handle edges on compound nodes
105 | });
106 | for( var i = 0; i < edges.length; i++ ){
107 | var edge = edges[i];
108 |
109 | g.setEdge( edge.source().id(), edge.target().id(), {
110 | minlen: getVal( edge, options.minLen ),
111 | weight: getVal( edge, options.edgeWeight ),
112 | name: edge.id()
113 | }, edge.id() );
114 |
115 | // console.log( g.edge(edge.source().id(), edge.target().id(), edge.id()) );
116 | }
117 |
118 | dagre.layout( g );
119 |
120 | var gNodeIds = g.nodes();
121 | for( var i = 0; i < gNodeIds.length; i++ ){
122 | var id = gNodeIds[i];
123 | var n = g.node( id );
124 |
125 | cy.getElementById(id).scratch().dagre = n;
126 | }
127 |
128 | var dagreBB;
129 |
130 | if( options.boundingBox ){
131 | dagreBB = { x1: Infinity, x2: -Infinity, y1: Infinity, y2: -Infinity };
132 | nodes.forEach(function( node ){
133 | var dModel = node.scratch().dagre;
134 |
135 | dagreBB.x1 = Math.min( dagreBB.x1, dModel.x );
136 | dagreBB.x2 = Math.max( dagreBB.x2, dModel.x );
137 |
138 | dagreBB.y1 = Math.min( dagreBB.y1, dModel.y );
139 | dagreBB.y2 = Math.max( dagreBB.y2, dModel.y );
140 | });
141 |
142 | dagreBB.w = dagreBB.x2 - dagreBB.x1;
143 | dagreBB.h = dagreBB.y2 - dagreBB.y1;
144 | } else {
145 | dagreBB = bb;
146 | }
147 |
148 | var constrainPos = function( p ){
149 | if( options.boundingBox ){
150 | var xPct = (p.x - dagreBB.x1) / dagreBB.w;
151 | var yPct = (p.y - dagreBB.y1) / dagreBB.h;
152 |
153 | return {
154 | x: bb.x1 + xPct * bb.w,
155 | y: bb.y1 + yPct * bb.h
156 | };
157 | } else {
158 | return p;
159 | }
160 | };
161 |
162 | nodes.layoutPositions(layout, options, function(){
163 | var dModel = this.scratch().dagre;
164 |
165 | return constrainPos({
166 | x: dModel.x,
167 | y: dModel.y
168 | });
169 | });
170 |
171 | return this; // chaining
172 | };
173 |
174 | cytoscape('layout', 'dagre', DagreLayout);
175 |
176 | };
177 |
178 | if( typeof module !== 'undefined' && module.exports ){ // expose as a commonjs module
179 | module.exports = register;
180 | }
181 |
182 | if( typeof define !== 'undefined' && define.amd ){ // expose as an amd/requirejs module
183 | define("scripts/cytoscape/cytoscape-dagre", function () {
184 | return register;
185 | });
186 | }
187 |
188 | if( typeof cytoscape !== 'undefined' && typeof dagre !== 'undefined' ){ // expose to global cytoscape (i.e. window.cytoscape)
189 | register( cytoscape, dagre );
190 | }
191 | return register;
192 | })();
193 |
--------------------------------------------------------------------------------
/src/Vso.StateModelVisualization/scripts/lib/VSS.SDK.js:
--------------------------------------------------------------------------------
1 | //dependencies=
2 | // Copyright (C) Microsoft Corporation. All rights reserved.
3 | ///
4 | ///
5 | ///
6 | ///
7 | ///
8 | /// This file is going to be embedded into the following typescript files in the build time:
9 | /// - VSS/SDK/XDM.ts
10 | /// - VSS/SDK/VSS.SDK.ts
11 | /// This module is unlike other modules which doesn't use AMD loading.
12 | var XDM;
13 | (function (XDM) {
14 | /**
15 | * Create a new deferred object
16 | */
17 | function createDeferred() {
18 | return new XdmDeferred();
19 | }
20 | XDM.createDeferred = createDeferred;
21 | var XdmDeferred = (function () {
22 | function XdmDeferred() {
23 | var _this = this;
24 | this._resolveCallbacks = [];
25 | this._rejectCallbacks = [];
26 | this._isResolved = false;
27 | this._isRejected = false;
28 | this.resolve = function (result) {
29 | _this._resolve(result);
30 | };
31 | this.reject = function (reason) {
32 | _this._reject(reason);
33 | };
34 | this.promise = {};
35 | this.promise.then = function (onFulfill, onReject) {
36 | return _this._then(onFulfill, onReject);
37 | };
38 | }
39 | XdmDeferred.prototype._then = function (onFulfill, onReject) {
40 | var _this = this;
41 | if ((!onFulfill && !onReject) ||
42 | (this._isResolved && !onFulfill) ||
43 | (this._isRejected && !onReject)) {
44 | return this.promise;
45 | }
46 | var newDeferred = new XdmDeferred();
47 | this._resolveCallbacks.push(function (value) {
48 | _this._wrapCallback(onFulfill, value, newDeferred, false);
49 | });
50 | this._rejectCallbacks.push(function (reason) {
51 | _this._wrapCallback(onReject, reason, newDeferred, true);
52 | });
53 | if (this._isResolved) {
54 | this._resolve(this._resolvedValue);
55 | }
56 | else if (this._isRejected) {
57 | this._reject(this._rejectValue);
58 | }
59 | return newDeferred.promise;
60 | };
61 | XdmDeferred.prototype._wrapCallback = function (callback, value, deferred, reject) {
62 | if (!callback) {
63 | if (reject) {
64 | deferred.reject(value);
65 | }
66 | else {
67 | deferred.resolve(value);
68 | }
69 | return;
70 | }
71 | var result;
72 | try {
73 | result = callback(value);
74 | }
75 | catch (ex) {
76 | deferred.reject(ex);
77 | return;
78 | }
79 | if (result === undefined) {
80 | deferred.resolve(value);
81 | }
82 | else if (result && typeof result.then === "function") {
83 | result.then(function (innerResult) {
84 | deferred.resolve(innerResult);
85 | }, function (innerReason) {
86 | deferred.reject(innerReason);
87 | });
88 | }
89 | else {
90 | deferred.resolve(result);
91 | }
92 | };
93 | XdmDeferred.prototype._resolve = function (result) {
94 | if (!this._isRejected && !this._isResolved) {
95 | this._isResolved = true;
96 | this._resolvedValue = result;
97 | }
98 | if (this._isResolved && this._resolveCallbacks.length > 0) {
99 | var resolveCallbacks = this._resolveCallbacks.splice(0);
100 | // 2.2.4. #onFulfilled or onRejected must not be called until the execution context stack contains only platform code.
101 | window.setTimeout(function () {
102 | for (var i = 0, l = resolveCallbacks.length; i < l; i++) {
103 | resolveCallbacks[i](result);
104 | }
105 | });
106 | }
107 | };
108 | XdmDeferred.prototype._reject = function (reason) {
109 | if (!this._isRejected && !this._isResolved) {
110 | this._isRejected = true;
111 | this._rejectValue = reason;
112 | if (this._rejectCallbacks.length === 0 && window.console && window.console.warn) {
113 | console.warn("Rejected XDM promise with no reject callbacks");
114 | if (reason) {
115 | console.warn(reason);
116 | }
117 | }
118 | }
119 | if (this._isRejected && this._rejectCallbacks.length > 0) {
120 | var rejectCallbacks = this._rejectCallbacks.splice(0);
121 | // 2.2.4. #onFulfilled or onRejected must not be called until the execution context stack contains only platform code.
122 | window.setTimeout(function () {
123 | for (var i = 0, l = rejectCallbacks.length; i < l; i++) {
124 | rejectCallbacks[i](reason);
125 | }
126 | });
127 | }
128 | };
129 | return XdmDeferred;
130 | }());
131 | var smallestRandom = parseInt("10000000000", 36);
132 | var maxSafeInteger = Number.MAX_SAFE_INTEGER || 9007199254740991;
133 | /**
134 | * Create a new random 22-character fingerprint.
135 | * @return string fingerprint
136 | */
137 | function newFingerprint() {
138 | // smallestRandom ensures we will get a 11-character result from the base-36 conversion.
139 | return Math.floor((Math.random() * (maxSafeInteger - smallestRandom)) + smallestRandom).toString(36) +
140 | Math.floor((Math.random() * (maxSafeInteger - smallestRandom)) + smallestRandom).toString(36);
141 | }
142 | /**
143 | * Catalog of objects exposed for XDM
144 | */
145 | var XDMObjectRegistry = (function () {
146 | function XDMObjectRegistry() {
147 | this._registeredObjects = {};
148 | }
149 | /**
150 | * Register an object (instance or factory method) exposed by this frame to callers in a remote frame
151 | *
152 | * @param instanceId unique id of the registered object
153 | * @param instance Either: (1) an object instance, or (2) a function that takes optional context data and returns an object instance.
154 | */
155 | XDMObjectRegistry.prototype.register = function (instanceId, instance) {
156 | this._registeredObjects[instanceId] = instance;
157 | };
158 | /**
159 | * Get an instance of an object registered with the given id
160 | *
161 | * @param instanceId unique id of the registered object
162 | * @param contextData Optional context data to pass to a registered object's factory method
163 | */
164 | XDMObjectRegistry.prototype.getInstance = function (instanceId, contextData) {
165 | var instance = this._registeredObjects[instanceId];
166 | if (!instance) {
167 | return null;
168 | }
169 | if (typeof instance === "function") {
170 | return instance(contextData);
171 | }
172 | else {
173 | return instance;
174 | }
175 | };
176 | return XDMObjectRegistry;
177 | }());
178 | XDM.XDMObjectRegistry = XDMObjectRegistry;
179 | ;
180 | /**
181 | * The registry of global XDM handlers
182 | */
183 | XDM.globalObjectRegistry = new XDMObjectRegistry();
184 | /**
185 | * Represents a channel of communication between frames\document
186 | * Stays "alive" across multiple funtion\method calls
187 | */
188 | var XDMChannel = (function () {
189 | function XDMChannel(postToWindow, targetOrigin) {
190 | if (targetOrigin === void 0) { targetOrigin = null; }
191 | this._nextMessageId = 1;
192 | this._deferreds = {};
193 | this._nextProxyFunctionId = 1;
194 | this._proxyFunctions = {};
195 | this._postToWindow = postToWindow;
196 | this._targetOrigin = targetOrigin;
197 | this._channelObjectRegistry = new XDMObjectRegistry();
198 | this._channelId = XDMChannel._nextChannelId++;
199 | if (!this._targetOrigin) {
200 | this._handshakeToken = newFingerprint();
201 | }
202 | }
203 | /**
204 | * Get the object registry to handle messages from this specific channel.
205 | * Upon receiving a message, this channel registry will be used first, then
206 | * the global registry will be used if no handler is found here.
207 | */
208 | XDMChannel.prototype.getObjectRegistry = function () {
209 | return this._channelObjectRegistry;
210 | };
211 | /**
212 | * Invoke a method via RPC. Lookup the registered object on the remote end of the channel and invoke the specified method.
213 | *
214 | * @param method Name of the method to invoke
215 | * @param instanceId unique id of the registered object
216 | * @param params Arguments to the method to invoke
217 | * @param instanceContextData Optional context data to pass to a registered object's factory method
218 | * @param serializationSettings Optional serialization settings
219 | */
220 | XDMChannel.prototype.invokeRemoteMethod = function (methodName, instanceId, params, instanceContextData, serializationSettings) {
221 | var message = {
222 | id: this._nextMessageId++,
223 | methodName: methodName,
224 | instanceId: instanceId,
225 | instanceContext: instanceContextData,
226 | params: this._customSerializeObject(params, serializationSettings),
227 | jsonrpc: "2.0",
228 | serializationSettings: serializationSettings
229 | };
230 | if (!this._targetOrigin) {
231 | message.handshakeToken = this._handshakeToken;
232 | }
233 | var deferred = createDeferred();
234 | this._deferreds[message.id] = deferred;
235 | this._sendRpcMessage(message);
236 | return deferred.promise;
237 | };
238 | /**
239 | * Get a proxied object that represents the object registered with the given instance id on the remote side of this channel.
240 | *
241 | * @param instanceId unique id of the registered object
242 | * @param contextData Optional context data to pass to a registered object's factory method
243 | */
244 | XDMChannel.prototype.getRemoteObjectProxy = function (instanceId, contextData) {
245 | return this.invokeRemoteMethod(null, instanceId, null, contextData);
246 | };
247 | XDMChannel.prototype.invokeMethod = function (registeredInstance, rpcMessage) {
248 | var _this = this;
249 | if (!rpcMessage.methodName) {
250 | // Null/empty method name indicates to return the registered object itself.
251 | this._success(rpcMessage, registeredInstance, rpcMessage.handshakeToken);
252 | return;
253 | }
254 | var method = registeredInstance[rpcMessage.methodName];
255 | if (typeof method !== "function") {
256 | this._error(rpcMessage, new Error("RPC method not found: " + rpcMessage.methodName), rpcMessage.handshakeToken);
257 | return;
258 | }
259 | try {
260 | // Call specified method. Add nested success and error call backs with closure
261 | // so we can post back a response as a result or error as appropriate
262 | var methodArgs = [];
263 | if (rpcMessage.params) {
264 | methodArgs = this._customDeserializeObject(rpcMessage.params);
265 | }
266 | var result = method.apply(registeredInstance, methodArgs);
267 | if (result && result.then && typeof result.then === "function") {
268 | result.then(function (asyncResult) {
269 | _this._success(rpcMessage, asyncResult, rpcMessage.handshakeToken);
270 | }, function (e) {
271 | _this._error(rpcMessage, e, rpcMessage.handshakeToken);
272 | });
273 | }
274 | else {
275 | this._success(rpcMessage, result, rpcMessage.handshakeToken);
276 | }
277 | }
278 | catch (exception) {
279 | // send back as error if an exception is thrown
280 | this._error(rpcMessage, exception, rpcMessage.handshakeToken);
281 | }
282 | };
283 | XDMChannel.prototype.getRegisteredObject = function (instanceId, instanceContext) {
284 | if (instanceId === "__proxyFunctions") {
285 | // Special case for proxied functions of remote instances
286 | return this._proxyFunctions;
287 | }
288 | // Look in the channel registry first
289 | var registeredObject = this._channelObjectRegistry.getInstance(instanceId, instanceContext);
290 | if (!registeredObject) {
291 | // Look in the global registry as a fallback
292 | registeredObject = XDM.globalObjectRegistry.getInstance(instanceId, instanceContext);
293 | }
294 | return registeredObject;
295 | };
296 | /**
297 | * Handle a received message on this channel. Dispatch to the appropriate object found via object registry
298 | *
299 | * @param data Message data
300 | * @param origin Origin of the frame that sent the message
301 | * @return True if the message was handled by this channel. Otherwise false.
302 | */
303 | XDMChannel.prototype.onMessage = function (data, origin) {
304 | var _this = this;
305 | var rpcMessage = data;
306 | if (rpcMessage.instanceId) {
307 | // Find the object that handles this requestNeed to find implementation
308 | // Look in the channel registry first
309 | var registeredObject = this.getRegisteredObject(rpcMessage.instanceId, rpcMessage.instanceContext);
310 | if (!registeredObject) {
311 | // If not found return false to indicate that the message was not handled
312 | return false;
313 | }
314 | if (typeof registeredObject["then"] === "function") {
315 | registeredObject.then(function (resolvedInstance) {
316 | _this.invokeMethod(resolvedInstance, rpcMessage);
317 | }, function (e) {
318 | _this._error(rpcMessage, e, rpcMessage.handshakeToken);
319 | });
320 | }
321 | else {
322 | this.invokeMethod(registeredObject, rpcMessage);
323 | }
324 | }
325 | else {
326 | // response
327 | // Responses look like this -
328 | // {"jsonrpc": "2.0", "result": ["hello", 5], "id": "9"}
329 | // {"jsonrpc": "2.0", "error": {"code": -32601, "message": "Method not found."}, "id": "5"}
330 | var deferred = this._deferreds[rpcMessage.id];
331 | if (!deferred) {
332 | // Message not handled by this channel.
333 | return false;
334 | }
335 | if (rpcMessage.error) {
336 | deferred.reject(this._customDeserializeObject([rpcMessage.error])[0]);
337 | }
338 | else {
339 | deferred.resolve(this._customDeserializeObject([rpcMessage.result])[0]);
340 | }
341 | delete this._deferreds[rpcMessage.id];
342 | }
343 | // Message handled by this channel
344 | return true;
345 | };
346 | XDMChannel.prototype.owns = function (source, origin, data) {
347 | /// Determines whether the current message belongs to this channel or not
348 | var rpcMessage = data;
349 | if (this._postToWindow === source) {
350 | // For messages coming from sandboxed iframes the origin will be set to the string "null". This is
351 | // how onprem works. If it is not a sandboxed iFrame we will get the origin as expected.
352 | if (this._targetOrigin) {
353 | if (origin) {
354 | return origin.toLowerCase() === "null" || this._targetOrigin.toLowerCase().indexOf(origin.toLowerCase()) === 0;
355 | }
356 | else {
357 | return false;
358 | }
359 | }
360 | else {
361 | if (rpcMessage.handshakeToken && rpcMessage.handshakeToken === this._handshakeToken) {
362 | this._targetOrigin = origin;
363 | return true;
364 | }
365 | }
366 | }
367 | return false;
368 | };
369 | XDMChannel.prototype.error = function (data, errorObj) {
370 | var rpcMessage = data;
371 | this._error(rpcMessage, errorObj, rpcMessage.handshakeToken);
372 | };
373 | XDMChannel.prototype._error = function (messageObj, errorObj, handshakeToken) {
374 | // Post back a response as an error which look like this -
375 | // {"id": "5", "error": {"code": -32601, "message": "Method not found."}, "jsonrpc": "2.0", }
376 | var message = {
377 | id: messageObj.id,
378 | error: this._customSerializeObject([errorObj], messageObj.serializationSettings)[0],
379 | jsonrpc: "2.0",
380 | handshakeToken: handshakeToken
381 | };
382 | this._sendRpcMessage(message);
383 | };
384 | XDMChannel.prototype._success = function (messageObj, result, handshakeToken) {
385 | // Post back response result which look like this -
386 | // {"id": "9", "result": ["hello", 5], "jsonrpc": "2.0"}
387 | var message = {
388 | id: messageObj.id,
389 | result: this._customSerializeObject([result], messageObj.serializationSettings)[0],
390 | jsonrpc: "2.0",
391 | handshakeToken: handshakeToken
392 | };
393 | this._sendRpcMessage(message);
394 | };
395 | XDMChannel.prototype._sendRpcMessage = function (message) {
396 | var messageString = JSON.stringify(message);
397 | this._postToWindow.postMessage(messageString, "*");
398 | };
399 | XDMChannel.prototype._shouldSkipSerialization = function (obj) {
400 | for (var i = 0, l = XDMChannel.WINDOW_TYPES_TO_SKIP_SERIALIZATION.length; i < l; i++) {
401 | var instanceType = XDMChannel.WINDOW_TYPES_TO_SKIP_SERIALIZATION[i];
402 | if (window[instanceType] && obj instanceof window[instanceType]) {
403 | return true;
404 | }
405 | }
406 | if (window.jQuery) {
407 | for (var i = 0, l = XDMChannel.JQUERY_TYPES_TO_SKIP_SERIALIZATION.length; i < l; i++) {
408 | var instanceType = XDMChannel.JQUERY_TYPES_TO_SKIP_SERIALIZATION[i];
409 | if (window.jQuery[instanceType] && obj instanceof window.jQuery[instanceType]) {
410 | return true;
411 | }
412 | }
413 | }
414 | return false;
415 | };
416 | XDMChannel.prototype._customSerializeObject = function (obj, settings, parentObjects, nextCircularRefId, depth) {
417 | var _this = this;
418 | if (parentObjects === void 0) { parentObjects = null; }
419 | if (nextCircularRefId === void 0) { nextCircularRefId = 1; }
420 | if (depth === void 0) { depth = 1; }
421 | if (!obj || depth > XDMChannel.MAX_XDM_DEPTH) {
422 | return null;
423 | }
424 | if (this._shouldSkipSerialization(obj)) {
425 | return null;
426 | }
427 | var serializeMember = function (parentObject, newObject, key) {
428 | var item;
429 | try {
430 | item = parentObject[key];
431 | }
432 | catch (ex) {
433 | }
434 | var itemType = typeof item;
435 | if (itemType === "undefined") {
436 | return;
437 | }
438 | // Check for a circular reference by looking at parent objects
439 | var parentItemIndex = -1;
440 | if (itemType === "object") {
441 | parentItemIndex = parentObjects.originalObjects.indexOf(item);
442 | }
443 | if (parentItemIndex >= 0) {
444 | // Circular reference found. Add reference to parent
445 | var parentItem = parentObjects.newObjects[parentItemIndex];
446 | if (!parentItem.__circularReferenceId) {
447 | parentItem.__circularReferenceId = nextCircularRefId++;
448 | }
449 | newObject[key] = {
450 | __circularReference: parentItem.__circularReferenceId
451 | };
452 | }
453 | else {
454 | if (itemType === "function") {
455 | var proxyFunctionId = _this._nextProxyFunctionId++;
456 | newObject[key] = {
457 | __proxyFunctionId: _this._registerProxyFunction(item, obj),
458 | __channelId: _this._channelId
459 | };
460 | }
461 | else if (itemType === "object") {
462 | if (item && item instanceof Date) {
463 | newObject[key] = {
464 | __proxyDate: item.getTime()
465 | };
466 | }
467 | else {
468 | newObject[key] = _this._customSerializeObject(item, settings, parentObjects, nextCircularRefId, depth + 1);
469 | }
470 | }
471 | else if (key !== "__proxyFunctionId") {
472 | // Just add non object/function properties as-is. Don't include "__proxyFunctionId" to protect
473 | // our proxy methods from being invoked from other messages.
474 | newObject[key] = item;
475 | }
476 | }
477 | };
478 | var returnValue;
479 | if (!parentObjects) {
480 | parentObjects = {
481 | newObjects: [],
482 | originalObjects: []
483 | };
484 | }
485 | parentObjects.originalObjects.push(obj);
486 | if (obj instanceof Array) {
487 | returnValue = [];
488 | parentObjects.newObjects.push(returnValue);
489 | for (var i = 0, l = obj.length; i < l; i++) {
490 | serializeMember(obj, returnValue, i);
491 | }
492 | }
493 | else {
494 | returnValue = {};
495 | parentObjects.newObjects.push(returnValue);
496 | var keys = {};
497 | try {
498 | // We want to get both enumerable and non-enumerable properties
499 | // including inherited enumerable properties. for..in grabs
500 | // enumerable properties (including inherited properties) and
501 | // getOwnPropertyNames includes non-enumerable properties.
502 | // Merge these results together.
503 | for (var key in obj) {
504 | keys[key] = true;
505 | }
506 | var ownProperties = Object.getOwnPropertyNames(obj);
507 | for (var i = 0, l = ownProperties.length; i < l; i++) {
508 | keys[ownProperties[i]] = true;
509 | }
510 | }
511 | catch (ex) {
512 | }
513 | for (var key in keys) {
514 | // Don't serialize properties that start with an underscore.
515 | if ((key && key[0] !== "_") || (settings && settings.includeUnderscoreProperties)) {
516 | serializeMember(obj, returnValue, key);
517 | }
518 | }
519 | }
520 | parentObjects.originalObjects.pop();
521 | parentObjects.newObjects.pop();
522 | return returnValue;
523 | };
524 | XDMChannel.prototype._registerProxyFunction = function (func, context) {
525 | var proxyFunctionId = this._nextProxyFunctionId++;
526 | this._proxyFunctions["proxy" + proxyFunctionId] = function () {
527 | return func.apply(context, Array.prototype.slice.call(arguments, 0));
528 | };
529 | return proxyFunctionId;
530 | };
531 | XDMChannel.prototype._customDeserializeObject = function (obj, circularRefs) {
532 | var _this = this;
533 | var that = this;
534 | if (!obj) {
535 | return null;
536 | }
537 | if (!circularRefs) {
538 | circularRefs = {};
539 | }
540 | var deserializeMember = function (parentObject, key) {
541 | var item = parentObject[key];
542 | var itemType = typeof item;
543 | if (key === "__circularReferenceId" && itemType === 'number') {
544 | circularRefs[item] = parentObject;
545 | delete parentObject[key];
546 | }
547 | else if (itemType === "object" && item) {
548 | if (item.__proxyFunctionId) {
549 | parentObject[key] = function () {
550 | return that.invokeRemoteMethod("proxy" + item.__proxyFunctionId, "__proxyFunctions", Array.prototype.slice.call(arguments, 0), null, { includeUnderscoreProperties: true });
551 | };
552 | }
553 | else if (item.__proxyDate) {
554 | parentObject[key] = new Date(item.__proxyDate);
555 | }
556 | else if (item.__circularReference) {
557 | parentObject[key] = circularRefs[item.__circularReference];
558 | }
559 | else {
560 | _this._customDeserializeObject(item, circularRefs);
561 | }
562 | }
563 | };
564 | if (obj instanceof Array) {
565 | for (var i = 0, l = obj.length; i < l; i++) {
566 | deserializeMember(obj, i);
567 | }
568 | }
569 | else if (typeof obj === "object") {
570 | for (var key in obj) {
571 | deserializeMember(obj, key);
572 | }
573 | }
574 | return obj;
575 | };
576 | XDMChannel._nextChannelId = 1;
577 | XDMChannel.MAX_XDM_DEPTH = 100;
578 | XDMChannel.WINDOW_TYPES_TO_SKIP_SERIALIZATION = [
579 | "Node",
580 | "Window",
581 | "Event"
582 | ];
583 | XDMChannel.JQUERY_TYPES_TO_SKIP_SERIALIZATION = [
584 | "jQuery"
585 | ];
586 | return XDMChannel;
587 | }());
588 | XDM.XDMChannel = XDMChannel;
589 | /**
590 | * Registry of XDM channels kept per target frame/window
591 | */
592 | var XDMChannelManager = (function () {
593 | function XDMChannelManager() {
594 | this._channels = [];
595 | this._subscribe(window);
596 | }
597 | XDMChannelManager.get = function () {
598 | if (!this._default) {
599 | this._default = new XDMChannelManager();
600 | }
601 | return this._default;
602 | };
603 | /**
604 | * Add an XDM channel for the given target window/iframe
605 | *
606 | * @param window Target iframe window to communicate with
607 | * @param targetOrigin Url of the target iframe (if known)
608 | */
609 | XDMChannelManager.prototype.addChannel = function (window, targetOrigin) {
610 | var channel = new XDMChannel(window, targetOrigin);
611 | this._channels.push(channel);
612 | return channel;
613 | };
614 | XDMChannelManager.prototype._handleMessageReceived = function (event) {
615 | // get channel and dispatch to it
616 | var i, len, channel;
617 | var rpcMessage;
618 | if (typeof event.data === "string") {
619 | try {
620 | rpcMessage = JSON.parse(event.data);
621 | }
622 | catch (error) {
623 | }
624 | }
625 | if (rpcMessage) {
626 | var handled = false;
627 | var channelOwner;
628 | for (i = 0, len = this._channels.length; i < len; i++) {
629 | channel = this._channels[i];
630 | if (channel.owns(event.source, event.origin, rpcMessage)) {
631 | // keep a reference to the channel owner found.
632 | channelOwner = channel;
633 | handled = channel.onMessage(rpcMessage, event.origin) || handled;
634 | }
635 | }
636 | if (!!channelOwner && !handled) {
637 | if (window.console) {
638 | console.error("No handler found on any channel for message: " + JSON.stringify(rpcMessage));
639 | }
640 | // for instance based proxies, send an error on the channel owning the message to resolve any control creation promises
641 | // on the host frame.
642 | if (rpcMessage.instanceId) {
643 | channelOwner.error(rpcMessage, "The registered object " + rpcMessage.instanceId + " could not be found.");
644 | }
645 | }
646 | }
647 | };
648 | XDMChannelManager.prototype._subscribe = function (windowObj) {
649 | var _this = this;
650 | if (windowObj.addEventListener) {
651 | windowObj.addEventListener("message", function (event) {
652 | _this._handleMessageReceived(event);
653 | });
654 | }
655 | else {
656 | // IE8
657 | windowObj.attachEvent("onmessage", function (event) {
658 | _this._handleMessageReceived(event);
659 | });
660 | }
661 | };
662 | return XDMChannelManager;
663 | }());
664 | XDM.XDMChannelManager = XDMChannelManager;
665 | })(XDM || (XDM = {}));
666 | var VSS;
667 | (function (VSS) {
668 | // W A R N I N G: if VssSDKVersion changes, the VSS WEB SDK demand resolver needs to be updated with the new version
669 | VSS.VssSDKVersion = 2.0;
670 | VSS.VssSDKRestVersion = "2.2";
671 | var bodyElement;
672 | var webContext;
673 | var hostPageContext;
674 | var extensionContext;
675 | var initialConfiguration;
676 | var initialContribution;
677 | var initOptions;
678 | var loaderConfigured = false;
679 | var usingPlatformScripts;
680 | var usingPlatformStyles;
681 | var isReady = false;
682 | var readyCallbacks;
683 | var parentChannel = XDM.XDMChannelManager.get().addChannel(window.parent);
684 | var shimmedLocalStorage;
685 | var hostReadyForShimUpdates = false;
686 | var Storage = (function () {
687 | var changeCallback;
688 | function invokeChangeCallback() {
689 | if (changeCallback) {
690 | changeCallback.call(this);
691 | }
692 | }
693 | function Storage(changeCallback) {
694 | }
695 | Object.defineProperties(Storage.prototype, {
696 | getItem: {
697 | get: function () {
698 | return function (key) {
699 | var item = this["" + key];
700 | return typeof item === "undefined" ? null : item;
701 | };
702 | }
703 | },
704 | setItem: {
705 | get: function () {
706 | return function (key, value) {
707 | key = "" + key;
708 | var existingValue = this[key];
709 | var newValue = "" + value;
710 | if (existingValue !== newValue) {
711 | this[key] = newValue;
712 | invokeChangeCallback();
713 | }
714 | };
715 | }
716 | },
717 | removeItem: {
718 | get: function () {
719 | return function (key) {
720 | key = "" + key;
721 | if (typeof this[key] !== "undefined") {
722 | delete this[key];
723 | invokeChangeCallback();
724 | }
725 | };
726 | }
727 | },
728 | clear: {
729 | get: function () {
730 | return function () {
731 | var keys = Object.keys(this);
732 | if (keys.length > 0) {
733 | for (var _i = 0, keys_1 = keys; _i < keys_1.length; _i++) {
734 | var key = keys_1[_i];
735 | delete this[key];
736 | }
737 | invokeChangeCallback();
738 | }
739 | };
740 | }
741 | },
742 | key: {
743 | get: function () {
744 | return function (index) {
745 | return Object.keys(this)[index];
746 | };
747 | }
748 | },
749 | length: {
750 | get: function () {
751 | return Object.keys(this).length;
752 | }
753 | }
754 | });
755 | return Storage;
756 | }());
757 | function shimSandboxedProperties() {
758 | var updateSettingsTimeout;
759 | function updateShimmedStorageCallback() {
760 | // Talk to the host frame on a 50 ms delay in order to batch storage/cookie updates
761 | if (!updateSettingsTimeout) {
762 | updateSettingsTimeout = setTimeout(function () {
763 | updateSettingsTimeout = 0;
764 | updateHostSandboxedStorage();
765 | }, 50);
766 | }
767 | }
768 | // Override document.cookie if it is not available
769 | var hasCookieSupport = false;
770 | try {
771 | hasCookieSupport = typeof document.cookie === "string";
772 | }
773 | catch (ex) {
774 | }
775 | if (!hasCookieSupport) {
776 | Object.defineProperty(Document.prototype, "cookie", {
777 | get: function () {
778 | return "";
779 | },
780 | set: function (value) {
781 | }
782 | });
783 | }
784 | // Override browser storage
785 | var hasLocalStorage = false;
786 | try {
787 | hasLocalStorage = !!window.localStorage;
788 | }
789 | catch (ex) {
790 | }
791 | if (!hasLocalStorage) {
792 | delete window.localStorage;
793 | shimmedLocalStorage = new Storage(updateShimmedStorageCallback);
794 | Object.defineProperty(window, "localStorage", { value: shimmedLocalStorage });
795 | delete window.sessionStorage;
796 | Object.defineProperty(window, "sessionStorage", { value: new Storage() });
797 | }
798 | }
799 | if (!window["__vssNoSandboxShim"]) {
800 | try {
801 | shimSandboxedProperties();
802 | }
803 | catch (ex) {
804 | if (window.console && window.console.warn) {
805 | window.console.warn("Failed to shim support for sandboxed properties: " + ex.message + ". Set \"window.__vssNoSandboxShim = true\" in order to bypass the shim of sandboxed properties.");
806 | }
807 | }
808 | }
809 | /**
810 | * Service Ids for core services (to be used in VSS.getService)
811 | */
812 | var ServiceIds;
813 | (function (ServiceIds) {
814 | /**
815 | * Service for showing dialogs in the host frame
816 | * Use:
817 | */
818 | ServiceIds.Dialog = "ms.vss-web.dialog-service";
819 | /**
820 | * Service for interacting with the host frame's navigation (getting/updating the address/hash, reloading the page, etc.)
821 | * Use:
822 | */
823 | ServiceIds.Navigation = "ms.vss-web.navigation-service";
824 | /**
825 | * Service for interacting with extension data (setting/setting documents and collections)
826 | * Use:
827 | */
828 | ServiceIds.ExtensionData = "ms.vss-web.data-service";
829 | })(ServiceIds = VSS.ServiceIds || (VSS.ServiceIds = {}));
830 | /**
831 | * Initiates the handshake with the host window.
832 | *
833 | * @param options Initialization options for the extension.
834 | */
835 | function init(options) {
836 | initOptions = options || {};
837 | usingPlatformScripts = initOptions.usePlatformScripts;
838 | usingPlatformStyles = initOptions.usePlatformStyles;
839 | // Run this after current execution path is complete - allows objects to get initialized
840 | window.setTimeout(function () {
841 | var appHandshakeData = {
842 | notifyLoadSucceeded: !initOptions.explicitNotifyLoaded,
843 | extensionReusedCallback: initOptions.extensionReusedCallback,
844 | vssSDKVersion: VSS.VssSDKVersion
845 | };
846 | parentChannel.invokeRemoteMethod("initialHandshake", "VSS.HostControl", [appHandshakeData]).then(function (handshakeData) {
847 | hostPageContext = handshakeData.pageContext;
848 | webContext = hostPageContext.webContext;
849 | initialConfiguration = handshakeData.initialConfig || {};
850 | initialContribution = handshakeData.contribution;
851 | extensionContext = handshakeData.extensionContext;
852 | if (handshakeData.sandboxedStorage) {
853 | var updateNeeded = false;
854 | if (shimmedLocalStorage) {
855 | if (handshakeData.sandboxedStorage.localStorage) {
856 | // Merge host data in with any values already set.
857 | var newData = handshakeData.sandboxedStorage.localStorage;
858 | // Check for any properties written prior to the initial handshake
859 | for (var _i = 0, _a = Object.keys(shimmedLocalStorage); _i < _a.length; _i++) {
860 | var key = _a[_i];
861 | var value = shimmedLocalStorage.getItem(key);
862 | if (value !== newData[key]) {
863 | newData[key] = value;
864 | updateNeeded = true;
865 | }
866 | }
867 | // Update the stored values
868 | for (var _b = 0, _c = Object.keys(newData); _b < _c.length; _b++) {
869 | var key = _c[_b];
870 | shimmedLocalStorage.setItem(key, newData[key]);
871 | }
872 | }
873 | else if (shimmedLocalStorage.length > 0) {
874 | updateNeeded = true;
875 | }
876 | }
877 | hostReadyForShimUpdates = true;
878 | if (updateNeeded) {
879 | // Talk to host frame to issue update
880 | updateHostSandboxedStorage();
881 | }
882 | }
883 | if (usingPlatformScripts || usingPlatformStyles) {
884 | setupAmdLoader();
885 | }
886 | else {
887 | triggerReady();
888 | }
889 | });
890 | }, 0);
891 | }
892 | VSS.init = init;
893 | function updateHostSandboxedStorage() {
894 | var storage = {
895 | localStorage: JSON.stringify(shimmedLocalStorage || {})
896 | };
897 | parentChannel.invokeRemoteMethod("updateSandboxedStorage", "VSS.HostControl", [storage]);
898 | }
899 | /**
900 | * Ensures that the AMD loader from the host is configured and fetches a script (AMD) module
901 | * (and its dependencies). If no callback is supplied, this will still perform an asynchronous
902 | * fetch of the module (unlike AMD require which returns synchronously). This method has no return value.
903 | *
904 | * Usage:
905 | *
906 | * VSS.require(["VSS/Controls", "VSS/Controls/Grids"], function(Controls, Grids) {
907 | * ...
908 | * });
909 | *
910 | * @param modules A single module path (string) or array of paths (string[])
911 | * @param callback Method called once the modules have been loaded.
912 | */
913 | function require(modules, callback) {
914 | var modulesArray;
915 | if (typeof modules === "string") {
916 | modulesArray = [modules];
917 | }
918 | else {
919 | modulesArray = modules;
920 | }
921 | if (!callback) {
922 | // Generate an empty callback for require
923 | callback = function () { };
924 | }
925 | if (loaderConfigured) {
926 | // Loader already configured, just issue require
927 | issueVssRequire(modulesArray, callback);
928 | }
929 | else {
930 | if (!initOptions) {
931 | init({ usePlatformScripts: true });
932 | }
933 | else if (!usingPlatformScripts) {
934 | usingPlatformScripts = true;
935 | if (isReady) {
936 | // We are in the ready state, but previously not using the loader, so set it up now
937 | // which will re-trigger ready
938 | isReady = false;
939 | setupAmdLoader();
940 | }
941 | }
942 | ready(function () {
943 | issueVssRequire(modulesArray, callback);
944 | });
945 | }
946 | }
947 | VSS.require = require;
948 | function issueVssRequire(modules, callback) {
949 | if (hostPageContext.diagnostics.bundlingEnabled) {
950 | window.require(["VSS/Bundling"], function (VSS_Bundling) {
951 | VSS_Bundling.requireModules(modules, callback);
952 | });
953 | }
954 | else {
955 | window.require(modules, callback);
956 | }
957 | }
958 | /**
959 | * Register a callback that gets called once the initial setup/handshake has completed.
960 | * If the initial setup is already completed, the callback is invoked at the end of the current call stack.
961 | */
962 | function ready(callback) {
963 | if (isReady) {
964 | window.setTimeout(callback, 0);
965 | }
966 | else {
967 | if (!readyCallbacks) {
968 | readyCallbacks = [];
969 | }
970 | readyCallbacks.push(callback);
971 | }
972 | }
973 | VSS.ready = ready;
974 | /**
975 | * Notifies the host that the extension successfully loaded (stop showing the loading indicator)
976 | */
977 | function notifyLoadSucceeded() {
978 | parentChannel.invokeRemoteMethod("notifyLoadSucceeded", "VSS.HostControl");
979 | }
980 | VSS.notifyLoadSucceeded = notifyLoadSucceeded;
981 | /**
982 | * Notifies the host that the extension failed to load
983 | */
984 | function notifyLoadFailed(e) {
985 | parentChannel.invokeRemoteMethod("notifyLoadFailed", "VSS.HostControl", [e]);
986 | }
987 | VSS.notifyLoadFailed = notifyLoadFailed;
988 | /**
989 | * Get the web context from the parent host
990 | */
991 | function getWebContext() {
992 | return webContext;
993 | }
994 | VSS.getWebContext = getWebContext;
995 | /**
996 | * Get the configuration data passed in the initial handshake from the parent frame
997 | */
998 | function getConfiguration() {
999 | return initialConfiguration;
1000 | }
1001 | VSS.getConfiguration = getConfiguration;
1002 | /**
1003 | * Get the context about the extension that owns the content that is being hosted
1004 | */
1005 | function getExtensionContext() {
1006 | return extensionContext;
1007 | }
1008 | VSS.getExtensionContext = getExtensionContext;
1009 | /**
1010 | * Gets the information about the contribution that first caused this extension to load.
1011 | */
1012 | function getContribution() {
1013 | return initialContribution;
1014 | }
1015 | VSS.getContribution = getContribution;
1016 | /**
1017 | * Get a contributed service from the parent host.
1018 | *
1019 | * @param contributionId Full Id of the service contribution to get the instance of
1020 | * @param context Optional context information to use when obtaining the service instance
1021 | */
1022 | function getService(contributionId, context) {
1023 | return getServiceContribution(contributionId).then(function (serviceContribution) {
1024 | if (!context) {
1025 | context = {};
1026 | }
1027 | if (!context["webContext"]) {
1028 | context["webContext"] = getWebContext();
1029 | }
1030 | if (!context["extensionContext"]) {
1031 | context["extensionContext"] = getExtensionContext();
1032 | }
1033 | return serviceContribution.getInstance(serviceContribution.id, context);
1034 | });
1035 | }
1036 | VSS.getService = getService;
1037 | /**
1038 | * Get the contribution with the given contribution id. The returned contribution has a method to get a registered object within that contribution.
1039 | *
1040 | * @param contributionId Id of the contribution to get
1041 | */
1042 | function getServiceContribution(contributionId) {
1043 | var deferred = XDM.createDeferred();
1044 | VSS.ready(function () {
1045 | parentChannel.invokeRemoteMethod("getServiceContribution", "vss.hostManagement", [contributionId]).then(function (contribution) {
1046 | var serviceContribution = contribution;
1047 | serviceContribution.getInstance = function (objectId, context) {
1048 | return getBackgroundContributionInstance(contribution, objectId, context);
1049 | };
1050 | deferred.resolve(serviceContribution);
1051 | }, deferred.reject);
1052 | });
1053 | return deferred.promise;
1054 | }
1055 | VSS.getServiceContribution = getServiceContribution;
1056 | /**
1057 | * Get contributions that target a given contribution id. The returned contributions have a method to get a registered object within that contribution.
1058 | *
1059 | * @param targetContributionId Contributions that target the contribution with this id will be returned
1060 | */
1061 | function getServiceContributions(targetContributionId) {
1062 | var deferred = XDM.createDeferred();
1063 | VSS.ready(function () {
1064 | parentChannel.invokeRemoteMethod("getContributionsForTarget", "vss.hostManagement", [targetContributionId]).then(function (contributions) {
1065 | var serviceContributions = [];
1066 | contributions.forEach(function (contribution) {
1067 | var serviceContribution = contribution;
1068 | serviceContribution.getInstance = function (objectId, context) {
1069 | return getBackgroundContributionInstance(contribution, objectId, context);
1070 | };
1071 | serviceContributions.push(serviceContribution);
1072 | });
1073 | deferred.resolve(serviceContributions);
1074 | }, deferred.reject);
1075 | });
1076 | return deferred.promise;
1077 | }
1078 | VSS.getServiceContributions = getServiceContributions;
1079 | /**
1080 | * Create an instance of a registered object within the given contribution in the host's frame
1081 | *
1082 | * @param contribution The contribution to get an object from
1083 | * @param objectId Optional id of the registered object (the contribution's id property is used by default)
1084 | * @param contextData Optional context to use when getting the object.
1085 | */
1086 | function getBackgroundContributionInstance(contribution, objectId, contextData) {
1087 | var deferred = XDM.createDeferred();
1088 | VSS.ready(function () {
1089 | parentChannel.invokeRemoteMethod("getBackgroundContributionInstance", "vss.hostManagement", [contribution, objectId, contextData]).then(deferred.resolve, deferred.reject);
1090 | });
1091 | return deferred.promise;
1092 | }
1093 | /**
1094 | * Register an object (instance or factory method) that this extension exposes to the host frame.
1095 | *
1096 | * @param instanceId unique id of the registered object
1097 | * @param instance Either: (1) an object instance, or (2) a function that takes optional context data and returns an object instance.
1098 | */
1099 | function register(instanceId, instance) {
1100 | parentChannel.getObjectRegistry().register(instanceId, instance);
1101 | }
1102 | VSS.register = register;
1103 | /**
1104 | * Get an instance of an object registered with the given id
1105 | *
1106 | * @param instanceId unique id of the registered object
1107 | * @param contextData Optional context data to pass to the contructor of an object factory method
1108 | */
1109 | function getRegisteredObject(instanceId, contextData) {
1110 | return parentChannel.getObjectRegistry().getInstance(instanceId, contextData);
1111 | }
1112 | VSS.getRegisteredObject = getRegisteredObject;
1113 | /**
1114 | * Fetch an access token which will allow calls to be made to other VSTS services
1115 | */
1116 | function getAccessToken() {
1117 | return parentChannel.invokeRemoteMethod("getAccessToken", "VSS.HostControl");
1118 | }
1119 | VSS.getAccessToken = getAccessToken;
1120 | /**
1121 | * Fetch an token which can be used to identify the current user
1122 | */
1123 | function getAppToken() {
1124 | return parentChannel.invokeRemoteMethod("getAppToken", "VSS.HostControl");
1125 | }
1126 | VSS.getAppToken = getAppToken;
1127 | /**
1128 | * Requests the parent window to resize the container for this extension based on the current extension size.
1129 | */
1130 | function resize() {
1131 | if (!bodyElement) {
1132 | bodyElement = document.getElementsByTagName("body").item(0);
1133 | }
1134 | parentChannel.invokeRemoteMethod("resize", "VSS.HostControl", [bodyElement.scrollWidth, bodyElement.scrollHeight]);
1135 | }
1136 | VSS.resize = resize;
1137 | function setupAmdLoader() {
1138 | var hostRootUri = getRootUri(hostPageContext.webContext);
1139 | // Place context so that VSS scripts pick it up correctly
1140 | window.__vssPageContext = hostPageContext;
1141 | // MS Ajax config needs to exist before loading MS Ajax library
1142 | window.__cultureInfo = hostPageContext.microsoftAjaxConfig.cultureInfo;
1143 | // Append CSS first
1144 | if (usingPlatformStyles !== false) {
1145 | if (hostPageContext.coreReferences.stylesheets) {
1146 | hostPageContext.coreReferences.stylesheets.forEach(function (stylesheet) {
1147 | if (stylesheet.isCoreStylesheet) {
1148 | var cssLink = document.createElement("link");
1149 | cssLink.href = getAbsoluteUrl(stylesheet.url, hostRootUri);
1150 | cssLink.rel = "stylesheet";
1151 | safeAppendToDom(cssLink, "head");
1152 | }
1153 | });
1154 | }
1155 | }
1156 | if (!usingPlatformScripts) {
1157 | // Just wanted to load CSS, no scripts. Can exit here.
1158 | loaderConfigured = true;
1159 | triggerReady();
1160 | return;
1161 | }
1162 | var scripts = [];
1163 | var anyCoreScriptLoaded = false;
1164 | // Add scripts and loader configuration
1165 | if (hostPageContext.coreReferences.scripts) {
1166 | hostPageContext.coreReferences.scripts.forEach(function (script) {
1167 | if (script.isCoreModule) {
1168 | var alreadyLoaded = false;
1169 | var global = window;
1170 | if (script.identifier === "JQuery") {
1171 | alreadyLoaded = !!global.jQuery;
1172 | }
1173 | else if (script.identifier === "JQueryUI") {
1174 | alreadyLoaded = !!(global.jQuery && global.jQuery.ui && global.jQuery.ui.version);
1175 | }
1176 | else if (script.identifier === "AMDLoader") {
1177 | alreadyLoaded = typeof global.define === "function" && !!global.define.amd;
1178 | }
1179 | if (!alreadyLoaded) {
1180 | scripts.push({ source: getAbsoluteUrl(script.url, hostRootUri) });
1181 | }
1182 | else {
1183 | anyCoreScriptLoaded = true;
1184 | }
1185 | }
1186 | });
1187 | if (hostPageContext.coreReferences.coreScriptsBundle && !anyCoreScriptLoaded) {
1188 | // If core scripts bundle exists and no core scripts already loaded by extension,
1189 | // we are free to add core bundle. otherwise, load core scripts individually.
1190 | scripts = [{ source: getAbsoluteUrl(hostPageContext.coreReferences.coreScriptsBundle.url, hostRootUri) }];
1191 | }
1192 | if (hostPageContext.coreReferences.extensionCoreReferences) {
1193 | scripts.push({ source: getAbsoluteUrl(hostPageContext.coreReferences.extensionCoreReferences.url, hostRootUri) });
1194 | }
1195 | }
1196 | // Define a new config for extension loader
1197 | var newConfig = {
1198 | baseUrl: extensionContext.baseUri,
1199 | contributionPaths: null,
1200 | paths: {},
1201 | shim: {}
1202 | };
1203 | // See whether any configuration specified initially. If yes, copy them to new config
1204 | if (initOptions.moduleLoaderConfig) {
1205 | if (initOptions.moduleLoaderConfig.baseUrl) {
1206 | newConfig.baseUrl = initOptions.moduleLoaderConfig.baseUrl;
1207 | }
1208 | // Copy paths
1209 | extendLoaderPaths(initOptions.moduleLoaderConfig, newConfig);
1210 | // Copy shim
1211 | extendLoaderShim(initOptions.moduleLoaderConfig, newConfig);
1212 | }
1213 | // Use some of the host config to support VSSF and TFS platform as well as some 3rd party libraries
1214 | if (hostPageContext.moduleLoaderConfig) {
1215 | // Copy host shim
1216 | extendLoaderShim(hostPageContext.moduleLoaderConfig, newConfig);
1217 | // Add contribution paths to new config
1218 | var contributionPaths = hostPageContext.moduleLoaderConfig.contributionPaths;
1219 | if (contributionPaths) {
1220 | for (var p in contributionPaths) {
1221 | if (contributionPaths.hasOwnProperty(p) && !newConfig.paths[p]) {
1222 | // Add the contribution path
1223 | var contributionPathValue = contributionPaths[p].value;
1224 | if (!contributionPathValue.match("^https?://")) {
1225 | newConfig.paths[p] = hostRootUri + contributionPathValue;
1226 | }
1227 | else {
1228 | newConfig.paths[p] = contributionPathValue;
1229 | }
1230 | // Look for other path mappings that fall under the contribution path (e.g. "bundles")
1231 | var configPaths = hostPageContext.moduleLoaderConfig.paths;
1232 | if (configPaths) {
1233 | var contributionRoot = p + "/";
1234 | var rootScriptPath = combinePaths(hostRootUri, hostPageContext.moduleLoaderConfig.baseUrl);
1235 | for (var pathKey in configPaths) {
1236 | if (startsWith(pathKey, contributionRoot)) {
1237 | var pathValue = configPaths[pathKey];
1238 | if (!pathValue.match("^https?://")) {
1239 | if (pathValue[0] === "/") {
1240 | pathValue = combinePaths(hostRootUri, pathValue);
1241 | }
1242 | else {
1243 | pathValue = combinePaths(rootScriptPath, pathValue);
1244 | }
1245 | }
1246 | newConfig.paths[pathKey] = pathValue;
1247 | }
1248 | }
1249 | }
1250 | }
1251 | }
1252 | }
1253 | }
1254 | // requireJS public api doesn't support reading the current config, so save it off for use by our internal host control.
1255 | window.__vssModuleLoaderConfig = newConfig;
1256 | scripts.push({ content: "require.config(" + JSON.stringify(newConfig) + ");" });
1257 | addScriptElements(scripts, 0, function () {
1258 | loaderConfigured = true;
1259 | triggerReady();
1260 | });
1261 | }
1262 | function startsWith(rootString, startSubstring) {
1263 | if (rootString && rootString.length >= startSubstring.length) {
1264 | return rootString.substr(0, startSubstring.length).localeCompare(startSubstring) === 0;
1265 | }
1266 | return false;
1267 | }
1268 | function combinePaths(path1, path2) {
1269 | var result = path1 || "";
1270 | if (result[result.length - 1] !== "/") {
1271 | result += "/";
1272 | }
1273 | if (path2) {
1274 | if (path2[0] === "/") {
1275 | result += path2.substr(1);
1276 | }
1277 | else {
1278 | result += path2;
1279 | }
1280 | }
1281 | return result;
1282 | }
1283 | function extendLoaderPaths(source, target, pathTranslator) {
1284 | if (source.paths) {
1285 | if (!target.paths) {
1286 | target.paths = {};
1287 | }
1288 | for (var key in source.paths) {
1289 | if (source.paths.hasOwnProperty(key)) {
1290 | var value = source.paths[key];
1291 | if (pathTranslator) {
1292 | value = pathTranslator(key, source.paths[key]);
1293 | }
1294 | if (value) {
1295 | target.paths[key] = value;
1296 | }
1297 | }
1298 | }
1299 | }
1300 | }
1301 | function extendLoaderShim(source, target) {
1302 | if (source.shim) {
1303 | if (!target.shim) {
1304 | target.shim = {};
1305 | }
1306 | for (var key in source.shim) {
1307 | if (source.shim.hasOwnProperty(key)) {
1308 | target.shim[key] = source.shim[key];
1309 | }
1310 | }
1311 | }
1312 | }
1313 | function getRootUri(webContext) {
1314 | var hostContext = (webContext.account || webContext.host);
1315 | var rootUri = hostContext.uri;
1316 | var relativeUri = hostContext.relativeUri;
1317 | if (rootUri && relativeUri) {
1318 | // Ensure both relative and root paths end with a trailing slash before trimming the relative path.
1319 | if (rootUri[rootUri.length - 1] !== "/") {
1320 | rootUri += "/";
1321 | }
1322 | if (relativeUri[relativeUri.length - 1] !== "/") {
1323 | relativeUri += "/";
1324 | }
1325 | rootUri = rootUri.substr(0, rootUri.length - relativeUri.length);
1326 | }
1327 | return rootUri;
1328 | }
1329 | function addScriptElements(scripts, index, callback) {
1330 | var _this = this;
1331 | if (index >= scripts.length) {
1332 | callback.call(this);
1333 | return;
1334 | }
1335 | var scriptTag = document.createElement("script");
1336 | scriptTag.type = "text/javascript";
1337 | if (scripts[index].source) {
1338 | var scriptSource = scripts[index].source;
1339 | scriptTag.src = scriptSource;
1340 | scriptTag.addEventListener("load", function () {
1341 | addScriptElements.call(_this, scripts, index + 1, callback);
1342 | });
1343 | scriptTag.addEventListener("error", function (e) {
1344 | notifyLoadFailed("Failed to load script: " + scriptSource);
1345 | });
1346 | safeAppendToDom(scriptTag, "head");
1347 | }
1348 | else if (scripts[index].content) {
1349 | scriptTag.textContent = scripts[index].content;
1350 | safeAppendToDom(scriptTag, "head");
1351 | addScriptElements.call(this, scripts, index + 1, callback);
1352 | }
1353 | }
1354 | function safeAppendToDom(element, section) {
1355 | var parent = document.getElementsByTagName(section)[0];
1356 | if (!parent) {
1357 | parent = document.createElement(section);
1358 | document.appendChild(parent);
1359 | }
1360 | parent.appendChild(element);
1361 | }
1362 | function getAbsoluteUrl(url, baseUrl) {
1363 | var lcUrl = (url || "").toLowerCase();
1364 | if (lcUrl.substr(0, 2) !== "//" && lcUrl.substr(0, 5) !== "http:" && lcUrl.substr(0, 6) !== "https:") {
1365 | url = baseUrl + (lcUrl[0] === "/" ? "" : "/") + url;
1366 | }
1367 | return url;
1368 | }
1369 | function triggerReady() {
1370 | var _this = this;
1371 | isReady = true;
1372 | if (readyCallbacks) {
1373 | var savedReadyCallbacks = readyCallbacks;
1374 | readyCallbacks = null;
1375 | savedReadyCallbacks.forEach(function (callback) {
1376 | callback.call(_this);
1377 | });
1378 | }
1379 | }
1380 | })(VSS || (VSS = {}));
1381 |
--------------------------------------------------------------------------------
/src/Vso.StateModelVisualization/scripts/lib/VSS.SDK.min.js:
--------------------------------------------------------------------------------
1 | //dependencies=
2 | // Copyright (C) Microsoft Corporation. All rights reserved.
3 | var XDM,VSS;(function(n){function u(){return new o}function s(){return Math.floor(Math.random()*(f-t)+t).toString(36)+Math.floor(Math.random()*(f-t)+t).toString(36)}var i,r,e;n.createDeferred=u;var o=function(){function n(){var n=this;this._resolveCallbacks=[];this._rejectCallbacks=[];this._isResolved=!1;this._isRejected=!1;this.resolve=function(t){n._resolve(t)};this.reject=function(t){n._reject(t)};this.promise={};this.promise.then=function(t,i){return n._then(t,i)}}return n.prototype._then=function(t,i){var u=this,r;return!t&&!i||this._isResolved&&!t||this._isRejected&&!i?this.promise:(r=new n,this._resolveCallbacks.push(function(n){u._wrapCallback(t,n,r,!1)}),this._rejectCallbacks.push(function(n){u._wrapCallback(i,n,r,!0)}),this._isResolved?this._resolve(this._resolvedValue):this._isRejected&&this._reject(this._rejectValue),r.promise)},n.prototype._wrapCallback=function(n,t,i,r){if(!n){r?i.reject(t):i.resolve(t);return}var u;try{u=n(t)}catch(f){i.reject(f);return}u===undefined?i.resolve(t):u&&typeof u.then=="function"?u.then(function(n){i.resolve(n)},function(n){i.reject(n)}):i.resolve(u)},n.prototype._resolve=function(n){if(this._isRejected||this._isResolved||(this._isResolved=!0,this._resolvedValue=n),this._isResolved&&this._resolveCallbacks.length>0){var t=this._resolveCallbacks.splice(0);window.setTimeout(function(){for(var i=0,r=t.length;i0){var t=this._rejectCallbacks.splice(0);window.setTimeout(function(){for(var i=0,r=t.length;it.MAX_XDM_DEPTH)||this._shouldSkipSerialization(n))return null;if(a=function(t,e,o){var s,c,l,a,v;try{s=t[o]}catch(y){}(c=typeof s,c!=="undefined")&&(l=-1,c==="object"&&(l=r.originalObjects.indexOf(s)),l>=0?(a=r.newObjects[l],a.__circularReferenceId||(a.__circularReferenceId=u++),e[o]={__circularReference:a.__circularReferenceId}):c==="function"?(v=h._nextProxyFunctionId++,e[o]={__proxyFunctionId:h._registerProxyFunction(s,n),__channelId:h._channelId}):c==="object"?e[o]=s&&s instanceof Date?{__proxyDate:s.getTime()}:h._customSerializeObject(s,i,r,u,f+1):o!=="__proxyFunctionId"&&(e[o]=s))},r||(r={newObjects:[],originalObjects:[]}),r.originalObjects.push(n),n instanceof Array)for(o=[],r.newObjects.push(o),e=0,c=n.length;e0&&(f=!0);lt=!0;f&&tt()}e||a?ht():w()})},0)}function tt(){var n={localStorage:JSON.stringify(u||{})};i.invokeRemoteMethod("updateSandboxedStorage","VSS.HostControl",[n])}function pt(n,t){var i;i=typeof n=="string"?[n]:n;t||(t=function(){});l?it(i,t):(r?e||(e=!0,s&&(s=!1,ht())):nt({usePlatformScripts:!0}),rt(function(){it(i,t)}))}function it(n,i){t.diagnostics.bundlingEnabled?window.require(["VSS/Bundling"],function(t){t.requireModules(n,i)}):window.require(n,i)}function rt(n){s?window.setTimeout(n,0):(f||(f=[]),f.push(n))}function wt(){i.invokeRemoteMethod("notifyLoadSucceeded","VSS.HostControl")}function ut(n){i.invokeRemoteMethod("notifyLoadFailed","VSS.HostControl",[n])}function ft(){return b}function bt(){return k}function et(){return c}function kt(){return d}function dt(n,t){return ot(n).then(function(n){return t||(t={}),t.webContext||(t.webContext=ft()),t.extensionContext||(t.extensionContext=et()),n.getInstance(n.id,t)})}function ot(t){var r=XDM.createDeferred();return n.ready(function(){i.invokeRemoteMethod("getServiceContribution","vss.hostManagement",[t]).then(function(n){var t=n;t.getInstance=function(t,i){return st(n,t,i)};r.resolve(t)},r.reject)}),r.promise}function gt(t){var r=XDM.createDeferred();return n.ready(function(){i.invokeRemoteMethod("getContributionsForTarget","vss.hostManagement",[t]).then(function(n){var t=[];n.forEach(function(n){var i=n;i.getInstance=function(t,i){return st(n,t,i)};t.push(i)});r.resolve(t)},r.reject)}),r.promise}function st(t,r,u){var f=XDM.createDeferred();return n.ready(function(){i.invokeRemoteMethod("getBackgroundContributionInstance","vss.hostManagement",[t,r,u]).then(f.resolve,f.reject)}),f.promise}function ni(n,t){i.getObjectRegistry().register(n,t)}function ti(n,t){return i.getObjectRegistry().getInstance(n,t)}function ii(){return i.invokeRemoteMethod("getAccessToken","VSS.HostControl")}function ri(){return i.invokeRemoteMethod("getAppToken","VSS.HostControl")}function ui(){o||(o=document.getElementsByTagName("body").item(0));i.invokeRemoteMethod("resize","VSS.HostControl",[o.scrollWidth,o.scrollHeight])}function ht(){var i=oi(t.webContext),f,g,n,s,o,b,k,nt,tt,d,u;if(window.__vssPageContext=t,window.__cultureInfo=t.microsoftAjaxConfig.cultureInfo,a!==!1&&t.coreReferences.stylesheets&&t.coreReferences.stylesheets.forEach(function(n){if(n.isCoreStylesheet){var t=document.createElement("link");t.href=h(n.url,i);t.rel="stylesheet";p(t,"head")}}),!e){l=!0;w();return}if(f=[],g=!1,t.coreReferences.scripts&&(t.coreReferences.scripts.forEach(function(n){if(n.isCoreModule){var r=!1,t=window;n.identifier==="JQuery"?r=!!t.jQuery:n.identifier==="JQueryUI"?r=!!(t.jQuery&&t.jQuery.ui&&t.jQuery.ui.version):n.identifier==="AMDLoader"&&(r=typeof t.define=="function"&&!!t.define.amd);r?g=!0:f.push({source:h(n.url,i)})}}),t.coreReferences.coreScriptsBundle&&!g&&(f=[{source:h(t.coreReferences.coreScriptsBundle.url,i)}]),t.coreReferences.extensionCoreReferences&&f.push({source:h(t.coreReferences.extensionCoreReferences.url,i)})),n={baseUrl:c.baseUri,contributionPaths:null,paths:{},shim:{}},r.moduleLoaderConfig&&(r.moduleLoaderConfig.baseUrl&&(n.baseUrl=r.moduleLoaderConfig.baseUrl),ei(r.moduleLoaderConfig,n),ct(r.moduleLoaderConfig,n)),t.moduleLoaderConfig&&(ct(t.moduleLoaderConfig,n),s=t.moduleLoaderConfig.contributionPaths,s))for(o in s)if(s.hasOwnProperty(o)&&!n.paths[o]&&(b=s[o].value,n.paths[o]=b.match("^https?://")?b:i+b,k=t.moduleLoaderConfig.paths,k)){nt=o+"/";tt=v(i,t.moduleLoaderConfig.baseUrl);for(d in k)fi(d,nt)&&(u=k[d],u.match("^https?://")||(u=u[0]==="/"?v(i,u):v(tt,u)),n.paths[d]=u)}window.__vssModuleLoaderConfig=n;f.push({content:"require.config("+JSON.stringify(n)+");"});y(f,0,function(){l=!0;w()})}function fi(n,t){return n&&n.length>=t.length?n.substr(0,t.length).localeCompare(t)===0:!1}function v(n,t){var i=n||"";return i[i.length-1]!=="/"&&(i+="/"),t&&(i+=t[0]==="/"?t.substr(1):t),i}function ei(n,t,i){var r,u;if(n.paths){t.paths||(t.paths={});for(r in n.paths)n.paths.hasOwnProperty(r)&&(u=n.paths[r],i&&(u=i(r,n.paths[r])),u&&(t.paths[r]=u))}}function ct(n,t){if(n.shim){t.shim||(t.shim={});for(var i in n.shim)n.shim.hasOwnProperty(i)&&(t.shim[i]=n.shim[i])}}function oi(n){var r=n.account||n.host,t=r.uri,i=r.relativeUri;return t&&i&&(t[t.length-1]!=="/"&&(t+="/"),i[i.length-1]!=="/"&&(i+="/"),t=t.substr(0,t.length-i.length)),t}function y(n,t,i){var f=this,r,u;if(t>=n.length){i.call(this);return}r=document.createElement("script");r.type="text/javascript";n[t].source?(u=n[t].source,r.src=u,r.addEventListener("load",function(){y.call(f,n,t+1,i)}),r.addEventListener("error",function(){ut("Failed to load script: "+u)}),p(r,"head")):n[t].content&&(r.textContent=n[t].content,p(r,"head"),y.call(this,n,t+1,i))}function p(n,t){var i=document.getElementsByTagName(t)[0];i||(i=document.createElement(t),document.appendChild(i));i.appendChild(n)}function h(n,t){var i=(n||"").toLowerCase();return i.substr(0,2)!=="//"&&i.substr(0,5)!=="http:"&&i.substr(0,6)!=="https:"&&(n=t+(i[0]==="/"?"":"/")+n),n}function w(){var t=this,n;s=!0;f&&(n=f,f=null,n.forEach(function(n){n.call(t)}))}var yt;n.VssSDKVersion=2;n.VssSDKRestVersion="2.2";var o,b,t,c,k,d,r,l=!1,e,a,s=!1,f,i=XDM.XDMChannelManager.get().addChannel(window.parent),u,lt=!1,g=function(){function n(){t&&t.call(this)}function i(){}var t;return Object.defineProperties(i.prototype,{getItem:{get:function(){return function(n){var t=this[""+n];return typeof t=="undefined"?null:t}}},setItem:{get:function(){return function(t,i){t=""+t;var u=this[t],r=""+i;u!==r&&(this[t]=r,n())}}},removeItem:{get:function(){return function(t){t=""+t;typeof this[t]!="undefined"&&(delete this[t],n())}}},clear:{get:function(){return function(){var r=Object.keys(this),t,i,u;if(r.length>0){for(t=0,i=r;t