It's no secret that printed books are a thing of the past. They are being replaced by a new generation of devices (and programs with them), which allow not only to read the material, like a book, but also to diversify reading with interactive inserts. It can be like some dynamically changing material (twitter tape, etc.), or some interactive content (animation of condenser, etc.)
Apple has released the iBook Author application, which allows you to create interactive books for the iBooks application for ipad devices. Today, I propose to get acquainted with how to create HTML widgets for this application on the example of the Twitter widget.
To create widgets there are many programs, the cost of which varies from 0 (Dashcode) to 60 (
Tumult Hype ).
We will create our own widget “manually”, without using specialized programs.
Now I propose to move from words to deeds.
Widget structure
HTML widget for iBook is a folder with wdgt extension.
When creating an HTML widget, you can select the following required files:
- Default.png = File - a preview of the widget. This image will be displayed to the user until he launches the widget. The file name is strong and case sensitive. The resolution of the widget (unless otherwise specified) will be equal to the resolution of this image.
Retina: If you create a widget that supports Retina displays, then a Default@2x.png file must also be created, which has twice the resolution than the original Default.png. - Primary HTML file - the name of this file can be set to any. It is the main functional part of the widget. It can include both css and js files (there are no restrictions here, everything is the same as writing web pages)
Retina: For support, you need to write the same way as web pages for Retina displays ( articles on this topic already flashed on Habré) - Info.plist - the config file for the widget. Contains information about the name of the widget, its resolution, the main HTML file, etc. Let us dwell on it a little more ...
')
Info.plist - what's in the config?
This config file is nothing more than an xml file, in which parameters are written as
<key></key> < ></ >
Parameters used:
- CFBundleDevelopmentRegion - An optional parameter, as a rule, describes the widget's native language. Its values ​​can be English, Russain
- CFBundleDisplayName - Required parameter, the value of which is a string with the name of the widget. In our example, we specify Twitter
- CFBundleIdentifier - A required parameter is a string that uniquely identifies the widget. Uses an inverted domain name. For example: ru.MySite.widgets.Twitter
- CFBundleName - Optional parameter, represented by a string specifying the name of the widget.
- CFBundleVersion and CFBundleShortVersionString - optional parameters - strings containing the version of the widget. “Long” and “short” versions respectively
- Height and Width are optional parameters. - the value of these parameters are the numbers that define the height and width of the widgets
- IBNotifiesOnReady - Optional parameter. When this parameter is set to true, the widget must inform the application when it is necessary to go to the HTML code, removing the image (when loading the widget, until it is ready, Default.png is displayed) This parameter should be set to true if the widget must do “complex calculations”, or collect data from several servers ... (in general, when the widget will “prepare the information” for some time). In other cases, it is better to set it to false. Switching from image to HTML code occurs via widget.notifyContentIsReady ()
- MainHTML - represented by a string, specifies the name of the main html file of the widget
The use of these parameters will be sufficient to create the first widgets.
Now let's proceed directly to the implementation of the widget.
Directly widget implementation
Planning a widget job
In order for the author to control the widget, let's take the initial conditions:
- When the widget starts, it sends a request to the server, gets a list of users \ hashtags that should display
- Generates data in the “format” needed for twitter
- "Import" Twitter API script, waiting for its response
- Decorates data in a user-friendly view
In this widget we will use the jQuery library (to simplify working with JSON and create AJAX requests)
Html file
It is not a “practical” use in this example. Contain a container for future tweets:
<!DOCTYPE html> <html> <head> <meta http-equiv="content-type" content="text/html; charset=utf-8"> <script type="text/javascript" src="jquery.min.js"></script> <link rel="stylesheet" type="text/css" href="style.css"/> </head> <body> <input type="hidden" id="ID" value="SomeWidgetIDForServerQuery" /> <div id='tweet-container'> </div> <script type="text/javascript" src="ServerQuery.js"></script> </body> </html>
JavaScript stuffing
The main task of the script will be sending AJAX requests to servers. There is one limitation here. In direct form, such requests can not be sent to us. More precisely, the request will go to the server, the server will process it, but the widget will not be able to accept the data. Therefore, the way out of this situation is to use JSONP.
What it is? This is a pattern that will allow us to send data to a remote server and “bypass” the domain restriction policy.
What does this method look like?
The widget creates a GET request, in the parameters of which (besides the necessary information) indicates the callback function. The server returns data wrapped into a javascript function call in the format
callback-( JSON);
Define the data that the server will send: let's say the data will contain only one parameters parameter, which will be represented as an array of name elements (including both users and hashtags)
Sample server response:
jsonp_callback({"results":[{"name":"user1"},{"name":"#hashCode1"}]})
Let's write a script that will make a request to the server and process the received data, transferring it to the “necessary” format for twitter:
(the format is simple - user tweets are defined as from: user, the connection between various search filters is performed using "+ OR +", hash tags do not change except for # transfer to the character% 23)
// var requestStr = ""; // " " function PrepareItem(item) { if (requestStr != "") requestStr += '+OR+'; if (item[0] != "#") {// requestStr += 'from:' + item; } else {// - requestStr += '%23'; for (var index = 1; index < item.length; index++) requestStr += item[index]; } } $(document).ready(function() { $.ajax({ // dataType: 'jsonp', jsonp: 'jsonp_callback', // GET -callback url: "http://mySite.ru/widgets/twitter.php", data: { ID:$('#ref').val() }, success: function( result ) { $(result.results).each(function(item) { PrepareItem(item.name); }); // ( ) CreateQueryToTwitter(requestStr); } });
However, JSONP assumes the second method of data transfer (physically it does not differ from the first one - the difference is only in software implementations) - it is the second method that we will use to send the request to Twitter.
This method consists of adding a script element to HTML, whose body is empty, and src contains the same GET request indicating the callback function and other parameters.
After sending the request, we just have to parse the Twitter response (the most useful information is in the results element) and format it.
function CreateQueryToTwitter(request) { var newScript = document.createElement('script'); newScript.setAttribute("type", "text/javascript");// newScript.setAttribute("src", "http://search.twitter.com/search.json?q=" + request + "&callback=tweetResponse&rpp=40"); // HTML document.getElementsByTagName("head")[0].appendChild(newScript); } function tweetResponse(result) { var container=$('#tweet-container'); container.html(''); // $(result.results).each(function () { var str = (' <div class="tweet">\ <div class="avatar"><a href="http://twitter.com/'+this.from_user+'" target="_blank"><img src="'+this.profile_image_url+'" alt="'+this.from_user+'" /></a></div>\ <div class="user"><a href="http://twitter.com/'+this.from_user+'" target="_blank">'+this.from_user+'</a></div>\ <div class="txt">' + formatTweet(this.text) + '</div>\ '); container.append(str); }); } // , function formatTweet(str) { str=' '+str; str = str.replace(/((ftp|https?):\/\/([-\w\.]+)+(:\d+)?(\/([\w/_\.]*(\?\S+)?)?)?)/gm,'<a href="$1" target="_blank">$1</a>'); str = str.replace(/([^\w])\@([\w\-]+)/gm,'$1@<a href="http://twitter.com/$2" target="_blank">$2</a>'); str = str.replace(/([^\w])\#([-,-,az,AZ,\-]+)/gm,'$1<a href="http://twitter.com/search?q=%23$2" target="_blank">#$2</a>'); return str; }
It remains only to register the styles for the widget and the HTML code is ready:
body { font-family: Tahoma,Arial,Verdana,sans-serif; width:1024px; margin: auto; height:768px; } #tweet-container { overflow-y:scroll; height:768px; width:660px; margin: auto; } .tweet { color:black; margin-top:5px; background-color:#F0F1F4; border-bottom:3px solid #5ea8de; padding:10px; margin-left:5px; width:600px; height:autopx; } .avatar { float:left; } .avatar:hover { opacity:0.5; } .user { float:left; padding-left:10px; } .user a { text-decoration:none; color:black; font-size:1.1em; } .user a:hover { color:#DB4FDB } .txt { clear:left; }
Now add the file Info.plist to this webpage. In my widget, it looks like this:
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <plist version="1.0"> <dict> <key>BackwardsCompatibleClassLookup</key> <true/> <key>CFBundleDevelopmentRegion</key> <string>Russian</string> <key>CFBundleDisplayName</key> <string>twitter</string> <key>BRNotifiesOnReady</key> <true/> <key>CFBundleIdentifier</key> <string>ru.mySite.widget.twtter</string> <key>CFBundleName</key> <string>twitter</string> <key>CFBundleShortVersionString</key> <string>1.0</string> <key>CFBundleVersion</key> <string>1.0</string> <key>KFNotifiesOnReady</key> <false/> <key>Height</key> <integer>768</integer> <key>MainHTML</key> <string>main.html</string> <key>Width</key> <integer>1024</integer> <key>IBNotifiesOnReady</key> <false/> </dict> </plist>
Thus, it remains only to rename the folder by adding the .wdgt extension to it and it can be safely used in the Ibook Authors.
Finally
In this how-to, I tried to make out the steps of creating the simplest widget and describe the most “dangerous” places, such as the connection of the widget with the server. Of course, if you remove the connection of the widget with a certain server, which assigns names and hash tags to it and writing them in the widget, you can reduce the number of dependencies of this widget.
I hope this article does not just take your time, but it will be something useful. :)