<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>iCab Blog &#187; JavaScript</title>
	<atom:link href="http://www.icab.de/blog/tag/javascript/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.icab.de/blog</link>
	<description>iCab related stuff; Mac, iPhone and Cocoa programming</description>
	<lastBuildDate>Sat, 21 Aug 2010 12:14:09 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.0.1</generator>
		<item>
		<title>Customize the contextual menu of UIWebView</title>
		<link>http://www.icab.de/blog/2010/07/11/customize-the-contextual-menu-of-uiwebview/</link>
		<comments>http://www.icab.de/blog/2010/07/11/customize-the-contextual-menu-of-uiwebview/#comments</comments>
		<pubDate>Sun, 11 Jul 2010 17:15:26 +0000</pubDate>
		<dc:creator>Alexander</dc:creator>
				<category><![CDATA[Programming]]></category>
		<category><![CDATA[iPhone & iPod Touch]]></category>
		<category><![CDATA[Cocoa]]></category>
		<category><![CDATA[Development]]></category>
		<category><![CDATA[iPhone]]></category>
		<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[WebKit]]></category>

		<guid isPermaLink="false">http://www.icab.de/blog/?p=159</guid>
		<description><![CDATA[When you tap and hold your finger on a link in a UIWebView object for about a second, a contextual menu will open and provides a few choices: copy the URL to the pasteboard, open the link and a button to close the menu again. Unfortunately there&#8217;s no API available to add additional menu items [...]]]></description>
			<content:encoded><![CDATA[<p>When you tap and hold your finger on a link in a UIWebView object for about a second, a contextual menu will open and provides a few choices: copy the URL to the pasteboard, open the link and a button to close the menu again. Unfortunately there&#8217;s no API available to add additional menu items to this contextual menu. But if you look at iCab Mobile, you&#8217;ll notice that there are a lot of additional menu items here. How is this done?</p>
<p>First of all, you really can&#8217;t add additional menu items to the default ones of the standard contextual menu. But you can switch off the contextual menu using a certain CSS property. So the solution would be to switch off the default menu and implement your own from scratch. And to implement your own contextual menu, you have to first catch the tab-and-hold gesture, get the coordinates of the finger on the screen, translate these coordinates into the coordinate system of the web page and finally look for the HTML elements at this location. Based on the HTML elements, you can then decide which menu items to include in the contextual menu, and then create and display the contextual menu.</p>
<p>The first step is to switch off the default contextual menu of UIWebView. This is easy because we only need to set the CSS property &#8220;-webkit-touch-callout&#8221; to &#8220;none&#8221; for the body element of the web page. We can do this using JavaScript in the UIWebView delegate method &#8220;webViewDidFinishLoad:&#8221;&#8230;</p>
<pre style="padding-left: 0.7em; border-left: 1px solid #030; color: #003300; font-size: 0.9em;">- (void)webViewDidFinishLoad:(UIWebView *)webView
{
   [webView stringByEvaluatingJavaScriptFromString:@"document.body.style.webkitTouchCallout='none';"];
}</pre>
<p>Now, the default Contextual menu won&#8217;t open anymore.</p>
<p>The next step is to catch the &#8220;tap-and-hold&#8221; gesture. If your App only needs to run on iOS 3.2, iOS 4.0 or newer, you can simply use the new UIGestureRecognizer API. But if you still want to address the 1st generation iPod Touch and iPhone devices (where you can install at most iOS 3.1) where these UIGestureRecognizer classes are not available, you need to do a little bit more. I&#8217;ll show here a solution which will work with iOS 3.0 as well, so it&#8217;s compatible to all iPhone, iPad and iPod Touch devices.</p>
<p>A big problem when you need to catch gestures within UIWebView is that UIWebView internally consists of many nested views, and only the inner ones do respond to events and gestures. Which means, if you subclass UIWebView and overwrite the methods &#8220;touchesBegan:withEvent:&#8221;, &#8220;touchesMoved:withEvent:&#8221; etc., you&#8217;ll notice that these methods won&#8217;t be called. The events are delivered directly to these private inner views which are actually doing all the work. And if you overwrite &#8220;hitTest:withEvent:&#8221;, which is used by the iOS to find the view to which the events will be delivered, you would be able to get the touch events, but then you would have to deliver the events to these inner views yourself so these can still do their work. And because these inner views are private and the relationships between these views are unknown, it can be extremely dangerous to mess with the events here. </p>
<p>A much easier way would be to subclass UIWindow and overwrite the method &#8220;sendEvent:&#8221; of the UIWindows class. Here you&#8217;ll get all events before they are delivered to the views. And we can deliver the tap-and-hold events using the notification manager to the rest of the app. Each object that is interested in this gesture can listen to this notification.</p>
<p>Recognizing the tap-and-hold gesture is not very complicated. What we need to do is to save the screen coordinates of the finger when the finger first touches the screen. At that time we will also start a timer which fires after about a second. As soon as another finger touches the screen, the finger moves or the touch event is canceled, we invalidate the timer because then it can not be a simple tap-and-hold gesture anymore. If the timer fires, we can be sure that a single finger has touched the screen for about a second without moving and then we&#8217;ve recognized the &#8220;tap-and-hold&#8221; gesture. We post the gesture as notification.</p>
<p>This is the implementation of the UIWindow subclass:</p>
<p><span style="color: #993300;">MyWindow.h:</span></p>
<pre style="padding-left: 0.7em; border-left: 1px solid #030; color: #003300; font-size: 0.9em;">
@interface MyWindow : UIWindow
{
   CGPoint    tapLocation;
   NSTimer    *contextualMenuTimer;
}
@end</pre>
<p><span style="color: #993300;">MyWindow.m:</span></p>
<pre style="padding-left: 0.7em; border-left: 1px solid #030; color: #003300; font-size: 0.9em;">#import "MyWindow.h"

@implementation MyWindow

- (void)tapAndHoldAction:(NSTimer*)timer
{
   contextualMenuTimer = nil;
   NSDictionary *coord = [NSDictionary dictionaryWithObjectsAndKeys:
             [NSNumber numberWithFloat:tapLocation.x],@"x",
             [NSNumber numberWithFloat:tapLocation.y],@"y",nil];
   [[NSNotificationCenter defaultCenter] postNotificationName:@"TapAndHoldNotification" object:coord];
}

- (void)sendEvent:(UIEvent *)event
{
   NSSet *touches = [event touchesForWindow:self];
   [touches retain];

   [super sendEvent:event];    // Call super to make sure the event is processed as usual

   if ([touches count] == 1) { // We're only interested in one-finger events
      UITouch *touch = [touches anyObject];

      switch ([touch phase]) {
         case UITouchPhaseBegan:  // A finger touched the screen
            tapLocation = [touch locationInView:self];
            [contextualMenuTimer invalidate];
            contextualMenuTimer = [NSTimer scheduledTimerWithTimeInterval:0.8
                        target:self selector:@selector(tapAndHoldAction:)
                        userInfo:nil repeats:NO];
            break;

         case UITouchPhaseEnded:
         case UITouchPhaseMoved:
         case UITouchPhaseCancelled:
            [contextualMenuTimer invalidate];
            contextualMenuTimer = nil;
            break;
      }
   } else {                    // Multiple fingers are touching the screen
      [contextualMenuTimer invalidate];
      contextualMenuTimer = nil;
   }
   [touches release];
}
@end</pre>
<p>Some remarks for the UITouchPhaseMoved phase: it can be sometimes useful to allow small movements on the screen. You can add some code to check for the distance the finger has moved and if it is within a certain range, you just don&#8217;t abort the timer. This helps users which have difficulties to hold the finger still for about a second.</p>
<p>Another important thing you have to do when the App window is created by a NIB file: you have to change the UIWindow class of the window within the NIB file in Interface Builder to the new subclass MyWindow. This way the window is created with our subclass, which is important.</p>
<p>The next step is to listen for the &#8220;TapAndHoldNotification&#8221; notification  within the UIWebView delegate and when this notification is received, we need to check which HTML element was touched.</p>
<p>When initializing the UIWebView delegate, we need to add the delegate as observer for the notification&#8230;</p>
<pre style="padding-left: 0.7em; border-left: 1px solid #030; color: #003300; font-size: 0.9em;">   [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(contextualMenuAction:) name:@"TapAndHoldNotification" object:nil];
}</pre>
<p>And here&#8217;s the method &#8220;contextualMenuAction:&#8221;&#8230;</p>
<pre style="padding-left: 0.7em; border-left: 1px solid #030; color: #003300; font-size: 0.9em;">- (void)contextualMenuAction:(NSNotification*)notification
{
   CGPoint pt;
   NSDictionary *coord = [notification object];
   pt.x = [[coord objectForKey:@"x"] floatValue];
   pt.y = [[coord objectForKey:@"y"] floatValue];

   // convert point from window to view coordinate system
   pt = [webView convertPoint:pt fromView:nil];

   // convert point from view to HTML coordinate system
   CGPoint offset  = [webView scrollOffset];
   CGSize viewSize = [webView frame].size;
   CGSize windowSize = [webView windowSize];

   CGFloat f = windowSize.width / viewSize.width;
   pt.x = pt.x * f + offset.x;
   pt.y = pt.y * f + offset.y;

   [self openContextualMenuAt:pt];
}</pre>
<p>The method &#8220;scrollOffset&#8221; and &#8220;windowSize&#8221; are implemented as category for the UIWebView class. &#8220;scrollOffset&#8221; is required to make sure the coordinates are also correct when the web page was scrolled. The &#8220;windowSize&#8221; returns the visible width and height of the HTML document from the point of view of the HTML document. So based on the windowsSize of the &#8220;HTML window&#8221; and the view size of the UIWebView, you can calculate the zoom factor, and the zoom factor is necessary to transform and scale the screen coordinates to the correct HTML coordinates.</p>
<p>Here&#8217;s the implementation of &#8220;scrollOffset&#8221; and &#8220;windowSize&#8221;&#8230;</p>
<p><span style="color: #993300;">WebViewAdditions.h:</span></p>
<pre style="padding-left: 0.7em; border-left: 1px solid #030; color: #003300; font-size: 0.9em;">@interface UIWebView(WebViewAdditions)
- (CGSize)windowSize;
- (CGPoint)scrollOffset;
@end</pre>
<p><span style="color: #993300;">WebViewAdditions.m:</span></p>
<pre style="padding-left: 0.7em; border-left: 1px solid #030; color: #003300; font-size: 0.9em;">#import "WebViewAdditions.h"

@implementation UIWebView(WebViewAdditions)

- (CGSize)windowSize
{
   CGSize size;
   size.width = [[self stringByEvaluatingJavaScriptFromString:@"window.innerWidth"] integerValue];
   size.height = [[self stringByEvaluatingJavaScriptFromString:@"window.innerHeight"] integerValue];
   return size;
}

- (CGPoint)scrollOffset
{
   CGPoint pt;
   pt.x = [[self stringByEvaluatingJavaScriptFromString:@"window.pageXOffset"] integerValue];
   pt.y = [[self stringByEvaluatingJavaScriptFromString:@"window.pageYOffset"] integerValue];
   return pt;
}
@end</pre>
<p>Finally, we need to implement the method &#8220;openContextualMenuAt:&#8221; for the UIWebView delegate, which first checks for the HTML elements that are at the touch locations and the creates the contextual menu. Checking for the HTML elements at the touch location must be done via JavaScript&#8230;</p>
<p><span style="color: #993300;">JSTools.js</span></p>
<pre style="padding-left: 0.7em; border-left: 1px solid #030; color: #003300; font-size: 0.9em;">function MyAppGetHTMLElementsAtPoint(x,y) {
   var tags = ",";
   var e = document.elementFromPoint(x,y);
   while (e) {
      if (e.tagName) {
         tags += e.tagName + ',';
      }
      e = e.parentNode;
   }
   return tags;
}</pre>
<p>This JavaScript function simply collects the tag names of all HTML elements at the touch coordinates and returns the tag names as string list. The JavaScript file must be added as &#8220;resource&#8221; to your XCode project. It can happen that Xcode treats JavaScript file as normal code and tries to compile and link it instead of adding it to the resources. So make sure the JavaScript file is in the &#8220;Copy Bundle Resources&#8221; section within the XCode project target and not in the &#8220;Compile Sources&#8221; or &#8220;Link Binaries&#8221; section.</p>
<pre style="padding-left: 0.7em; border-left: 1px solid #030; color: #003300; font-size: 0.9em;">- (void)openContextualMenuAt:(CGPoint)pt
{
   // Load the JavaScript code from the Resources and inject it into the web page
   NSString *path = [[NSBundle mainBundle] pathForResource:@"JSTools" ofType:@"js"];
   NSString *jsCode = [NSString stringWithContentsOfFile:path encoding:NSUTF8StringEncoding error:nil];
   [webView stringByEvaluatingJavaScriptFromString: jsCode];

   // get the Tags at the touch location
   NSString *tags = [webView stringByEvaluatingJavaScriptFromString:
            [NSString stringWithFormat:@"MyAppGetHTMLElementsAtPoint(%i,%i);",(NSInteger)pt.x,(NSInteger)pt.y]];

   // create the UIActionSheet and populate it with buttons related to the tags
   UIActionSheet *sheet = [[UIActionSheet alloc] initWithTitle:@"Contextual Menu"
                  delegate:self cancelButtonTitle:@"Cancel"
                  destructiveButtonTitle:nil otherButtonTitles:nil];

   // If a link was touched, add link-related buttons
   if ([tags rangeOfString:@",A,"].location != NSNotFound) {
      [sheet addButtonWithTitle:@"Open Link"];
      [sheet addButtonWithTitle:@"Open Link in Tab"];
      [sheet addButtonWithTitle:@"Download Link"];
   }
   // If an image was touched, add image-related buttons
   if ([tags rangeOfString:@",IMG,"].location != NSNotFound) {
      [sheet addButtonWithTitle:@"Save Picture"];
   }
   // Add buttons which should be always available
   [sheet addButtonWithTitle:@"Save Page as Bookmark"];
   [sheet addButtonWithTitle:@"Open Page in Safari"];

   [sheet showInView:webView];
   [sheet release];
}</pre>
<p>This method injects the JavaScript code which looks for the HTML elements at the touch location into the web page and calls this function. The return value will be a string with a comma-separated list of tag names. This string will start and end with a comma so we can simply check for occurrences of a substring &#8220;,tagName,&#8221; if we want to find out if an element with a certain tag name was touched. In our example, we simple add some buttons if an &#8220;A&#8221; tag was hit and some other buttons if and &#8220;IMG&#8221; tag was hit. But what you&#8217;re doing is up to you. Also the information that is returned from the JavaScript function (in this example &#8220;MyAppGetHTMLElementsAtPoint()&#8221;) is up to you. In iCab Mobile it returns the HREF and SRC attributes of A and IMG tags, so the URLS can be directly processed.</p>
<p>The example doesn&#8217;t include the UIActionSheet delegate method which is called when you tab on one of the buttons in the contextual menu. But I think you should already know how to handle this. Also a few other details might be missing, but I think you should be able now to implement your own custom contextual menu for UIWebView objects with the information from the blog post.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.icab.de/blog/2010/07/11/customize-the-contextual-menu-of-uiwebview/feed/</wfw:commentRss>
		<slash:comments>15</slash:comments>
		</item>
		<item>
		<title>Modules for iCab Mobile (Updated 2010/08/21)</title>
		<link>http://www.icab.de/blog/2010/02/17/modules-for-icab-mobile/</link>
		<comments>http://www.icab.de/blog/2010/02/17/modules-for-icab-mobile/#comments</comments>
		<pubDate>Wed, 17 Feb 2010 21:39:38 +0000</pubDate>
		<dc:creator>Alexander</dc:creator>
				<category><![CDATA[Programming]]></category>
		<category><![CDATA[iCab]]></category>
		<category><![CDATA[iPhone & iPod Touch]]></category>
		<category><![CDATA[Development]]></category>
		<category><![CDATA[iCab Mobile]]></category>
		<category><![CDATA[iPhone]]></category>
		<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[Module]]></category>
		<category><![CDATA[modules]]></category>

		<guid isPermaLink="false">http://www.icab.de/blog/?p=118</guid>
		<description><![CDATA[The version 2.1 of iCab Mobile introduces a new &#8220;modules&#8221; feature. Modules make it possible to add new features in iCab Mobile just by downloading them. They can be used for simple things like increasing the font size so a page is more easy to read on the small iPhone screen, but also more complex tasks can be [...]]]></description>
			<content:encoded><![CDATA[<p>The version 2.1 of iCab Mobile introduces a new &#8220;modules&#8221; feature. Modules make it possible to add new features in iCab Mobile just by downloading them. They can be used for simple things like increasing the font size so a page is more easy to read on the small iPhone screen, but also more complex tasks can be done, like downloading YouTube videos or to post a web page URL at Twitter (including the login and creating a tiny URL). iCab Mobile 2.1 comes with a few built-in modules, and there are also several modules available for download.</p>
<p>This blog post will explain, how you can write your own modules for iCab Mobile and how you (and maybe other users) can install them in iCab Mobile.</p>
<h2>Technical background</h2>
<p>Technically, modules are somehow similar to bookmarklets, but with more features and more flexibility. This means the modules are written in JavaScript code and they can do everything that can be done with JavaScript. Unlike bookmarklets, where the complete JavaScript code must be squeezed in one single line so that it can be used as a URL with &#8220;javascript&#8221; scheme, the modules can be nicely formatted, without any line limitation. Modules have a special header section where the module properties are defined. The properties include an icon that is displayed in the Modules panel of iCab Mobile, but also settings which do allow the user to configure the module in the iCab Mobile module settings panel.</p>
<h2>The module structure</h2>
<p>The module is a normal text file with JavaScript code. There&#8217;s a header section and the code section. Here&#8217;s an example, how this looks like:</p>
<pre style="padding-left: 0.7em; border-left: 1px solid #030; color: #003300; font-size: 0.9em;">//startconfig
//id=de.icab.crazy
//icon=iVBORw0KGgoAAAANSUhEUgAAACwAAAAkCAYAAADy19hsAAAWrGlDQ1BJQ0...
//title=Crazy Layout
//description=The module sets random colors for the page elements
//description.de=Das Modul setzt alle Farben der Seite auf Zufallswerte
//var=confirmation;type=confirmation;default=false;
//var=language;type=language;
//endconfig

var hex = "0123456789abcdef";

function iCabMobileGoCrazyForElement(element) {
  if (element.nodeType == 1) {
    if (element.style.display != "none" &amp;&amp;
           element.nodeName.toLowerCase() != 'select') {
      element.style.backgroundColor = "#" +
                   hex.charAt(Math.random()*16) +
                   hex.charAt(Math.random()*16) +
                   hex.charAt(Math.random()*16);
      element.style.color = "#" +
                  hex.charAt(Math.random()*16) +
                  hex.charAt(Math.random()*16) +
                  hex.charAt(Math.random()*16);
      for (var i=0; i&lt;element.childNodes.length; i++) {
        iCabMobileGoCrazyForElement(element.childNodes[i]);
      }
    }
  }
}

var doAction = (confirmation == false);

if (confirmation) {
  var text = "Go crazy?";
  doAction = confirm(text);
}

if (doAction) {
  iCabMobileGoCrazyForElement(window.document.body);
}</pre>
<p>The header section defines the properties of the module. The header section starts with the line</p>
<pre style="padding-left: 0.7em; border-left: 1px solid #030; color: #003300; font-size: 0.9em;">//startconfig</pre>
<p>and ends with the line</p>
<pre style="padding-left: 0.7em; border-left: 1px solid #030; color: #003300; font-size: 0.9em;">//endconfig</pre>
<p>Between these two lines all the properties are defined, each individual property definition occupies exactly one line, so currently you can&#8217;t split the definition of a property into multiple lines. Also no empty lines are allowed in the header section. Each property definition has the following format:</p>
<pre style="padding-left: 0.7em; border-left: 1px solid #030; color: #003300; font-size: 0.9em;">//property=value;additional paramaters</pre>
<p>The additional parameters are optional and not always needed or required.</p>
<p>There are some properties which are required. If they are missing, iCab Mobile will ignore the module. Other properties are optional and do not need to be present. Here are the properties which are currently supported:</p>
<dl>
<dt><strong>id</strong> (required) </dt>
<dd>This property identifies the module. When updating or reinstalling a module, the value of the &#8220;id&#8221; will be used to find the old module that has to be replaced by the new one. So when updating a module, you must not change the value of the &#8220;id&#8221;. Everything else can be modified, even the name of the module. The value of the &#8220;id&#8221; property should be unique among all existing modules. The best way to find a good &#8220;id&#8221; value is to use a reverse domain name appended with the module name. If your own web page has the domain &#8220;www.your-domain.com&#8221; you should set the &#8220;id&#8221; value to &#8220;com.your-domain.moduleName&#8221;. All of your own modules will have the same reverse domain prefix and the module name as suffix. If you don&#8217;t have your own domain, you can use your name and city as a prefix and maybe some random numbers, anything which makes it unlikely that someone else uses the same id value. The &#8220;id&#8221; is not visible to the user within iCab Mobile. Its only used to identify the module.<br />
Example:</p>
<pre style="padding-left: 0.7em; border-left: 1px solid #030; color: #003300; font-size: 0.9em;">//id=de.icab.crazy</pre>
<p>or if you don&#8217;t have your own domain, you may use something like this:</p>
<pre style="padding-left: 0.7em; border-left: 1px solid #030; color: #003300; font-size: 0.9em;">//id=de.darmstadt.clauss.alexander.crazy</pre>
</dd>
<dt><strong>title</strong> (required)</dt>
<dd>The title defines the name of the module and is displayed within the modules settings of iCab mobile. The title property is needed so that the user can enable or disable the modules in the in-app settings and also configure the modules settings.<br />
Example:</p>
<pre style="padding-left: 0.7em; border-left: 1px solid #030; color: #003300; font-size: 0.9em;">//title=Crazy Colors</pre>
<p>You can also define localized versions of the title. Just append the language code (for example &#8220;en&#8221; for English, &#8220;de&#8221; for German, &#8220;it&#8221; for Italian etc.) of any of the languages which are supported by the iPhone OS to the &#8220;title&#8221; keyword<br />
like this:</p>
<pre style="padding-left: 0.7em; border-left: 1px solid #030; color: #003300; font-size: 0.9em;">//title.de=Verrückte Farben</pre>
<p>This way you can easily localize the module in many languages. The key &#8220;title&#8221; without a language code appended will be used as default language which is used when none of the defined languages does match the current language of the iPhone or iPod Touch. In general the default language should be English. If no default language is defined, then the very first language that is define will be used as default language. But it is highly recommended, that you simply use English as the default language (without language code).</p>
<p>So if the module supports English and German, the title would be defined this way:</p>
<pre style="padding-left: 0.7em; border-left: 1px solid #030; color: #003300; font-size: 0.9em;">//title=Crazy Colors
//title.de=Verrückte Farben</pre>
</dd>
<dt><strong>description</strong> (optional)</dt>
<dd>The description is displayed in the settings panel of the module in iCab Mobile. The description should explain what the module is doing. Localizing is done in the same way as described above for the title. Append the language code to the &#8220;description&#8221; keyword:</p>
<pre style="padding-left: 0.7em; border-left: 1px solid #030; color: #003300; font-size: 0.9em;">//description=The module sets random colors for the page elements
//description.de=Das Modul setzt alle Farben der Seite auf Zufallswerte</pre>
</dd>
<dt><strong>icon</strong> (optional, but highly recommended)</dt>
<dd>The icon is displayed when the user opens the modules panel where the modules can be activated by tapping on their icon. The icon should be an image file in PNG or JPG format. The data of the image file must be encoded with &#8220;base64&#8243; and this &#8220;base64&#8243; encoded data can be then used as value for the &#8220;icon&#8221; property. Usually the base64 data is formatted in lines of at most 64 characters length, but for the modules, all line breaks must be removed, so the icons data can be completely included in one line. The size of the module icons (the visible part) should be approx. 41*32. You can use the following empty icon image as a template for your own icons:<br />
<a href="http://www.icab.de/img/empty.png"><img src="http://www.icab.de/img/empty.png" alt="" /></a></p>
<pre style="padding-left: 0.7em; border-left: 1px solid #030; color: #003300; font-size: 0.9em;">//icon=iVBORw0KGgoAAAANSUhEUgAAACwAAAAkCAYAAADy19hsAAAWrGlDQ1BJQ0...</pre>
</dd>
<dt><strong>var</strong></dt>
<dd>The &#8220;var&#8221; property defines JavaScript variables, which are initialized by iCab Mobile. These variables can be used to allow the user to configure the modules, but also to get certain system properties, like the system language.</p>
<p>Variables do have the following format:</p>
<pre style="padding-left: 0.7em; border-left: 1px solid #030; color: #003300; font-size: 0.9em;">//var=NameOfVar;type=typeOfVar;default=defaultValue;title=labelForSettings;</pre>
<p>iCab Mobile will create a standard JavaScript variable declaration and initialization for all of these variable properties and adds these to the JavaScript code of the module. So your JavaScript code of the module can check and use these variables just like any other variables.</p>
<p>The value of the &#8220;var&#8221; property is the name of the variable.</p>
<p>The &#8220;title&#8221; property is required, when the variable should be presented to the user in the module settings, so the user can change the value of the variable. The value of the &#8220;title&#8221; property is used as the label in the settings panel. You can add additional localized versions of the title, just by appending the language code (as described above).</p>
<p>The &#8220;default&#8221; property can be used to define a certain default value for the variable.</p>
<p>The value of the &#8220;type&#8221; property is the type of the variable, which can be one of the following:</p>
<dl>
<dt><strong>bool</strong> or <strong>boolean</strong></dt>
<dd>The variable can have the values <strong>true</strong> or <strong>false</strong>. In the module settings this variable will be represented by a &#8220;switch&#8221; control. If the user switches it on, the variable will have the value  <strong>true</strong> otherwise the variable will have the value <strong>false</strong>. The title attribute is required for this type. The title is shown as label for the switch in the module settings. </dd>
<dt><strong>int</strong> or <strong>integer</strong></dt>
<dd>The variable can have a numerical value. In the module settings, this variable is represented with a text field where you can enter digits. The title attribute is required for this type. The title is shown as label for the edit field in the module settings. </dd>
<dt><strong>string</strong> or <strong>text</strong></dt>
<dd>The variable can have a string value. In the module settings, it is represented as a text field. The title attribute is required for this type. The title is shown as label for the edit field in the module settings. </dd>
<dt><strong>pass</strong> or <strong>password</strong></dt>
<dd>The variable can have a string value. In the module settings, it is represented as a text field where the input is hidden. The title attribute is required for this type. The title is shown as label for the edit field in the module settings. </dd>
<dt><strong>lang</strong> or <strong>language</strong></dt>
<dd>The value of this variable is a string that contains the language code of the currently selected system language. This variable is not presented in the settings panel of the module. Therefore no title property is required here. </dd>
<dt><strong>confirm</strong> or <strong>confirmation</strong></dt>
<dd>The variable has a boolean value. It is represented in the module settings by a switch where the user can enable or disable a confirmation box. If enabled, iCab Mobile will ask each time the module is activated, if it should be really executed. This variable does not need a &#8220;title&#8221; property because it is automatically localized within the module settings panel. </dd>
<dt><strong>autorun</strong></dt>
<dd>The variable has a boolean value. It is represented in the module settings by a switch where the user can configure, if the module should be automatically run when the page has finished loading. This variable does not need a &#8220;title&#8221; property because it is automatically localized within the module settings panel. If there&#8217;s no variable of the type &#8220;autorun&#8221; set, the module can only be opened manually. You can set the  default value for the variable to switch on or off the &#8220;autorun&#8221; feature for the module. But the user will be always able to disable the &#8220;autorun&#8221; feature in the module settings. When the modul is executed, the variable that is defined with the type &#8220;autorun&#8221; will have the value &#8220;true&#8221; if the module was executed automatically (which means when the page has finished loading) and the value &#8220;false&#8221; if the module was opened manually by the user. So a module is able to do different thing when called automatically and when called manually (for example it can mark elements when called automatically to notify the user about something, and modify the elements when called manually, to do the real work). [This feature will be available in iCab Mobile 3.3, it's not yet available in version 3.2]</dd>
<dt><strong>select</strong></dt>
<dd>The variable of this type can be used to select one item from an array of items. This variable type uses the title property in the same way as the other types to define the label in the module settings. This variable type requires that two other properties are defined as well. The &#8220;values&#8221; property must be defined to create the array of values the user can choose from, and the &#8220;valuetitles&#8221; property must be defined to define the array of titles for these values. The &#8220;valuetitles&#8221; properties is localizable again, so append the language code as shown above for the &#8220;title&#8221; property. For the &#8220;values&#8221; and &#8220;valuetitles&#8221; property, all items must be separated by the &#8220;|&#8221; character.<br />
An example:</p>
<pre style="padding-left: 0.7em; border-left: 1px solid #030; color: #003300; font-size: 0.9em;">//var=s;type=select;valuetitles=One|Two|Three;values=1|2|3;title=Number;default=2</pre>
<p>In the settings the user would be able to choose between &#8220;One&#8221;, &#8220;Two&#8221; and &#8220;Three&#8221;. If the user selects &#8220;One&#8221; the variable <strong>s</strong> would have the value <strong>1</strong>, if the user selects &#8220;Two&#8221; the variable <strong>s</strong> would have the value <strong>2</strong> etc. The default value would be <strong>2</strong> and the user would see that &#8220;Two&#8221; is preselected. The whole setting would have the label &#8220;Number&#8221;.</p>
</dd>
</dl>
<p>When the user activates a module, iCab Mobile will process the header sections and creates JavaScript variable declarations for all the variable definitions from the header section. The values for these variables will be determined by the module settings. Technically, iCab will add these variable declarations before the JavaScript code section of the module, so the module can access these variables. These variables and the JavaScript code section will be embedded in a block, so they have their own scope and do not interfere with the JavaScript code and variables which are already present in the web page.</p>
<p>This means, when the module looks like this:</p>
<pre style="padding-left: 0.7em; border-left: 1px solid #030; color: #003300; font-size: 0.9em;">//startconfig
//id=de.icab.module
//icon=iVBORw0KGgoAAAANSUhEUgAAACwAAAAkCAYAAADy19hsAAAWrGlDQ1BJQ0...
//title=Some Module
//var=confirmation;type=confirmation;default=false;
//var=language;type=language;
//var=text;type=string;title=Text;
//endconfig

function DoSomething() {
  // here's the actual code which is doing all the work of the module
}

if (confirmation) {
  doAction = confirm("Really activate the module?");
} else {
  doAction = true;
}

if (doAction) {
  DoSomething();
}</pre>
<p>the resulting code that is actually executed in the context of the web page looks like this:</p>
<pre style="padding-left: 0.7em; border-left: 1px solid #030; color: #003300; font-size: 0.9em;">{
  var confirmation = true;
  var language = "en";
  var text = "User Input";

  function DoSomething() {
    // here's the actual code which is doing all the work
  }

  if (confirmation) {
    doAction = confirm("Really activate the module?");
  } else {
    doAction = true;
  }

  if (doAction) {
    DoSomething();
  }
}</pre>
</dd>
</dl>
<h2>Special JavaScript functions</h2>
<p>Modules can also call special JavaScript functions, which are defined by iCab Mobile to do certain tasks.</p>
<p>There are the following functions available:</p>
<dl>
<dt><span style="font-family: monospace; color: #003300; font-size: 0.9em;">startDownload(url,file)</span> </dt>
<dd>This function starts a download. The parameters are the URL and a suggestion<br />
for the filename under which the download should be saved. </dd>
<dt><span style="font-family: monospace; color: #003300; font-size: 0.9em;">postRequest(url,content,&#8221;callBackFunction&#8221;)</span> </dt>
<dd>This function gets the data from the URL using the HTTP POST command (posting &#8220;content&#8221; to the server) and then passes the HTTP status code and the data to a function called &#8220;callBackFunction&#8221; (see getRequest() below for a description of the callback function).</dd>
<dt><span style="font-family: monospace; color: #003300; font-size: 0.9em;">getRequest(url,&#8221;callBackFunction&#8221;)</span></dt>
<dd>This function gets the data from the URL using the HTTP GET command and then passes the HTTP status code and the data to a function called &#8220;callBackFunction&#8221; (the second parameter is a string with the name of the callback function that must be implemented by the Module to process the data):</p>
<pre style="padding-left: 0.7em; border-left: 1px solid #030; color: #003300; font-size: 0.9em;">function callBackFunction(status, data) {
  // "status" is an integer value with the HTTP status code (200, 404, etc)
  // "data" is a string with the URL data
}</pre>
</dd>
</dl>
<h2>Installing the modules</h2>
<p>Installing the modules is done by simply downloading them from a web site. So all you need to do is to provide a web page which contains a link to your module. The link URL must use the URL scheme &#8220;icabmodule&#8221; or &#8220;javascriptmodule&#8221; instead of the usual &#8220;http&#8221; scheme. This is how iCab Mobile detects that it should download and install a module.</p>
<p>For example, if your module can be accessed with the URL <strong>http://your.webspace.com/modules/TheModule.icabmodule</strong><br />
the HTML link you would have to include in your web page would look like this:</p>
<pre style="padding-left: 0.7em; border-left: 1px solid #030; color: #003300; font-size: 0.9em;">&lt;a href="icabmodule://your.webspace.com/modules/TheModule.icabmodule"&gt;Download TheModule&lt;/a&gt;</pre>
<p>or alternatively look like this:</p>
<pre style="padding-left: 0.7em; border-left: 1px solid #030; color: #003300; font-size: 0.9em;">&lt;a href="javascriptmodule://your.webspace.com/modules/TheModule.icabmodule"&gt;Download TheModule&lt;/a&gt;</pre>
<p>If you include these links on a web page which is accessible to other people as well, other iCab Mobile users would be also able to install your module. So you can easily share your modules with other users.</p>
<p>I&#8217;ve created two URL schemes for the modules. In case other iPhone developers are interested in this module feature (candidates would be developers of other iPhone browsers),  it would be great if all these modules would be compatible. In this case the general URL scheme &#8220;javascriptmodule&#8221; could be used by all the Apps supporting the modules feature. And the other URL scheme could be used for application specific-modules (so &#8220;icabmodule&#8221; would be only accepted by iCab Mobile).<br />
Just a reminder: My <a href="http://www.icab.de/blog/2009/09/12/applink-for-the-iphone/">AppLink Proposal</a> can be also used by other iPhone Apps.</p>
<p>You can also sent the module to <a href="mailto:alexander@icab.de">me</a> so I can include it into the modules download page, which is accessible through the &#8220;Download&#8221; module that is built-in in iCab Mobile. This way the module can be immediately found by all iCab Mobile users.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.icab.de/blog/2010/02/17/modules-for-icab-mobile/feed/</wfw:commentRss>
		<slash:comments>64</slash:comments>
		</item>
		<item>
		<title>Search and highlight text in UIWebView</title>
		<link>http://www.icab.de/blog/2010/01/12/search-and-highlight-text-in-uiwebview/</link>
		<comments>http://www.icab.de/blog/2010/01/12/search-and-highlight-text-in-uiwebview/#comments</comments>
		<pubDate>Mon, 11 Jan 2010 23:26:20 +0000</pubDate>
		<dc:creator>Alexander</dc:creator>
				<category><![CDATA[Programming]]></category>
		<category><![CDATA[iPhone & iPod Touch]]></category>
		<category><![CDATA[Cocoa]]></category>
		<category><![CDATA[Development]]></category>
		<category><![CDATA[iPhone]]></category>
		<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[WebKit]]></category>

		<guid isPermaLink="false">http://www.icab.de/blog/?p=110</guid>
		<description><![CDATA[Several iPhone Apps (like my &#8220;iCab Mobile&#8221; or &#8220;NewsTap&#8221; Apps) provide a search feature which allows to search for text in the content that is currently displayed within a UIWebView. The found occurrences of the searched text are highlighted with a yellow background, so the search result can be visually located very easy. This blog [...]]]></description>
			<content:encoded><![CDATA[<p>Several iPhone Apps (like my &#8220;iCab Mobile&#8221; or &#8220;NewsTap&#8221; Apps) provide a search feature which allows to search for text in the content that is currently displayed within a UIWebView. The found occurrences of the searched text are highlighted with a yellow background, so the search result can be visually located very easy.</p>
<p>This blog post describes how this can be implemented. I&#8217;m implementing this feature as a category for the UIWebView class, so you can use the new search feature for all UIWebView objects in your Apps very easily.</p>
<p>First of all, UIWebView doesn&#8217;t allow us to access its content directly, so we have to use JavaScript again. But if you&#8217;ve read my other blog posts, you already know this approach.</p>
<p>Our goal is to implement two methods for our new UIWebView category. One method should start the search and highlights the search results. As a result this method should return the number of occurrences of the text we&#8217;ve searched. The other method should remove all the highlighted search results again to restore the original layout of the web page.</p>
<p>What we need to do is to write the main code in JavaScript and a wrapper code in Objective C which is simply calling the JavaScript code.</p>
<p>We start with the JavaScript code that is doing the real work. As I&#8217;ve already described in the blog post <a href="http://www.icab.de/blog/2009/07/27/webkit-on-the-iphone-part-1/"> WebKit on the iPhone</a>, the JavaScript code will be saved as resource file in the XCode project. This way it can be loaded from within the Objective C code from the application bundle very easily, and we don&#8217;t mix the code of multiple programming languages (JavaScript and Objective C) in the same files.</p>
<p>The following code is the the JavaScript implementation; below I&#8217;ll explain what it is doing and how it works:</p>
<p><span style="color: #993300;">SearchWebView.js:</span></p>
<pre style="padding-left: 0.7em; border-left: 1px solid #030; color: #003300; font-size: 0.9em;">// We're using a global variable to store the number of occurrences
var MyApp_SearchResultCount = 0;

// helper function, recursively searches in elements and their child nodes
function MyApp_HighlightAllOccurencesOfStringForElement(element,keyword) {
  if (element) {
    if (element.nodeType == 3) {        // Text node
      while (true) {
        var value = element.nodeValue;  // Search for keyword in text node
        var idx = value.toLowerCase().indexOf(keyword);

        if (idx &lt; 0) break;             // not found, abort

        var span = document.createElement("span");
        var text = document.createTextNode(value.substr(idx,keyword.length));
        span.appendChild(text);
        span.setAttribute("class","MyAppHighlight");
        span.style.backgroundColor="yellow";
        span.style.color="black";
        text = document.createTextNode(value.substr(idx+keyword.length));
        element.deleteData(idx, value.length - idx);
        var next = element.nextSibling;
        element.parentNode.insertBefore(span, next);
        element.parentNode.insertBefore(text, next);
        element = text;
        MyApp_SearchResultCount++;	// update the counter
      }
    } else if (element.nodeType == 1) { // Element node
      if (element.style.display != "none" &amp;&amp; element.nodeName.toLowerCase() != 'select') {
        for (var i=element.childNodes.length-1; i&gt;=0; i--) {
          MyApp_HighlightAllOccurencesOfStringForElement(element.childNodes[i],keyword);
        }
      }
    }
  }
}

// the main entry point to start the search
function MyApp_HighlightAllOccurencesOfString(keyword) {
  MyApp_RemoveAllHighlights();
  MyApp_HighlightAllOccurencesOfStringForElement(document.body, keyword.toLowerCase());
}

// helper function, recursively removes the highlights in elements and their childs
function MyApp_RemoveAllHighlightsForElement(element) {
  if (element) {
    if (element.nodeType == 1) {
      if (element.getAttribute("class") == "MyAppHighlight") {
        var text = element.removeChild(element.firstChild);
        element.parentNode.insertBefore(text,element);
        element.parentNode.removeChild(element);
        return true;
      } else {
        var normalize = false;
        for (var i=element.childNodes.length-1; i&gt;=0; i--) {
          if (MyApp_RemoveAllHighlightsForElement(element.childNodes[i])) {
            normalize = true;
          }
        }
        if (normalize) {
          element.normalize();
        }
      }
    }
  }
  return false;
}

// the main entry point to remove the highlights
function MyApp_RemoveAllHighlights() {
  MyApp_SearchResultCount = 0;
  MyApp_RemoveAllHighlightsForElement(document.body);
}</pre>
<p>The basic principle of searching the text and removing the highlighted search results is the same: We&#8217;re working at DOM level (Document Object Model), which means the HTML document is represented as a tree structure where each HTML element, text, comment etc. is represented as a node. All the nodes are linked together with parent and child connections. The root element of each HTML document is the element that is created by the HTML tag. This element has usually two children: The HEAD element and the BODY element. Only the content of the BODY element is visible and displayed on screen, so we only need to process this part of the document tree.</p>
<p>What we need to do is to start with the body element and traverse all of its child nodes. From within the child nodes we need to go to their child nodes as well, and so on until we reach a leaf nodes, which has no child elements. Text nodes are always leaf nodes and text nodes are the nodes which might contain the text we&#8217;re looking for.</p>
<p>Traversing the whole HTML tree searching for all text nodes can be done by a recursive algorithm called Depth-First-Search (DFS). The DFS algorithm will traverse the tree structure starting from a root element (in our case the BODY element) to the first leaf node in a branch of the tree (for example going to the first child of the root first, from there again going to the first child, etc until a leaf node is reached). Then the algorithm goes back (backtracking) to the last node where not all child nodes were traversed yet and continues with the next unvisited child nodes etc. This way all nodes of the whole tree are traversed and we are able to find all the text nodes in which we are looking for the text we are earching.</p>
<p>The functions &#8220;MyApp_HighlightAllOccurencesOfStringForElement(element,keyword)&#8221; and &#8220;MyApp_RemoveAllHighlightsForElement(element)&#8221; are both implementations of this DFS algorithm. These functions are called from MyApp_HighlightAllOccurencesOfString(keyword)&#8221; and &#8220;MyApp_RemoveAllHighlights()&#8221; which are doing the necessary initialization and provide the DFS functions with the proper root element (the BODY element). The initialization for a new search is to make sure than no highlighted text from a previous search is present, so we simple call the function to remove all text highlights.</p>
<p>When searching for a text, we check if the currently inspected node is a text node or if it is an element node. If it is an element node it can have child nodes, and these must be inspected as well. If it is a text node, we have to find out if the text of this node contains the text we&#8217;re searching. If yes, we insert the text highlight, otherwise we are finished with this node. Also if the node is neither a text node nor an element node, there aren&#8217;t any child nodes which are interesting for us, so we are finished with this node as well.</p>
<p>When the text of a text node contains the searched text, we have to split the text into three parts. Part one will contain the text up to the searched text, part two contains the searched text itself and part three contains the rest of the text. A new element will be created (a SPAN element) and the second part (the searched text) will become a child node of this new element. Now we can assign StyleSheet rules to the newly created SPAN element to create the highlight effect (setting the background color to yellow, setting the text color to black, you can even increase the font size, if you want to). Now the new element is linked with part one and three so it becomes a part of the tree strucuture of the HTML tree. And because the searched text might be found multiple times in the original text node, we continue to search for the searched text in the third part of the original text node. If we find another occurrence of the searched text, we split this third part again in three parts, otherwise we are finished. When we create a SPAN element for the highlight effect, we also assign a special value (here &#8220;MyAppHighlight&#8221;) for the  class attribute. This is important to be able to find these elements later again when we want to remove the highlight effects using the function &#8220;MyApp_RemoveAllHighlights()&#8221;. For this task we traverse the tree as well, but now we&#8217;re looking for elements whose class attribute has this special value. To restore the original state of the HTML document, we have to remove the elements we&#8217;ve inserted before (the ones with the special value of the class attribute) and we need to concatenate the text node we&#8217;ve split. JavaScript can help us to concatenate the text nodes again, because it provides the &#8220;normalize()&#8221; function which can do this for us.</p>
<p>In JavaScript we can find out the type of a node with the &#8220;nodeType&#8221; property. A value of 1 means that the node is a normal element node (like the &#8220;body&#8221; node, a &#8220;span&#8221; node etc.). A value of 3 means that the node is a text node. In this case the property nodeValue contains the text of the node. Other values for nodeType represent comment nodes (in HTML these are written as &#8220;&lt;!&#8211; Comment &#8211;&gt;&#8221;), attribute nodes (for HTML attributes like for example the &#8220;HREF&#8221; attribute for the &#8220;A&#8221; tag), document nodes and some more. In our case only the values 1 (element node) and 3 (text node) are important.</p>
<p>In the above implementation, we count the number of found occurrences in a global variable.</p>
<p><strong>Note:</strong> You&#8217;ll notice that the JavaScript function names and variables and also the value for the class attribute I&#8217;m using in the above code are very lengthy and they do also have a prefix like &#8220;MyApp_&#8221;. The reason for this is to avoid any conflicts with existing function and variable names of the web page in which we inject our JavaScript code. If you&#8217;re generating the HTML code yourself that is displayed in the UIWebView object, you can choose shorter and simpler names. But if you have to deal with HTML and JavaScript code of any web pages (like in a web browser like iCab Mobile), you should use longer names and also add the name of your app as Prefix to all function and variable names to avoid any conflicts.</p>
<p>The Cocoa/Objective C part of the implementation is very simple. We only need to declare the interface and write a simple wrapper which loads and calls the JavaScript code that is actually doing all the hard work. The interface is also simple, we only need two methods: one to start the search and which highlights the found text and one which removes all the highlights again.</p>
<p><span style="color: #993300;">SearchWebView.h:</span></p>
<pre style="padding-left: 0.7em; border-left: 1px solid #030; color: #003300; font-size: 0.9em;">@interface UIWebView (SearchWebView)

- (NSInteger)highlightAllOccurencesOfString:(NSString*)str;
- (void)removeAllHighlights;

@end</pre>
<p>The typical use case would be to provide a search field where the user can enter some text. This text would be passed to the method &#8220;highlightAllOccurencesOfString:&#8221;. And when the user shakes the device, the App could call the method &#8220;removeAllHighlights&#8221; to remove all the highlighted search results again.</p>
<p>The implementation would look like this:</p>
<p><span style="color: #993300;">SearchWebView.m:</span></p>
<pre style="padding-left: 0.7em; border-left: 1px solid #030; color: #003300; font-size: 0.9em;">@implementation UIWebView (SearchWebView)

- (NSInteger)highlightAllOccurencesOfString:(NSString*)str
{
    NSString *path = [[NSBundle mainBundle] pathForResource:@"SearchWebView" ofType:@"js"];
    NSString *jsCode = [NSString stringWithContentsOfFile:path encoding:NSUTF8StringEncoding error:nil];
    [self stringByEvaluatingJavaScriptFromString:jsCode];

    NSString *startSearch = [NSString stringWithFormat:@"MyApp_HighlightAllOccurencesOfString('%@')",str];
    [self stringByEvaluatingJavaScriptFromString:startSearch];

    NSString *result = [self stringByEvaluatingJavaScriptFromString:@"MyApp_SearchResultCount"];
    return [result integerValue];
}

- (void)removeAllHighlights
{
    [self stringByEvaluatingJavaScriptFromString:@"MyApp_RemoveAllHighlights()"];
}

@end</pre>
<p>The first thing we&#8217;re doing in the method &#8220;highlightAllOccurencesOfString:&#8221; is to load the JavaScript file we&#8217;ve written above from the application bundle and inject it into the web page that is currently displayed in UIWebView. Because we&#8217;re implementing this as a category for UIWebView, we can use &#8220;self&#8221; to call the method &#8220;stringByEvaluatingJavaScriptFromString:&#8221; of the UIWebView instances.</p>
<p>After we&#8217;ve injected the JavaScript code we simply call the JavaScript function we&#8217;ve defined above to do the search.</p>
<p>And finally we access the variable we&#8217;ve defined in the JavaScript code, which represents the number of occurrences of the string that were found, and we return its integer value as the result of the method.</p>
<p>In the method &#8220;removeAllHighlights&#8221; we only need to call the  corresponding JavaScript function we&#8217;ve defined in the JavaScript code from above. Loading the external JavaScript file and injecting it into the Web page is not necessary here. If we&#8217;ve started a search before, the code is already injected and we don&#8217;t need to do this again. And if we haven&#8217;t started a search before, we just don&#8217;t need the JavaScript code because there are no highlights which have to be removed.</p>
<p>As you can see, the Objective C code for the UIWebView category is just a simple wrapper code for the JavaScript code. Now, when you have a UIWebView object in your App, you can simply search for text in its content by calling &#8220;highlightAllOccurencesOfString:&#8221;, like in this example where we&#8217;re searching for the text &#8220;keyword&#8221;:</p>
<pre style="padding-left: 0.7em; border-left: 1px solid #030; color: #003300; font-size: 0.9em;">  // webView is an instance of UIWebView
  [webView highlightAllOccurencesOfString:@"keyword"];</pre>
<p>Additional notes: In case your App has to deal with web pages which can have frames, you have to add some additional code that looks for frames. You have to traverse all the documents of all the frames to find all the text nodes in all frames. The code from above isn&#8217;t doing this to keep it as simple as possible.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.icab.de/blog/2010/01/12/search-and-highlight-text-in-uiwebview/feed/</wfw:commentRss>
		<slash:comments>77</slash:comments>
		</item>
	</channel>
</rss>
