This article discusses how to use HTML/CSS/JavaScript/Ajax to create a simple lightweight Windows Vista/7 sidebar desktop gadget that allows to preview featured articles contributed to CodeProject's web site.
Editorial Note
This article is one of the articles in the series that demonstrate the development of the CodeProject's foundation miscellaneous tools and APIs that can be easily and conveniently used for offline article editing and previewing, showing a member's reputation statistics, using CodeProject's search engine to find articles about the topics a reader is interested in, etc. The Windows Vista/7 sidebar gadget introduced in this article is the most up-to-date web application that can be used to preview the featured contents once published at the CodeProject's web site by its members and contributors. Hope the following gadget will be extremely useful for a large audience of readers and members contributing and reading articles once ever published at the CodeProject's web site. Another valuable purpose of this paper and the code being uploaded is to demonstrate how to easily create and deploy a Windows Vista/7 sidebar gadget based on the example of creating the gadget mentioned above.
Introduction
"Gadgets are lightweight HTML and script-based applications that provide the ability to derive and present information or functionality from a variety of sources, such as local applications and controls, or websites and services. Developers with experience authoring webpages will find the process of creating a gadget very familiar..."
- Microsoft Developers Network (MSDN): Developing Gadgets For Windows Vista/7 Sidebar
In this article, we'll introduce the implementation of a simple lightweight Windows Vista/Seven desktop gadget that allows to preview the featured articles ever published and announced at the CodeProject's main page. The following gadget will automatically refresh in a certain interval of time specified by a user to render those new featured articles currently exhibited on the CodeProject's main web page. The using of this gadget allows a user to keep up with the latest CodeProject's featured articles published at the CodeProject's web site as if a user has manually opened the CodeProject's main page with a regular web browser.
Moreover, the following gadget provides a functionality that could be very useful to CodeProject's members contributing articles to the CodeProject's web site. In particular, when a featured article that a member once has published is presently announced in the CodeProject's main web page, the gadget reminds about it by either playing a notification sound or delaying on showing up the article's announcement:
During the discussion, we'll take a closer look at such fundamental aspects as using HTML/CSS markup language to create the core gadget's main web page rendered at the gadget's window in the desktop's sidebar when the gadget is run. Particularly, we'll explain how to use HTML and CSS-styles to author the basic layout of the core gadget's main web page. As well, we'll delve into such important topics of the web application development as the implementation of the basic gadget's functionality by using DHTML/JavaScript language, including the event handling mechanism that orchestrates the dynamic responsive gadget's behavior, Ajax-requests to download and retrieve the contents of the CodeProject's main web page as well as use the JavaScript's regular expressions RegExp
object, string manipulation functions and other parsing mechanisms such as (DOM - Document Object Model) used to either extract particular data on a featured article from the main web page being loaded or update the gadget's HTML-document contents programmatically, using timers to provide the gadget's dynamically refresh the contents rendered by the gadget in a specific interval of time, etc.
In particular, we'll demonstrate how to maintain the core gadget's main web page, that has a specific layout in which featured articles announcements are rendered. At the same time, we'll particularly spotlight on such essential topics as designing the gadget's core web page, using CSS-stylesheets to provide the attribute values of certain HTML-elements used in the core gadget's main web page HTML-document.
To provide the basic functionality of the gadget being discussed, we'll find out how to implement the number of functions written in JavaScript that will perform such tasks as rendering the core gadget's main web page, loading the CodeProject's main web page contents as well as retrieving and parsing them programmatically by using regular expressions and DOM, handling events to provide the basic gadget's dynamic behavior and functionality such as refreshing the gadget's window in a certain interval of time, using JavaScript's random number generator and math functions to randomly select the CodeProject's web site category under which the current featured article is being announced, firing the sound notifications when the featured article once contributed by a specific CodeProject's member is exhibited, etc.
Additionally, we'll spend a moment to discuss about creating the gadget's settings dialog box that allows to personalize the using of the following gadget by setting the timeout of the refresh interval in seconds, the name of CodeProject's member identity used for notification purposes. In addition, we will discuss about such aspects of the Windows 7 sidebar gadgets development as creating a gadget's installable package, using various built-in and external development tools to perform the gadget's main scripts debugging, etc.
Background
At this point, let's thoroughly discuss about the fundamental steps of the development process, which is based on using various scripting languages such as HTML/CSS and JavaScript as well as using Microsoft UI development tools for that purpose.
Creating Project
The first initial aspect that we're about to discuss at the very beginning in this article is creating the new project from "scratch" containing all necessary files required to provide the gadget's basic appearance and functionality under the Windows Vista/7 sidebar desktop application.
Specifically, at the very beginning, we should create a simple directory, at whatever location, locally on your PC, and name it, for example, CodeProject_FeaturedNotifier. Inside this directory, we simply intend to create the following set of sub-directories and files:
The list of sub-directories to be created inside project's directory:
- ./css - the sub-directory that will contain CSS-stylesheet files for the gadget's HTML-documents such as either main.html and settings.html
- ./js - the sub-directory that will contain the specific scripts that provide the gadget's basic functionality
- ./images - the sub-directory containing the images rendered within the gadget's main HTML-document
- ./sounds - the sub-directory containing the various sounds played when the gadget is used
The list of the gadget's project files:
- ./main.html - the core gadget's main web page
- ./settings.html - the main gadget's settings web page displayed within the settings dialog window
- ./gadget.xml - the gadget's main configuration file accessed and parsed by the sidebar application
- ./css/gadget.css - the main CSS-stylesheet file loaded by the gadget's main web page main.html
- ./css/settings.css - the CSS-stylesheet file used in the settings.html layout
- ./css/Main.min.css - the local copy of the main CodeProject's web page CSS-stylesheet loaded by the gadget
- ./js/main.js - the JavaScript file containing functions that implement the basic gadget's functionality
- ./js/settings.js - the JavaScript file containing functions written in JavaScript used to manage settings
- ./images/logo468x60.png - the image containing the CodeProject's logo rendered at the top of the gadget's main web page
- ./images/logo64x64.png - the image containing the gadget's icon
- ./sounds/notify.wav - the notification sound wav-file played when a member's featured article is announced
The Gadget's Main Web Page Layout using HTML/CSS
As we've already discussed above, in the introduction to this article, the main building block of a Windows sidebar gadget is the main web page (e.g., main.html) that basically contains the layout rendered in the gadget's window. To be more specific, in this particular case, we'll not, in fact, implement our own custom layout for that purpose. Instead, we'll use an already existing HTML-document loaded by the gadget from the original CodeProject's main web page.
Also, for that purpose, we'll need to download and use the appropriate CSS-stylesheet externally located at the https://codeproject.global.ssl.fastly.net/App_Themes/CodeProject/Css/Main.min.css?dt=2.8.170713.1 file. In this case, rather than downloading this CSS-stylesheet from the file whenever the gadget's main web page is loaded, we're just aiming to manually download this file and modify its contents by adjusting specific attribute values of several elements within the layout to make sure that the needed HTML-contents are properly rendered within the gadget's window being discussed.
The following HTML-source illustrates the gadget's main web page layout:
main.html
<html xmlns="<a href="http://www.w3.org/1999/xhtml">http://www.w3.org/1999/xhtml</a>">
<head>
<title>CodeProject's Featured Article Gadget</title>
<link type="text/css" rel="stylesheet" href="./css/gadget.css">
<link type="text/css" rel="stylesheet" href="./css/Main.min.css">
<script src="./js/main.js"></script>
<meta http-equiv="Content-Type" content="text/html; charset=Unicode" />
</head>
<body class="cpfa_gadget" onload="getFeaturedArticle(1);">
<table cellpadding="0" cellspacing="0">
<tr>
<td style="width: 100%; height: 40px">
<a href="<a href="https://www.codeproject.com/">
https://www.codeproject.com/</a>" target="_blank">
<img src="./images/logo468x60.png" style="width: 100%; height: 100%;"></a>
</td>
</tr>
<tr>
<td><div id="featured-article" style="display: block;
width: auto; height: auto;"></div></td>
</tr>
<tr><td align="center">
<span id="navigate"><a><p style="color:#6600ff;">
<b>BROWSE THIS ARTICLE!<b></p></a></span></div>
</td></tr>
</table>
</body>
</html>
./css/gadget.css
.cpfa_gadget{ margin: 0; width: 338px; height: 175px;
font-family: verdana; font-weight: bold; font-size: 20px; }
./css/Main.min.css: https://codeproject.global.ssl.fastly.net/App_Themes/CodeProject/Css/Main.min.css?dt=2.8.170713.1
The main difference between the downloadable and local version of the Main.min.css file is that the values of an attribute for certain page elements such as font size and layout dimensions were modified to provide the proper document contents rendering within the gadget's window.
As you've might noticed from the code listed above, the gadget's main web page main.html is really quite similar to any regular web page that was ever loaded by using a regular web browser. It normally contains meta-tags specifying such meta-data as the content-type and character set, one or more external links to the local CSS-stylesheet and script files, the body section of the document, in which we've created a table by adding specific <table></table>
HTML-tags to provide the layout (or just a "skeleton") of this page. The following table contains three rows and a single cell in each. The single cell of the first row is used to render the image of the CodeProject's logo added by using <img>
-tag as well as providing the basic functionality that allows to directly access the CodeProject's main web page in the external browser. This is done by creating an anchor tag with the value of parameter href
statically assigned to the URL-string containing the CodeProject's main web page address.
Another row correspondingly contains a cell similarly created by using <td></td>
tags, in which we normally render a dynamically loaded fragment of the CodeProject's main page exhibiting the featured article announcements. Typically, the following fragment is defined by the <div class="feature-article></div>
tags and all other HTML-tags inside the following div
-tag sections. Further, we'll discuss how to implement an appropriate JavaScript code to either dynamically load or perform modification of the following fragment of the CodeProject's main page HTML-document.
The cell of the final row is used to render an anchor tag <a href="article_currently_announced_url"></a>
used to easily navigate to the currently announced article web page in the CodeProject's web site with an external browser.
Moreover, the <body>
start tag also contains the event handling attribute onload="getFeaturedArticle(1);"
that is used to invoke the specific routine getFeaturedArticle(1);
written in JavaScript that actually renders the gadget's contents within the gadget's window.
Besides, the gadget's main page, at this point, we'll also discuss about another HTML-document used as the gadget's settings dialog window layout. This document is located at the same directory as the gadget's main web page and according to Windows Vista/7 sidebar gadgets development pattern has a standard reserved name such as settings.html:
settings.html
<html xmlns="<a href="http://www.w3.org/1999/xhtml">http://www.w3.org/1999/xhtml</a>">
<head>
<link href="./css/settings.css" rel="stylesheet" type="text/css" />
<script src="./js/settings.js"
type="text/javascript" language="javascript"></script>
<title>CodeProject's Settings</title>
</head>
<body>
<b>Interval:</b>
<select name="interval">
<option value="5">5</option>
<option value="15">15</option>
<option value="30">30</option>
<option value="60">60</option>
</select> secs
<br/><b>Member's Name (e.g. "John Smith"):</b>
<br/><input name="member" class="member"
type="text" maxlength="75" />
<br/><b>Autoreview:</b><input name="autoreview"
type="checkbox" maxlength="75" />
</body>
</html>
./css/settings.css
.member{ margin: 0; width: 260px; }
body { margin: 0; width:260px; height:120px; }
The settings.html document normally contains the either layout or the basic controls added by using specific HTML-tags such as <select><select>
or <input type="..."/>
to provide the ability to adjust the values of the main gadget's parameters and options, including the combobox to choose one of the gadget's refresh interval values, checkbox for specifying whether we want to autoreview our own featured article, the nickname of a CodeProject's member who once contributed articles.
Along with the specific HTML-documents that constituent the gadget being discussed, we also use multiple CSS-stylesheets located at the sub-directory css of the gadget's main project root directory. These files basically include:
- ./css/gadget.css - containing a single style of the gadget's main web page
- ./css/settings.css - containing styles for the settings HTML-document rendered within settings dialog window
- ./css/Main.min.css - containing style for the main page fragment externally loaded from the CodeProject's web site. This file is normally manually download and ported to the gadget's project.
Using JavaScript/AJAX to Implement the Gadget's Basic Functionality
Besides the web page development guidelines, at this time, we're about to discuss the details on how to implement the basic gadget's functionality by creating specific scripts written in JavaScript that orchestrate the basic functions that implement the dynamic behaviour and response of the following gadget.
Before we begin, let's again take a short glance at the HTML-document contained in the main.html file listed above. As we already know, the <body>
tag of the core gadget's main web page has the onload="getFeaturedArticle(1);"
event handler being specified as an attribute. The document loading event is normally fired and handled whenever the gadget's main web page is loaded and the ready state is changed to "complete". In this case, when the gadget is added to the sidebar by a user, its main web page is loaded locally and rendered within the gadget's window. This normally causes the following event to be fired and handled by executing a specific function being invoked. (e.g., getFeaturedArticle(1)
).
To be more specific, in this case, we're initially invoking the getFeaturedArticle(cat_id)
function implemented in the ./js/main.js script file, which performs the most of the job to dynamically load and render the gadget's main web page contents:
var connection_id = 0;
function getFeaturedArticle(cat_id) {
var xhttp = new XMLHttpRequest();
var timeout_interval = parseInt(System.Gadget.Settings.read("interval"));
xhttp.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 200) {
var cpfa_contents = this.responseText.match(
new RegExp('<div class="feature-article">' +
'(.*)' + '</div>', "gm")).join();
document.getElementById("featured-article").innerHTML = cpfa_contents;
var cpfa_div = getElementByClassName("div", "thumbnail");
var cpfa_title = getElementByClassName("div", "title");
var cpfa_url = 'https://www.codeproject.com' +
cpfa_div.childNodes.item(0).href.
substring(cpfa_div.childNodes.item(0).href.indexOf("/Articles"));
document.getElementById("navigate").childNodes.item(0).href = cpfa_url;
cpfa_div.childNodes.item(0).removeAttribute("href");
cpfa_title.childNodes.item(0).removeAttribute("href");
var author_name = System.Gadget.Settings.read("author_name");
if (author_name != "" && cpfa_contents.indexOf(author_name) > -1)
{
clearTimeout(connection_id);
System.Sound.playSound("./sounds/notify.wav");
if (System.Gadget.Settings.read("autoreview") == true)
window.location.href = cpfa_url;
connection_id = window.setTimeout(function() { getFeaturedArticle(1); },
timeout_interval * 1000 * ((timeout_interval > 5) ? 2 : 5));
}
}
}
xhttp.open("GET", 'https://www.codeproject.com/?cat='+cat_id, true);
xhttp.send();
var cpfa_cats = new Array(1,2,9,10,18,22,23,24,25,26,27,28);
connection_id = window.setTimeout(function() { getFeaturedArticle(cpfa_cats[Math.
floor(Math.random() * cpfa_cats.length) + 1]); }, timeout_interval * 1000);
}
When the following function is executed, it's normally performing an Ajax-request to the CodeProject's web site to loading its main page. This is typically done, by using XMLHttpRequest
or MSXML2.XMLHTTP.3.0
object being declared. Particularly to load the CodeProject's main web page contents, we're invoking two methods of either the XMLHttpRequest.open(method, url, async)
or the XMLHttpRequest.send()
to create an HTTP-request and dispatch it to the web server respectively. The XMLHttpRequest.open(method, url,
async)
method normally accepts the three main parameters as its arguments including the HTTP-method "GET
" or "POST
" added to the HTTP-header being sent, the exact URL-string with address of the web page being loaded and, also, the parameter whose value indicates whether we want to send a synchronous or asynchronous requests to server.
In this case, we're constructing the complete string
containing the URL of the page being loaded, by using JavaScript's string
manipulation concate operators. We're normally using the value of the cat_id
variable being appended, passed as an argument to the getFeaturedArticle(cat_id)
function. Also, we're using HTTP-method "GET
" and performing the asynchronous requests to the CodeProject's web server. After the appropriate HTTP-request is dispatched by calling the send
method, we'll retrieve the HTML-contents of the main page being loaded from the CodeProject's web site. To do this, we actually need to implement an anonymous callback function that will handle the onreadystatechange
event when the HTTP-request is completed.
By implementing the following callback function, we're actually creating the mechanism that performs the following tasks. When the onreadystatechange
event is fired and the specific callback is invoked, we're actually performing a check if the ready state of the document loaded is "completed" and the HTTP-status code is equal to the success (code: 200). If so, we're obtaining the contents being loaded from xhttp.responseText
property of the XMLHttpRequest
object.
Let's remind beforehand, that, in this case, we normally could have used another slightly different property xhttp.responseXML
of the XMLHttpRequest
object to slipstream the process of parsing the loaded contents by accessing the HTML-elements DOM objects. However, we cannot do this due to CORS cross-origin HTTP request policy violation set up and used in the CodeProject's web server that normally prohibits and blocks parsing HTML-documents as XML in the resources other than the CodeProject's web site.
That's actually why, to parse and extract data from the loaded HTML-contents, we're alternatively using JavaScripts regular expressions, specifically RegExp
generic object and match method that allows to use the regular expression such as <div class="feature-article">' + '(.*)' + '</div>
to locate and extract the fragment of the CodeProject's main web page announcing featured articles since it's used on the value of the responseText
property. According to the CodeProject's main web page document structure, the entire fragment that renders the presently shown featured article, located and extracted by using the above mentioned match
method and RegExp
object being created. As a result of the match method invocation, we're obtaining the return value which is an array of the fragments located by matching the regular expression's pattern. Finally, we're using the array method join()
to concatenate all items of the following array into a string. The return value of the join()
method is finally assigned to the cpfa_contents
variable whose value contains the HTML-fragment being extracted from CodeProject's main web page loaded:
var cpfa_contents = this.responseText.match(
new RegExp('<div class="feature-article">' + '(.*)' + '</div>', "gm")).join();
Since we've retrieved the contents of the CodeProject's main web page representing the featured articles by performing Ajax-request, we're aiming to render the following contents in the core gadget's main web page. To do this, we're obviously accessing the document object and invoke getElementById
method to get an object of the <div class="featured-article"></div>
scope in the gadget's main web page inside which we're about to render the featured article contents previous retrieved. Particularly, we'll use innerHTML
property of this object to dynamically add the following contents inside the following <div>
section by simply assigning the value of the cpfa_contents
variable to the following property:
document.getElementById("featured-article").innerHTML = cpfa_contents;
While processing the HTML-contents rendering a featured article loaded from the CodeProject's main web page, we actually need to extract values located at the specific <div>
and <span>
tags to obtain the URL of the featured article currently announced. Unfortunately, many elements of the ready HTML-document fragment being loaded does not contain the id
attribute that makes the process of using DOM-objects more complicated, since we need to get the objects of certain elements by their class-name.
From the other respect, another similar problem is that the sidebar desktop application, unlike the conventional IE web browser uses one of the existing minimalist beta-versions of WebBrowser ActiveX object to render core gadget's web page, that actually causes the sidebar application not to support many features of the JavaScript used in the regular browser. For example, unlike the fully-functional browsers, the getElementsByClassName(...)
method is not supported by the sidebar desktop application.
To get an object of a specific element by its class-name, we have to implement our own custom getElementByClassName
function, which allows to get such objects based on using another getElementsByTagName
JavaScript DOM-function, which is currently supported:
function getElementByClassName(tag_name, class_name) {
var object = document.getElementsByTagName(tag_name);
for (var index = 0; index < object.length; index++)
if (object[index].className == class_name)
return object[index];
}
In this case, we're obtaining the array of objects for each tag whose name is specified as the first parameter of this function and performing a simple linear search by iterating through the following array of objects finding those specific tag objects with the class-name value assigned to the second function's parameter. Since such an object has been located, the function returns its value immediately.
In the JavaScript code being discussed, we normally invoke the following getElementByClassName(...)
function twice to specific objects of the div
-tags having the class name equal to the value "thumbnail
" and "title
" respectively:
var cpfa_div = getElementByClassName("div", "thumbnail");
var cpfa_title = getElementByClassName("div", "title");
After we've obtained those objects and assigned them to the cpfa_div
and cpfa_title
variables, we're extracting the values of the href-attribute of the two anchor-tags located inside those div
-tags. The values of the href
attribute contains the URL of the currently announced featured article.
Later on, by using those values, we'll construct a complete URL-string containing address of the following article by using concate string manipulation functions including indexOf(...)
function that is used to locate the index of the fragment of the URL-string being parsed that starts with "/Article
" sub-string, as well as the substring(...)
function by using which we're extracting the URL string starting at the position returned by the indexOf(...)
function.
As we already know, the original value of the href-attribute assigned to the string
contains the wrong URL-address that begins with, for example, "file://....". This leads to the featured article currently announced not being able to be navigated by using an external web browser.
var cpfa_url = 'https://www.codeproject.com' + cpfa_div.childNodes.item(0).href.
substring(cpfa_div.childNodes.item(0).href.indexOf("/Articles"));
In this case, we're using cpfa_div
object previously obtained and accessing the childNodes
property to invoke the item(0)
method that allows to get an object of the first and only one child anchor tag inside the specific div
-tag section. After that, we're normally using the value of href
property containing the URL-string assigned to the specific href
-attribute of the anchor tag. Then, we're locating and extracting the substring and append it to the string
containing the URL-address of the CodeProject's main web page. Finally, by doing this, we're obtaining a valid URL-string for the current featured article and assign it to the cpfa_url
variable.
Later, we're assigning the value of cpfa_url
variable to the href-attribute of the anchor tag with id="navigate"
used to display the link with text "BROWSE THIS ARTICLE
" at the bottom of the gadget's main web page:
document.getElementById("navigate").childNodes.item(0).href = cpfa_url;
Also, to remove the unusable links, we need to remove the href
-attribute from the specific anchor-tags within the HTML-fragment by executing the following code:
cpfa_div.childNodes.item(0).removeAttribute("href");
cpfa_title.childNodes.item(0).removeAttribute("href");
As we've already discussed, the following gadget normally shows up a different featured article, whenever is updated, in a specific interval of time. To do this, we actually need to recursively invoke the same getFeaturedArticle
function by using it as a callback parameter of the window.setTimout
JavaScript function. This generic function normally executes getFeaturedArticle
after a specific interval elapsed. The recursive invocation to the getFeaturedArticle
provides the real-time gadget's contents updates. To make sure that we're rendering a new featured article after each gadget's refresh, we will implement and use the random selection of the CodeProject articles category. To do this, we actually need to declare and use a separate array containing items each one corresponding to a specific site category id. Actually, to do this, we'll use the JavaScript Math .floor()
and Math.random()
functions to generate a value of index of an item in the array of category ids. This way, we'll be selecting a distinct value of category-id and pass it as a parameter of the getFeaturedArticle
function during each function's invocation. This value is then appended to the URL-string used as a parameter of XMLHttpRequest.open
method invoked as it has already been discussed.
Another parameter value that is used along with the window.setTimeout
function is the integer value of timeout read from the gadget's settings by calling the following Windows Gadgets API function:
var timeout_interval = parseInt(System.Gadget.Settings.read("interval"));
Another essential aspect that I would like to make a focus on is the notification features implementation of the gadget being created. This typically can be implemented by using the same string
manipulation functions such as indexOf(...)
to locate and extract the data on the current featured article author's name. To obtain the author's name string value, we need to use the same Windows Gadget API function shown above particularly to retrieve the value of the author_name
settings property value.
Since then, we're performing another check if the author_name
property value is set and can be found in the text of the featured article announcement being rendered. If so, we're clearing the current timeout value used by the window.setTimeout
function using the value of the connection_id
variable returned whenever this function is called. Also we're using the appropriate JavaScript built-in multimedia function System.Sound.playSound("./sounds/notify.wav");
to play a notification sound contained in the ./sounds/notify.wav file. After that, we're performing a check if the autopreview
option is set by performing the similar steps described above. If so, we're setting the value of the window.location.href
to the URL-string of the currently exhibited featured article stored in the same cpfa_url
variable. Finally, we're modifying the value of the timeout interval parameter to make a delay on displaying the featured article published by a member's with a specific nickname, and invoke window.setTimeout
function over again, that causes a recursive call to the getFeaturedArticle
function. In this case, we're using an initial value of this function's single parameter equal to 1
. It means that, at this point, the process of the featured articles rendering will start over from the category having an id
value equal to 1
, as follows:
var author_name = System.Gadget.Settings.read("author_name");
if (author_name != "" && cpfa_contents.indexOf(author_name) > -1)
{
clearTimeout(connection_id);
System.Sound.playSound("./sounds/notify.wav");
if (System.Gadget.Settings.read("autoreview") == true)
window.location.href = cpfa_url;
connection_id = window.setTimeout(function() { getFeaturedArticle(1); },
timeout_interval * 1000 * ((timeout_interval > 5) ? 2 : 5));
}
At last, the final gadget's development topic which we're about to discuss, is creating the appropriate script and the number of functions used to either retrieve or set the value of the settings parameters that personalize the general look and behavior of the following gadget being used.
For that purpose, we must create the settings.js file that is loaded and used in the settings.html document. In the following file, we're implementing the two functions including the function that similarly handles the onreadystatechange
event as well as another anonymous callback function which is assigned to the System.Gadget.onSettingsClosing
property. The first function is used to read the values of each user-defined settings property and assign them to specific controls in the settings.html document that display those values when settings dialog window is open. The second function is executed to handle the event fired when the settings dialog window is about to close or commit by saving those parameter values being set by invoking System.Gadget.Settings.write
API-function:
document.onreadystatechange = function()
{
if (document.readyState=="complete")
{
member.value = System.Gadget.Settings.read("author_name");
interval.selectedIndex = System.Gadget.Settings.read("timeout");
autoreview.checked = System.Gadget.Settings.read("autoreview");
}
}
System.Gadget.onSettingsClosing = function(event)
{
if (event.closeAction == event.Action.commit)
{
System.Gadget.Settings.write("author_name", member.value);
System.Gadget.Settings.write("timeout", interval.selectedIndex);
System.Gadget.Settings.write("autoreview", autoreview.checked);
System.Gadget.Settings.write("interval",
interval.options[interval.selectedIndex].value);
event.cancel = false;
}
}
One more topic which is also worth discussing in this article is creating a gadget's "flyouts", but unfortunately the following discussion actually goes beyond the main point and subject discussed in this article so far. The gadget being introduced and developed is not intended to contain any flyouts that could be used to render various extra info on the featured article being announced.
Creating the Gadget's Configuration XML Script
The final essential step for developing the gadget being discussed, that makes the following gadget ready to be installed and used, is creating the gadget's main configuration file gadget.xml whose contents are listed below:
="1.0"="utf-8"
<gadget>
<name>CodeProject's Featured Articles</name>
<namespace>CodeProject.API</namespace>
<version>1.0.0.0</version>
<author name="Arthur V. Ratz">
<info url=
"<a href="https://www.codeproject.com/script/Membership/View.aspx?mid=11760135">
https://www.codeproject.com/script/Membership/View.aspx?mid=11760135</a>" />
<logo src="./images/logo64x64.png" />
</author>
<copyright>CPOL © Arthur V. Ratz</copyright>
<description>"Preview CodeProject's Featured Articles"</description>
<icons>
<icon height="64" width="64" src="./images/logo64x64.png" /></icons>
<hosts>
<host name="sidebar">
<base type="HTML" apiVersion="1.0.0" src="./main.html" />
<permissions>Full</permissions>
<platform minPlatformVersion="1.0" />
</host>
</hosts>
</gadget>
As you've might noticed from the XML code above, the following configuration script has the following tags, each one corresponding to a specific entity having multiple parameter attributes. In particular, in this script, we're using such descriptive tags as <name>
which allows to specify the name of the gadget, namespace, version, author name, copyright details, the external author's URL link, etc. Also, within the following script, we're creating such other entities as the <icon>
which basically provides the path to the gadget's logo file and its sizes. The special case among those gadget's configuration entities is the <base>
, <permissions>
and <platform>
tags by adding which we actually specify the path and filename of the core gadget's main web page HTML-document as well as the severity of the sidebar security policy and the sidebar platform minimal version. Notice that the gadget won't run unless you create the gadget.xml file with all these above mentioned entities.
Using the Code
How to Deploy, Install, Use this Project
Deploying the Complete Gadget's Package
Finally, after we've created all project files as the result of the gadget's development process, we'll further need to create a package containing the gadget being developed. This can be typically done by using, for example, one of the existing file compressors supporting the creation of zip-archives. As the result of compressing all those files from the project's parent directory, we'll finally get CodeProject_FeaturedNotifier.zip file. Now, to be able to install and use the gadget, we'll actually need to simply rename the following archive file to CodeProject_FeaturedNotifier.gadget
. Since then, to install the newly created gadget, we'll have to double click on the following file and confirm installing it when prompted by a specific dialog box. Finally, the installed gadget will appear in the desktop's sidebar:
To explore and delve into the source code included as a part of the gadget's project, just navigate to the other sub-directories such as root directory, css, js, images and sounds.
Installing the Gadget Ready
To use the gadget being discussed, just download the CodeProject_FeaturedNotifier.zip archive file and navigate to the CodeProject_FeaturedNotifier\installer directory and just run the gadget's package (e.g., CodeProject_FeaturedNotifier.gadget
). This will install the following gadget in the Windows sidebar desktop application and make it visible on the desktop. Please note that the Windows Vista/7 sidebar gadgets being installed are located at
%USERPROFILE%\AppData\Local\Microsoft\Windows Sidebar\Gadgets\CodeProject_FeaturedNotifier.gadget
Debugging Guidelines
Also, you can try debugging the following source code while the gadget has been added to the sidebar and run by using Microsoft Visual Studio debugger. For that purpose, under the IE web browser settings, Internet Explorer 11\Tools\Internet Options, untick two checkboxes: disable script debugging (Internet Explorer) and disable script debugging (other):
After that, launch Microsoft Visual Studio and navigate to the Microsoft Visual Studio\debug menu and toggle Attach To Process option. Finally, the dialog window in which we'll need to locate and specify the sidebar process being run, will appear:
After selecting the sidebar process, the scripts debugging is just to begin. To perform the actual debugging, we'll need to navigate the Visual Studio solution explorer's left pane to locate our gadget source files for debugging. By doing this, the gadget's scripts contents will appear in the Visual Studio's editor which allows, for example, to either set debug breakpoints or inspect the value of the particular variables with the autos or watch pane:
Optionally, we can use the IE or other web browser's built-in native development tools for the similar purpose. To do this, just navigate to Tools\F12 Developer Tools option to either preview an HTML-document's DOM structure or launch the scripts debugging process.
Points of Interest
I have created the Windows Vista/7 sidebar gadget just for fun and for my own needs, but it also can be useful to the other CodeProject.com members who contribute various articles, as well as for the audience of readers including the entrepreneur and experienced IT-professionals who are passionate about reading articles published at the CodeProject web site.
Feedback
I encourage everyone who has read and reviewed this article to send me any feedback including the gadget's bugs reports, your special thanks, wishes to use a better gadget or code, various comments, votes, etc. The readers can contact me by submitting their posts to the article's discussion forum below.
That's all, folks!
History
- 28th July, 2017 - First revision published (discusses basic gadget's functionality)