Customized Blogger search box

I write this here so I have a place to refer so when some good day I decide to change current I don't have to write this again :)

The implementation is far from optimal but I couldn't find a better option than polling until the search box is loaded to fully customize it. Some properties are overwritten by defaults and CSS seems to be not enough. If somebody know how to do this in a cleaner/better way, I want to hear it ( read : write a comment please :) )

The CSS code that will make the search field look nicer and hides the search button:
/* customization of search box - nacho4d (css)*/
.gsc-search-box{
}
table.gsc-search-box td{
 border: 0px;
}
input.gsc-input { /* This is the real search field*/
background-image:url('http://web.me.com/nacho4d/toolbar_find24.png');
background-repeat:no-repeat;
background-position:0% 50%;

border: 1px solid gray;
-webkit-border-radius: 5px ;
-moz-border-radius: 5px ;
border-radius: 5px ;

-moz-box-shadow: inset 0 5px 15px rgba(0,0,0,.15);
-webkit-box-shadow: inset 0 5px 15px rgba(0,0,0,.15);
box-shadow: inset 0 5px 15px rgba(0,0,0,.15);

font-size: 1.2em;
color: gray;
padding-left:30px;
padding-right:7px;
height: 24px;
}
input.gsc-input:focus { 
-moz-box-shadow: inset 0 3px 25px rgba(0,0,0,.0);
-webkit-box-shadow: inset 0 3px 25px rgba(0,0,0,0);
box-shadow: inset 0 3px 25px rgba(0,0,0,0);
color:black;
}
input.gsc-search-button { /* this is the search button */
display:none;
}
/* end of customization of search box - nacho4d (css)*/
Remember to put this somewhere inside <style> tag :)

The js code that will poll looking for the search field (which is loaded dynamically) and set a couple of attributes to do the final touch.
<!-- Start of customization of search box - nacho4d (js) -->
  <script type='text/javascript'>
function fixSearchField() {
  var searchFields = document.getElementsByTagName('input');
  for (var i = 0; i < searchFields.length; i++) {
    var searchField = searchFields[i];
    if (searchField.getAttribute('class', 0) == ' gsc-input'){
      // found it!
      searchField.style.paddingLeft = '30px';
      searchField.style.width = '120px';
      searchField.setAttribute('placeholder', 'Search ...');
      return;
    }
  }
  setTimeout('fixSearchField()', 1000);
}
fixSearchField();
</script>

<!-- End of customization of search box - nacho4d (js) -->

Remember to put this somewhere after </style> :)

I guess this would be much easier if I were hosting my own blog ... just saying.

scrollViewDidEndScrolling ?

Last time I tried this I think it was in iOS2.x, 3.x ... so instead of looking for an old code I decided to implement this again. I want to know when the text view did end scrolling. I mean when the text end moving (regardless regular dragging or drag + deceleration).


First I created my regular UITextView, set the delegate and implemented the delegate methods:
- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView
{
    if (scrollView.isDecelerating) {
    NSLog(@" ... restarting");
    } else {
    NSLog(@"Scroll will begin");
    }
}

- (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(CGPoint *)targetContentOffset
{
    NSLog(@"will end dragging: velocity: %f", velocity.y);
}

- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate
{
    if (decelerate) {
    } else {
        NSLog(@"Scroll did end (Dragging)");
    }
}

- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView
{
    NSLog(@"Scroll did end (Decelerating)");
}

So if I ...

scroll without deceleration I get:

Scroll will begin
will end dragging: velocity: 0.000000
Scroll did end (Dragging)


scroll with deceleration I get:

Scroll will begin
will end dragging: velocity: 0.321089
Scroll did end (Decelerating)


start scrolling with deceleration and while its decelerating do another scrolling without deceleration I get:

Scroll will begin
will end dragging: velocity: 1.094134
... restarting
will end dragging: velocity: 0.000000
Scroll did end (Dragging)


start scrolling with desceleration and while its decelerating do another scrolling with deceleration I get:

Scroll will begin
will end dragging: velocity: 1.995404
... restarting
will end dragging: velocity: 0.442658
Scroll did end (Decelerating)


Everything goes as expected :)


Why do I need scrollViewWillEndDragging:withVelocity:targetContentOffset: ?. This is my little finding: If that method is **not** implement and then I ...

start scrolling with deceleration and while its decelerating do another scrolling without deceleration I get :

Scroll will begin
will end dragging: velocity: 1.094134
... restarting
will end dragging: velocity: 0.000000
Scroll did end (Dragging)
Scroll did end (Decelerating)

Huh?? The textview ended dragging and just after decelerating? Did I find a bug?
So, in order to get notified only once when the text did end scrolling I need to implement scrollViewWillEndDragging:withVelocity:targetContentOffset: ?? ??
I hope I am not missing something.


In the mean time I uploaded a sample project that show the issue here.

Installing Node.js for the third time

I think this is the third time I want to use node.js in a different computer and I **always** get stuck in the same place.
I know there are several "How do I install node.js" tutorials, blogs, etc.
Here is what it worked for me:

  • In theory you install node using brew: brew install node (or brew upgrade node to upgrade current node installation) and then npm : curl http://npmjs.org/install.sh | sh.
    curl https://npmjs.org/install.sh | sh

  • But in practice, At least me, I always get permission errors, link errors, it looks pretty bad. It is because I need to set adjust my permissions first : sudo chown -R $USER /usr/local.

  • Then, If you got: "Error: The linking step did not complete successfully." finish node installation with brew link node.

  • Now installing npm should work flawless too: curl http://npmjs.org/install.sh | sh

Hope it helps... :)

Other References

Adding Linkedin Badge to Blogger

I write this here so If I ever decide to change the template of this blog I have a reference to go back and set this again :)

Basically I simply replaced :
<a class='profile-link' expr:href='data:userUrl' rel='author'><data:viewProfileMsg/></a>

with Linkedin's badge. Something like this :)
 <b:if cond='data:aboutme != ""'><dd class='profile-textblock'><data:aboutme/></dd></b:if>
 </dl>
   <a href='http://jp.linkedin.com/pub/guillermo-ignacio-enriquez-gutierrez/31/b53/173'><img id='linkedinImg' border='0' src='http://www.linkedin.com/img/webpromo/btn_myprofile_160x33.png'/></a>
   <!-- <a class='profile-link' expr:href='data:userUrl' rel='author'><data:viewProfileMsg/></a> -->
 </b:if>

optionally, remove the border and shadow of img tag. (Put this somewhere before ]]></b:skin>)
#linkedinImg{
-webkit-box-shadow: none; 
-moz-box-shadow: none; 
box-shadow: none;
border: 0px;
}

In my opinion this looks much better than Google+ :)


Catching Keyboard Events in iOS


Some notes before starting ...

Things explained here are NOT possible using public APIs so this definitely violates AppStore's rules. However, if you want to take your chances and implement this for the AppStore or perhaps for Jailbroken iPhones ... keep on reading :) I think this time it is harder to get caught since this doesn't need to link against private frameworks or add private headers, etc.

The question is: How?

The short answer

The trick is in accessing GSEventKey struct memory directly and check certain bytes to know the keycode and flags of the key pressed. Below code is almost self explanatory and should be put in your UIApplication subclass.

#define GSEVENT_TYPE 2
#define GSEVENT_FLAGS 12
#define GSEVENTKEY_KEYCODE 15
#define GSEVENT_TYPE_KEYUP 11

NSString *const GSEventKeyUpNotification = @"GSEventKeyUpHackNotification";

- (void)sendEvent:(UIEvent *)event
{
    [super sendEvent:event];

    if ([event respondsToSelector:@selector(_gsEvent)]) {

        // Key events come in form of UIInternalEvents.
        // They contain a GSEvent object which contains 
        // a GSEventRecord among other things

        int *eventMem;
        eventMem = (int *)[event performSelector:@selector(_gsEvent)];
        if (eventMem) {

            // So far we got a GSEvent :)
            
            int eventType = eventMem[GSEVENT_TYPE];
            if (eventType == GSEVENT_TYPE_KEYUP) {
                
                // Now we got a GSEventKey!
                
                // Read flags from GSEvent
                int eventFlags = eventMem[GSEVENT_FLAGS];
                if (eventFlags) { 

                    // This example post notifications only when 
                    // pressed key has Shift, Ctrl, Cmd or Alt flags

                    // Read keycode from GSEventKey
                    int tmp = eventMem[GSEVENTKEY_KEYCODE];
                    UniChar *keycode = (UniChar *)&tmp;

                    // Post notification
                    NSDictionary *inf;
                    inf = [[NSDictionary alloc] initWithObjectsAndKeys:
                      [NSNumber numberWithShort:keycode[0]],
                      @"keycode",
                      [NSNumber numberWithInt:eventFlags], 
                      @"eventFlags",
                      nil];
                    [[NSNotificationCenter defaultCenter] 
                        postNotificationName:GSEventKeyUpNotification 
                                      object:nil
                                    userInfo:userInfo];
                }
            }
        }
    }
}


If you are asking yourself "Where all those #defines come from?", "Application subclass?, I don't have such a thing in code" then please read the long answer, I hope you find if helpful :)

The long answer:

UIEvents are simple wrappers of GSEventRefs, which contain a GSEventRecord struct in it. Usually we only treat UIEvents representing touch events because the UIApplication will not dispatch other events to our views or objects we create. These are UIInternalEvents and can represent accelerometer events, volume events, keyboard events, etc.

  1. In order to intercept all events our application receives we need to override sendEvent: method in our UIApplication subclass.
  2. We need to access the GSEvent and check it is a key event and then check its flags (Shift, Cmd, Ctrl, Alt). Its easy as that, the problem is we don't know how to get the type and flags of GSEvent, it is a private API :(

- GSEvent objects -
So I did the homework and according to Kenny TM in here and here, GSEvents looks like:
typedef struct __GSEvent {
    CFRuntimeBase _base;
    GSEventRecord record;
} GSEvent;
typedef struct __GSEvent* GSEventRef;
typedef struct GSEventRecord {
    GSEventType type; // 0x8 //2
    GSEventSubType subtype;    // 0xC //3
    CGPoint location;     // 0x10 //4
    CGPoint windowLocation;    // 0x18 //6
    int windowContextId;    // 0x20 //8
    uint64_t timestamp;    // 0x24, from mach_absolute_time //9
    GSWindowRef window;    // 0x2C //
    GSEventFlags flags;    // 0x30 //12
    unsigned senderPID;    // 0x34 //13
    CFIndex infoSize; // 0x38 //14
} GSEventRecord;
typedef struct GSEventKey {
 GSEvent _super;
 UniChar keycode, characterIgnoringModifier, character; // 0x38, 0x3A, 0x3C
 short characterSet;  // 0x3E
 Boolean isKeyRepeating; // 0x40
} GSEventKey;
Headers seems to be a bit old (iOS3~4) but things haven't changed so much. What is for sure is that keycode is right next to infoSize. (Isn't this the fun of private APIs?).
Everything we have to do now is to count bytes from the start of the memory of GSEvent
int *eventMem;
eventMem = (int *)[event performSelector:@selector(_gsEvent)];
GSEventType is at 2
GSEventFlags at 12 and
Unichar keycode at index 15.
I do some checks in the way but that is basically all. :)

Yes, Apple might change GSEvent at anytime so if you are a challenger and would like to submit this to the AppStore, at least do do this conditionally:
// I am lazy to do the proper check here in the post :)
[super sendEvent:event];
if ([[[UIDevice currentDevice] systemVersion] intValue] == 5) {
    ...
}

If you use it or tried to, it would be awesome you drop a line in the comments. (^-^)/
I still have not completed a sample for this little hack but in the mean you can check this this gist out where you can find some keycodes and masks.

References

This work is licensed under BSD Zero Clause License | nacho4d ®