Last week, we took a look at how to create interactive mashups with Visio diagrams without writing any code. If you need more flexibility in creating rich diagram mashups than offered out of the box by web part connections, you can use the Visio Services JavaScript Mashup API.
In this post we'll review the breadth of possibilities by the Visio Services Mashup API and show you how to get started coding with a few simple examples.
Overview of the Visio Services JavaScript Mashup API
In a nutshell, the Visio Services JavaScript Mashup API enables developers to access and manipulate the Visio web drawing, its pages and shapes. Some key scenarios enabled by the Mashup API are:
- Interacting with web page elements that don’t expose web part connections.
- Creating visual markup on the Visio drawing canvas.
- Writing custom handlers for mouse events within the drawing.
- Exposing diagram data, such as shape name and shape data, to your solution.
- Combining data from inside and outside a diagram to build dynamic composite mashups.
Objects
The class diagram below summarizes the available objects in the API. Notice that related class members are collected into functional groups – each group is discussed in more detail in the API in the appendix of this article.
Events
Like many JavaScript APIs, it's important to note that the Visio Services JavaScript API is event-based. To program against the Visio Web Access web part, you must catch the events it exposes by writing event handler functions. The diagram below is the state chart of VwaControl events:
Typically, when the onApplicationLoad event fires you'll want to get a reference to the vwaControl object and set-up handlers for ondiagramcomplete. Note that vwaControl is the only valid object in the object model until ondiagramcomplete fires. Then, when the ondiagramcomplete event fires you'll want to get a reference to the currently loaded page and set-up handlers for the various mouse events.
Getting Started
Now that you understand the object and event model of the API, let's get started coding. Before you can interact programmatically with a Visio Web drawing you must add a Visio Web Access web part to a web part page, load a VDW file into it and add a Content Editor Web part to the page to contain your JavaScript.
To do so, assuming you have "Contribute", "Design" or "Full Control" permissions to a page in SharePoint Server 2010, you should:
- Create a drawing in Visio and publish it to SharePoint document library as a VDW file.
- Create a .JS file in any JavaScript editor and copy paste the following contents into it. Once you're done save it to the same document library as the drawing in step #1.
1: <script language="javascript">
2: // A hook into AJAX's Sys.Application.load event, raised after all scripts
3: // have been loaded and the objects in the application have been created
4: // and initialized.
5: Sys.Application.add_load(onApplicationLoad)
6:
7: // Global variables for the application
8: var webPartElementID = "WebPartWPQ3"; //Change this to the appropriate web part ID.
9: var vwaControl;
10: var vwaPage;
11: var vwaShapes;
12:
13: // Function Name: onApplicationLoad()
14: // Parameters: None
15: // Description: This function handles the AJAX's Sys.Application.load event
16: // When this event fires the function captures references to
17: // the Visio Web Access web part object and registers
18: // the following Visio Web Access specific event handlers:
19: //
20: // 1. diagramcomplete: raised when the request for a web
21: // drawing page finishes.
22: function onApplicationLoad() {
23: try{
24: vwaControl= new Vwa.VwaControl(webPartElementID)
25: vwaControl.addHandler("diagramcomplete", onDiagramComplete);
26: }
27: catch(err){
28: }
29: }
30:
31: // Function Name: onDiagramComplete
32: // Parameters: None
33: // Description: This function handles the onDiagramComplete event which is
34: // raised when a request for a web drawing page completes.
35: // When the event fires, this function captures references
36: // to script's global variables such as the current page, the
37: // collection of shapes on the page. It also sets the page zoom to
38: // "fit page to view" to give a full view of the page to
39: // users and registers the following Visio Web Access specific
40: // event handlers:
41: //
42: // 1. shapemouseleave: raised when the mouse enters a shape.
43: //
44: // 2. shapemouseenter: raised when the mouse leaves a shape.
45: //
46: // 3. shapeselectionchanged: raised when the active selection
47: // is changed from one shape
48: // to another.
49: function onDiagramComplete() {
50: try {
51: vwaPage = vwaControl.getActivePage();
52: vwaPage.setZoom(-1);
53: vwaShapes = vwaPage.getShapes();
54: vwaControl.addHandler(“shapeselectionchanged”, onShapeSelectionChanged);
55: vwaControl.addHandler(“shapemouseenter” , onShapeMouseEnter);
56: vwaControl.addHandler(“shapemouseleave”, onShapeMouseLeave);
57: }
58: catch(err) {
59: }
60: }
61:
62: // Put the sample code discussed later in the blog below...
63: </script>
- Edit the SharePoint page that will host the mash-up and add a Visio Web Access web part and configure it to load the web drawing published in step #1.
- On the same page, add a Content Editor Web Part and configure it to load the .JS file you created in step #2. This process is similar to step 3, however the Content Editor web part is in the Media and Content category of SharePoint web part picker and you need to set the "URL" property of the web part.
- Get out of page edit mode. Check the bottom left corner of your browser. If there is no JavaScript error, move to step 8. If there is however, you need to update the second line of the code snippet above which reads “var webPartElementID = "WebPartWPQ3";” with the ID that SharePoint gave to the Visio Web Access web part.
- Open the SharePoint web part page in source mode (in IE, right click and “View Source”) and search for class="VisioWebAccess",then back track to the immediate parent <div> tag and look for an attribute of the form id="WebPartWPQ?".
- Once you have this ID, replace it in the JS file and save it back to the document library
- Reload your mash-up page.
Tada! You have the basics of a mash-up. That said, it doesn't do much except get references to a few basic objects and setup some event handlers. The next section gives you a few useful examples that build on this base.
Example #1: Overlays on hover
In this example we will explore how to create an overlay that appears when a user hovers over a shape. In this scenario, the manager of a grocery store visualizes sales data on a floor plan of his store: sales figures are represented as colors on a store shelf. Now, let’s say we want to show a picture of the best-selling product of the day on each shelf as a user hovers his mouse over a shelf. This product changes daily and is calculated at night by running database queries; this data is then exposed through shape data when the drawing is refreshed.
Key JavaScript API Capabilities Used in this Solution
Capability | In this example… |
Creating dynamic visual markup on the Visio drawing canvas. | Product pictures take up a lot of screen real-estate -- you can't show all of them on the drawing without hiding the floor plan. Using overlays let you "fade them in" and "fade them out" as needed. |
Writing custom handlers for mouse events within the drawing. | The fade-in and fade-out feature discussed above requires you to handle the onmouseenter and onmouseleave events. |
Combining data from inside and outside a diagram to build dynamic composite mashups | The best-selling product on a shelf changes daily and is stored in the document. The product is stored outside the diagram. The mash-up must be able to pull these two sources of visual data into one diagram. |
Code
To solve this problem, you'll want to draw an overlay on the shape when the mouse enters the shape, and remove the overlay when the mouse leaves the shape. Let's take a look at one possible implementation:
1: // Function Name: shapeMouseEnter
2: // Parameters:
3: // source -- a reference to the base HTML element of the vwaControl.
4: // args -- the shape ID of the shape the mouse entered.
5: // Description:
6: // On entering a shelf shape (a shape that contains a "Best Seller" shape
7: // data), this function extracts that shape data then overlays the
8: // corresponding image on the shape.
9: shapeMouseEnter = function(source, args)
10: {
11: //Get the name of the shape that was just entered
12: var shape = vwaShapes.getItemById(args);
13:
14: //Determine the best seller for that shelf. This information is stored in a shape data //named “Best Seller” item that updates over time.
15: var bestSeller;
16: var shapeData = shape.getShapeData();
17: for (var j=0; j<shapeData.length; j++) {
18: if (shapeData[j].label = "Best Seller") {
19: bestSeller = shapeData[j].value;
20: }
21: }
22:
23: //Depending on which shape this is, draw the correct overlay.
24: switch(bestSeller)
25: {
26: case "productA":
27: shape.addOverlay(
28: "myOverlay" ,
29: "<Image Source=\"productA.jpg\" Width=\"50\" Height=\"70\"><\/Image>",
30: vwaVerticalAlignment.Top,
31: vwaHorizontalAlignment.Left,
32: shape.getBounds().width,
33: shape.getBounds().height);
34: break;
35:
36: //If the best seller is product B, then display an overlay with product B.
37: case "productB":
38: shape.addOverlay(
39: "myOverlay" ,
40: "<Image Source=\"productB.jpg\" Width=\"50\" Height=\"70\"><\/Image>",
41: vwaVerticalAlignment.Top,
42: vwaHorizontalAlignment.Left,
43: shape.getBounds().width,
44: shape.getBounds().height);
45: break;
46:
47: //You can add more cases below as needed for different shelves.
48:
49: default:
50: }
51: }
52:
53: // Function Name: shapeMouseLeave
54: // Parameters:
55: // source -- a reference to the base HTML element of the vwaControl.
56: // args -- the shape ID of the shape the mouse just left.
57: // Description:
58: // On leaving a shelf shape (a shape that contains a "Best Seller" shape
59: // data), this function removes the existing overlay on that shape.
60: shapeMouseLeave = function(source, args)
61: {
62: //Get the name of the shape that was just entered
63: var shape = vwaShapes.getItemById(args)
64:
65: //And remove the overlay
66: shape.removeOverlay("myOverlay");
67: }
Getting this to work
To add this to your solution, copy/paste the above code presented in the earlier “Getting Started”. Note that this code makes the following assumptions:
- There are shapes in the drawing with the shape data whose label is "Best Seller" and whose value is "product?" (where ? is A, B etc...).
- You have bitmap images of these products, named productA.jpg etc... stored in the same directory as the .JS file. They should be 50 pixels wide and 70 pixels high.
Example Scenario #2: Search and Highlighting...
In this second example, the scenario is a network diagram mashup that allows a network administrator to find computers in a network diagram that have a particular string and to highlight them; this is useful when looking at a computer topology to quickly spot computers that meet a particular spec, or contain a particular set of characters in their name. We are effectively “scraping” the diagram to find shapes that contain a particular piece of text in either shape text or shape data.
Key JavaScript API Capabilities Used in this Solution
Capability | In this example… |
Interacting with web page elements that don’t expose web part connections. | The search text box in this solution is not a SharePoint web part. |
Exposing diagram data, such as shape name and shape data to your solutions. | The solution searches on data within the drawing. |
Combining data form inside and outside a diagram to build dynamic composite mashups | Search text (data from outside the document) changes throughout a session, diagram markup must do so at the same rate. |
Code
To solve this problem, the algorithm iterates through all the shapes on the page and does a string compare against the name/shape data of that shape and highlight the shapes that contain that string. One implementation of this scenario is detailed below:
1: // Creates the user interface for this code sample which consists of a
2: // text box to enter the find-text and a button to trigger the
3: // find operation.
4: document.write("Find text:");
5: document.write("<BR><input type=\"text\" id=\"findText\"><\/input>");
6: document.write("<BR><Button id=\"findButton\" type=\"button\""+
7: "onClick=\"find()\">Find<\/Button>");
8:
9: // Function Name: find
10: // Parameters: None
11: // Description: This function iterates through vwaShapes and highlights
12: // the shapes who's name, shape data or shape hyperlinks
13: // contains the find-text. The search is case insensitive.
14: function find() {
15: // Iterate through the collection of shapes on the page, if either the
16: // shape name, shape data or shape hyperlinks contain the find-text,
17: // highlight it and jump back to the "shapeContainsFindText:" label
18: // to process the next shape.
19:
20: shapeContainsFindText:
21: for (var i=0; i<vwaShapes.getCount(); i++) {
22: // Extract the find-text from the text box and covert it to lower case.
23: var findText = document.getElementById('findText').value.toLowerCase();
24:
25: // Get a reference to the shape at index "i" of the shape collection
26: // and remove existing highlights.
27: var shape = vwaShapes.getItemAtIndex(i);
28: shape.removeHighlight();
29:
30: // Get a reference to the shape name, shape data and shape hyperlinks
31: // collections.
32: var shapeName = shape.getName();
33: var shapeData = shape.getShapeData();
34: var shapeHyperlinks = shape.getHyperlinks();
35:
36: // Highlight the shape if the find text is found within the shape name
37: // and return to the top of the loop to process the next shape.
38: if(shapeName.toLowerCase().indexOf(findText) != -1) {
39: shape.addHighlight(2,"#00FF00");
40: continue shapeContainsFindText;
41: }
42:
43: // If the shape name did not contain the find-text, search through the
44: // the shape's shape data values for the find-text. If the find-text
45: // is found highlight the shape and return to the top of the loop to
46: // process the next shape.
47: for (var j=0; j<shapeData.length; j++) {
48: if (shapeData[j].value.toLowerCase().indexOf(findText) !=-1) {
49: shape.addHighlight(2,"#00FF00");
50: continue shapeContainsFindText;
51: }
52: }
53:
54: // If the shape name or shape data did not contain the find-text,
55: // search through the shape's hyperlinks for the find-text. If the
56: // find-text is found, highlight the shape and return to the top of
57: // the loop to process the next shape.
58: for (var j=0; j<shapeHyperlinks.length; j++) {
59: if (shapeHyperLinks[j].value.toLowerCase().indexOf(findText) !=-1) {
60: shape.addHighlight(2,"#00FF00");
61: continue shapeContainsFindText;
62: }
63: }
64:
65: // If the text hasn't been found in the web drawing, then warn the user.
66: alert("Text not found in this web drawing!");
67: }
68: }
Getting this to work
To add this to your solution, copy/paste the above code presented in the earlier “Getting Started” section. Note that using the code above, the find text box and button will appear in the place of the Content Editor web part.
Example #3: Workflow Visualization
For our third example, we’d like to highlight an in-house use of the Visio Services JavaScript OM, namely the new SharePoint workflow visualization feature in SharePoint 2010. In a nutshell, using this feature, you can now create a workflow using Visio/SharePoint Designer and visualize the progress of an instance of that workflow in SharePoint.
The progress visualization is in fact an example of using the Visio mash-up to overlay data on an existing diagram. The SharePoint workflow team used the JavaScript API to overlay workflow instance data in the context of the workflow diagram. Taking a look at the picture below, all the visuals and the text highlighted in the green box are not actually part of the published diagram but are actually drawn over the workflow shape at run-time using the mash-up API. As the data changes during the lifetime of a workflow, so will the overlay.
As you can see, the integration between the Visio published diagram and the overlays is pretty seamless!
More information to come…
We’re in the process of developing a full MSDN reference for the JavaScript Mashup API, including samples and an overview document. We’ll make sure to announce this on the blog when it’s made publicly available. Please note that the contents of this blog entry are preliminary and may change by the time Visio Services 2010 is officially released.
Try it out
We hope this blog post has given you enough to get started building your own JavaScript solutions. Feel free to share your thoughts either through send-a-smile or by commenting on this blog post.
Appendix – API Reference