Hello, habravchane-makovody!
Today we will try to understand the basics of creating a widget for the Dashboard in Mac OS X. We will need the Dashcode program designed just for this.
First, a little theory. Dashboard widget is a specially crafted webpage packed in a bundle along with all the resources. Well, some service information in the appendage. Accordingly, the programming language used is JavaScript. If you are already familiar with it, as well as with HTML / CSS (although this is hardly needed), then you are
already able to write a simple widget. If not, then you should not be upset, this language is very simple and intuitive, you can deal with it quickly enough. Further, I will assume that the JS reader is more or less familiar. The article itself is designed for beginners, so please do not scold for "too simple presentation and detailed chewing of elementary things." In addition, I also ask you not to kick in for the design - well, I'm not a designer, not a designer! If anyone wants to help with this business -
welcome =)
For convenience, all source codes (as well as a widget that is ready to use) are posted on the githab, the link at the end of the article. But do not rush to just download them! It is better to spend a little time and figure out how to create it all yourself.
')
So let's get started. As a goal for experiments, I, of course, chose our favorite habr. We will step by step make a widget that displays karma, rating and position in the ranking of habraudera of the selected habraiser.
Such a widget (well, very similar) was
already created by the neoromantic
hacker as much as in 2007, but the download links are not working, and besides, that article did not contain practical guidelines for creating such widgets.
Restoring the right We fill these shortcomings.
Create an empty project. To do this, run Dashcode and click in the right places. The process is trivial.
What do we see? The base widget has the main and auxiliary states (respectively, front and back in the left panel). The first is displayed in the normal mode of operation, the second - to configure the widget parameters. You can switch between them by selecting the corresponding items in the list of components on the left. We can safely remove all unnecessary, except for the “info” and “Done” buttons, which are used to switch between the main and auxiliary states. Further, for simplicity, we will call it the front and back sides of the widget.
Now on the front side of our widget (without a single line of code!) We throw the necessary components: several labels. To do this, open the library component - button

Library at the top right - and dragging components of the type “Text” to the widget. Now open

Inspector (also the button on the right above) and with its help we adjust the sizes, colors and so on for our widget. With his help, we will give meaningful names to our inscriptions - for easier access from the code.
On the back side of the kinema inscription and input box. Well, and another picture - for beauty. And in the end we get something like
Well, not bad, our GUI is ready! We can press
Cmd+R
and poke the buttons (i) and Done, admiring the effect of the widget's coup.
But one GUI is not enough for us, so go to the logic. To do this, at the top left click on the button

View and choose in the drop-down list Source Code. And we can already see our automatically generated JavaScript code. And we boldly begin to rule it!
To begin with, let's define the “architecture” of our widget. We will request a timer using
the habr API about the user, parse them and display the karma and rating on the front side of the widget. To do this, we declare the global variable
updateTimer
at the beginning of the main.js file, create the
startTimer(msec)
and
stopTimer()
functions that will work with this timer. Also create a function
updateStats()
, which will be called on a timer.
function startTimer(msec) { updateTimer = setTimeout("updateStats()", msec); } function stopTimer() { clearTimeout(updateTimer); } function updateStats() { alert("It works!"); startTimer(updateInterval); }
In the
show()
function, we
startTimer(5000)
call to start the timer when the widget is shown, and in the
hide()
function, respectively, we
stopTimer()
to save resources when the widget is not shown (Dashboard is not active). Now we can start our widget and see in the console (Cmd + Alt + 1) the output “It works!” Every 5 seconds.
But we are not interested in such nonsense, we want to delay karma and rating on a timer! So, in the
updateStatus()
function, instead of an alert, we will call the
execStatsRequest()
function (the Habr API advises not to tweak user data more than once per minute, so at the same time we increase the interval).
Now it's up to HTTP requests to the habrahabr API. We create new functions - execStatsRequest () and processStatsRequest (), which will serve to launch and process requests. Here is how they look at me:
function execStatsRequest() { if (userName().length > 0) { var Url = "http://habrahabr.ru/api/profile/" + userName() + "/"; alert("User: " + userName() + "\nURL: " + Url); xmlHttp = new XMLHttpRequest(); xmlHttp.onreadystatechange = processStatsRequest; xmlHttp.overrideMimeType('text/xml'); xmlHttp.open("GET", Url, true); xmlHttp.send(); } else { resetStats(); } } function processStatsRequest() { if (xmlHttp.readyState == 4 && xmlHttp.status == 200) { alert("xml is " + xmlHttp.responseXML); if (xmlHttp.responseXML == null) { resetStats(); } else { alert(xmlHttp.responseText); var error = xmlHttp.responseXML.getElementsByTagName("error")[0]; if (error != null) { alert("Some error occured!"); resetStats(); setLogin("<" + userName() + " not found>"); return; } var login = xmlHttp.responseXML.getElementsByTagName("login")[0].firstChild.nodeValue; var karma = xmlHttp.responseXML.getElementsByTagName("karma")[0].firstChild.nodeValue; var rating = xmlHttp.responseXML.getElementsByTagName("rating")[0].firstChild.nodeValue; var position = xmlHttp.responseXML.getElementsByTagName("ratingPosition")[0].firstChild.nodeValue; setLogin(login); setKarma(karma); setRating(rating); setPosition(position); } } }
Here we create the URL of the request, create an object of the XMLHttpRequest type, and with its help we request our data using the GET method. What is remarkable, we have to
force the answer to the MIME type
"text / xml" , because for some reason for some reason it returns the text / html. And in the
processStatsRequest()
function we parse the response received in XML. At the same time, we check it for an error - and notify the user about it.
Here it is necessary to distract from the code and configure the widget itself - allow it to work with the network. To do this, in the left pane, scroll down the list of items and see the Widget Attributes item. Here we just tick the “Allow Network Access”. You can also customize the widget id and its version. Now back to the code.
The functions
setLogin()
,
setKarma()
and their ilk display the string passed to them in the required fields on the front side. They were created for convenience and look the same, like that:
function setLogin(login) { document.getElementById("userName").innerText = login; }
The
resetStats()
function sets default values ​​for all fields. And the setUserName () and userName () functions serve as a wrapper over the input field for the name of the habrauser on the back side of the widget:
function userName() { return document.getElementById("nameEdit").value; } function setUserName(name) { document.getElementById("nameEdit").value = name; }
Well, the widget is almost ready. Why almost? Yes, because we need to save the entered username in the settings. To do this, we write the
loadPrefs()
and
savePrefs()
functions.
var preferenceKey = "habraUserName"; function loadPrefs() { var name = widget.preferenceForKey(widget.identifier + "-" + preferenceKey); alert(widget.identifier + "-" + preferenceKey); alert("name from preferences: " + name); if (name != null) setUserName(name); } function savePrefs() { widget.setPreferenceForKey(userName(), widget.identifier + "-" + preferenceKey); }
It is advisable to call these functions in the
show()
and
hide()
functions, respectively. The setting will be unique for each widget, which allows you to add widgets to the Dashboard with information on several users.
Well, now the widget is certainly ready to use. But there is no limit to perfection! We now localize our widget in order to have Russian and English versions. You can also do (as homework) localization into French and Japanese.
Go to our front, we call the inspector. Now we select our labels one by one and in the Localization section of the inspector set the value in English in the Value field. They will probably coincide with the preset values ​​of the Key field. These values ​​will be entered in the default (English) localization, which can be seen in the file
en.lproj/localizedStrings.js
.
Now add Russian localization. Transitions in Widget Attributes and in the Localization section add (in the left list) the Russian language. We select it, and now in the right list we can enter localized strings.
These values, respectively, will be written in
ru.lproj/localizedStrings.js
.
Actually, that's all, we can run our widget and admire our (or someone else's) karma! To install the widget in the Dashboard, you need to select Run & Share in the left pane and select Save to Disk or Deploy to Dashboard - depending on our needs.
If you want to download a ready-made widget, then you are welcome:
here it is ! The source code of the project for Dashcode can be taken on a
githaba .
I hope someone found this article useful and the list of widgets for the Dashboard will be updated with great things!