Congratulations!

[Valid RSS] This is a valid RSS feed.

Recommendations

This feed is valid, but interoperability with the widest range of feed readers could be improved by implementing the following recommendations.

Source: http://www.m0interactive.com/rss/articles.xml

  1. <?xml version="1.0" encoding="iso-8859-1" ?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  2. <channel>
  3. <atom:link href="http://www.m0interactive.com/rss/articles.xml" rel="self" type="application/rss+xml" />
  4. <title>Mohamed Mansour | articles</title>
  5. <copyright>Copyright (c) 2006 Mohamed Mansour! Inc. All rights reserved.</copyright>
  6. <link>http://www.m0interactive.com/</link>
  7. <description>Mohamed Mansour RSS Feeds</description>
  8. <language>en-us</language>
  9. <item>
  10. <title>Software - Introducing Proxy Anywhere Chrome Extension</title>
  11. <link>http://www.m0interactive.com/archives/2011/06/02/introducing_proxy_anywhere_chrome_extension.html</link>
  12. <description><![CDATA[<p>Do you want to connect to a proxy server while browsing? Proxy Anywhere is a simple HTML5 application that allows you enter a proxy server and will use that when visiting websites. Today Chrome has updated their Dev Channel to version 13. In this version, they have graduated the API from experimental to stable. That allowed developers to create native HTML5 extensions like this.</p>
  13.  
  14. <p>So if your in Canada and want to watch Hulu from USA, this is your chance to do it :) Head to the gallery to download <a href="https://chrome.google.com/webstore/detail/eejcbegfnjfjnmdikkplhbhnemddchbn">Proxy Anywhere Chrome Extension!</a> As always, the source code is open source at GitHub, fork and pull in your patches :) <a href="https://github.com/mohamedmansour/proxy-anywhere-extension">https://github.com/mohamedmansour/proxy-anywhere-extension</a> </p>
  15.  
  16. <p>Some screenshots!</p>
  17. <img src="https://github.com/mohamedmansour/proxy-anywhere-extension/raw/master/screenshot/proxy_screenshot.png" />
  18.  
  19. <img src="https://github.com/mohamedmansour/proxy-anywhere-extension/raw/master/screenshot/proxy_screenshot_bypass.png" />
  20. ]]></description>
  21. <pubDate>Thu, 02 Jun 2011 00:15:59 -0400</pubDate>
  22. <guid>http://www.m0interactive.com/archives/2011/06/02/introducing_proxy_anywhere_chrome_extension.html</guid>
  23. </item>
  24. <item>
  25. <title>Software - Never use git-svn to convert your SVN repo to Git</title>
  26. <link>http://www.m0interactive.com/archives/2011/04/05/never_use_git_svn_to_convert_your_svn_repo_to_git.html</link>
  27. <description><![CDATA[<p>If you want to convert your SVN repository to Git, the first thing that you would try is the native built-in git-svn that comes with your distribution. Think twice when you use that approach on a
  28.  
  29. 1GB SVN repo with many branches and tags, it will take forever. It took around 8 hours for my repo. </p>
  30.  
  31. <h3>Problem with git-svn</h3>
  32. <ul>
  33. <li>Doesn't convert it as a bare repository, sure we can just look into .git folder.</li>
  34. <li>It doens't respect the standard layout for tags and branches. You have to pass those values in.</li>
  35. <li>Doesn't allow you to use an irregular layout. For example, it doesn't understand if you have two trunks, or folders in root. We have to just stick with trunk, branches, and tags. Nothing
  36.  
  37. more.</li>
  38. <li>It doesn't even understand what a tag is, it treats it as a branch. You have to create a script after its complete to manually tag those branches that should have originally been branches.</li>
  39. <li>SLOW, flithy slow!</li>
  40. </ul>
  41.  
  42. <h3>History</h3>
  43. <p>I didn't give up finding a better way. Even though it worked, I kept on searching online for alternatives.
  44. Every single script and solution that users have created use git-svn. Where are you Google, you didn't help at all! I was more curious how the bigger organizations have done the conversion. First
  45.  
  46. thing that came to my mind was KDE. I remembered KDE becuase I used gitolite before and they were on the list of adopters.</p>
  47.  
  48. <p>KDE is very old open source "true" project. When I mean "true", I mean, everything they do is in the public. I respect that in the highest level. Every feature they create, they communicate with
  49.  
  50. the community. Every decision they make, the community is involved. Absolutely no corporate black box that is hiding how they do their project from the community, unlike many projects that I have
  51.  
  52. seen. By saying these words, makes me super excited that I wanted to contribute to KDE. KDE converted their 64GB SVN repository in around 1 hour! I screamed in my mind when I heard that, 1 HOUR!!!! It
  53.  
  54. took me around 10 hours to convert 1GB. Since they are open sourced, they documented every step along the way!
  55.  
  56. <h3>Native svn2git</h3>
  57. <p>They called it, svn2git (the real one, too many fake clones out there that I tried and all use git-svn), and they have a nice wiki to explain how it is done <a
  58.  
  59. href="http://techbase.kde.org/Projects/MoveToGit/UsingSvn2Git">http://techbase.kde.org/Projects/MoveToGit/UsingSvn2Git</a></p>
  60.  
  61. <p>It is a C++ Qt application that communicates directly to the SVN repository. It is super fast, when I mean super fast, I converted my 1GB SVN repository in a matter of minutes, under 5 minutes!
  62.  
  63. From 10 hours to 5 minutes! This hidden tool is so powerful that it should really be known.</p>
  64.  
  65. <h3>How to use it</h3>
  66. <p>In Red Hat Enterprise, it is a pain I had to modify some code to backport it to Qt 4.0. RHEL don't support Qt 4.7, they just support Qt 4.0 which is quite sad. But luckily, the code for svn2git was
  67.  
  68. open source and beautiful! Easy to understand and easy to know where everythin is. It was my first time looking at Qt code, and to tell you the truth, it is beautiful!</p>
  69.  
  70. <p>Running it is pretty simple, all you have to do is: </p>
  71. <pre>
  72. qmake && make
  73. ./svn-all-fast-export --rules samples/standardlayout.rules ~/svn/
  74. </pre>
  75.  
  76. <p>Within a few minutes, the whole SVN including their tags, branches, and trunk converted! git-svn didn't do that!</p>
  77.  
  78. <p>Yes, I am super happy, I can now create complext rules to convert my SVN repo to a multiple Git repos to make the organization better.</p>
  79. ]]></description>
  80. <pubDate>Tue, 05 Apr 2011 18:13:10 -0400</pubDate>
  81. <guid>http://www.m0interactive.com/archives/2011/04/05/never_use_git_svn_to_convert_your_svn_repo_to_git.html</guid>
  82. </item>
  83. <item>
  84. <title>Software - Chromium msysgit vs cygwin Git Performance</title>
  85. <link>http://www.m0interactive.com/archives/2011/03/12/chromium_msysgit_vs_cygwin_git_performance.html</link>
  86. <description><![CDATA[<p>I got really frustrated with the inconsistencies and problems arising with
  87. Cygwins' Git support. The terminal goes haywire, awfully slow, and stuff just
  88. didn't work as I wanted to unless I do some modifications. Yesterday, I
  89. officially completed my set of patches to make msysgit function perfectly
  90. (I hope) with Chromium! My frustration no longer exists!</p>
  91.  
  92. <h3>Some history</h3>
  93. <p>Since the very beginning, Chromium was using SVN as its main repository, then
  94. around a year later some Chromium devs (Evan Martin and others) started to
  95. support Git and used git-svn to commit back to Chromium repo. Msysgit wasn't
  96. performing well, it was causing too many problems with git-svn, since it wasn't
  97. fully supported. The tooling in Chromium is all done in Python to make it
  98. cross platform with Linux/Mac/Windows and its pretty great! Some parts of the
  99. code are just checking for cygwin explicitly causing msysgit not to work.
  100. After my frustration with cygwin, with Marc-Antoine's help, we fixed msysgit
  101. issues and eveything works well. Really well!</p>
  102. <h3>Analysis</h3>
  103. <p>To tell you the truth, on Windows, it feels snappier and less hassle to work
  104.   on Chromiums source code with msysgit than cygwin. It feels faster and
  105.   natural. I was curious to see how well it improvement, and performed. So
  106.   here are the averaged timings of 5 trials. Some pre info: </p>
  107. <ul>
  108.  <li>Windows 7 x64</li>
  109.  <li>4GB RAM</li>
  110.  <li>Secondary HD</li>
  111.  <li>Latest Cygwin updates installed.</li>
  112.  <li>Cygwin Git 1.7.4</li>
  113.  <li>Msysgit Git 1.7.3.1</li>
  114. </ul>
  115.  
  116. <img src="http://chart.apis.google.com/chart?chxl=1:|status|checkout|git-rm|commit|diff-master|2:|Lower+the+better+(seconds)&chxp=2,50&chxr=0,0,3|1,5,0&chxt=y,x,t&chbh=a,5&chs=460x240&cht=bvg&chco=4D89F9,FF0000&chds=0,2.5,0,2.5&chd=t:2.265,1.124,0.775,1.778,1.373|1.364,1.078,0.508,0.937,1.312&chdl=Cygwin|Msysgit&chg=-1,0,0,4&chma=5&chtt=Msysgit+vs+Cygwin+Real+Time" width="400" height="200" alt="Msysgit vs Cygwin Real Time"/>
  117.  
  118. <img src="http://chart.apis.google.com/chart?chxl=0:|status|checkout|git-rm|commit|diff-master|1:|Lower+the+better+(seconds)&chxp=1,50&chxr=0,0,3|2,0,2.5&chxt=x,t,y&chbh=a,5,10&chs=480x200&cht=bvg&chco=4D89F9,FF0000&chds=0,2.5,0,2.5&chd=t:2.167,1.012,0.701,0.857,1.293|0.015,0.031,0.032,0.03,0.031&chdl=Cygwin|Msysgit&chg=-1,0,0,4&chma=5|5&chtt=Msysgit+vs+Cygwin+CPU+Time" width="400" height="166"  alt="Msysgit vs Cygwin Central Processing Unit Time"/>
  119.  
  120. <h4>Table timings</h4>  
  121. <table style="width: 100%">
  122.  <thead>
  123.    <tr>
  124.      <th></th>
  125.      <th>status</th>
  126.      <th>new-checkout</th>
  127.      <th>git-rm</th>
  128.      <th>commit</th>
  129.      <th>checkout-master</th>
  130.      <th>diff-master</th>
  131.    </tr>
  132.  </thead>
  133.  <tbody>
  134.    <tr style="background-color: #eee">
  135.      <td>cygwin real</td>
  136.      <td>2.265</td>
  137.      <td>1.124</td>
  138.      <td>0.775</td>
  139.      <td>1.778</td>
  140.      <td>1.963</td>
  141.      <td>1.373</td>
  142.    </tr>
  143.    <tr style="background-color: #eee">
  144.      <td>msysgit real</td>
  145.      <td>1.364</td>
  146.      <td>1.078</td>
  147.      <td>0.508</td>
  148.      <td>0.937</td>
  149.      <td>1.963</td>
  150.      <td>1.312</td>
  151.    </tr>
  152.    <tr>
  153.      <td>cygwin user+sys</td>
  154.      <td>2.167</td>
  155.      <td>1.012</td>
  156.      <td>0.701</td>
  157.      <td>0.857</td>
  158.      <td>1.512</td>
  159.      <td>1.293</td>
  160.    </tr>
  161.    <tr>
  162.      <td>msysgit user+sys</td>
  163.      <td>0.015</td>
  164.      <td>0.031</td>
  165.      <td>0.032</td>
  166.      <td>0.030</td>
  167.      <td>0.031</td>
  168.      <td>0.031</td>
  169.    </tr>
  170.  </tbody>
  171. </table>
  172.  
  173. <p>As shown above, the timings improved quite a bit. Around 50% to 100% on the
  174.   operation. It does feel snappier. I no longer have to wait 2-3 seconds waiting
  175.   for the "git status" to show up on a warm start. Even on a cold start status,
  176.   the timings improve quite a bit with msysgit.</p>
  177.  
  178. <p>So what does user+sys represent? Basically, it tells us how much actualy CPU
  179.   time the process used. It is quite significant ... Cygwin uses way more CPU
  180.   time than msysgit. Okay, not that much just around one second. But still, you
  181.   get the point.</p>
  182.  
  183. <h3>Conclusion</h3>
  184. <p>Not only you get speed improvements with msysgit, you get stability
  185.   improvements too. For example, cursor annoyances, copy paste screw-ups, terminal
  186.   freezes, random locking annoyances, etc are all not visible anymore. I have
  187.   been using it for around one months now, and no issues so far :) As a
  188.   bonus, the msysgit shell starts up super fast!</p>]]></description>
  189. <pubDate>Sat, 12 Mar 2011 18:27:21 -0500</pubDate>
  190. <guid>http://www.m0interactive.com/archives/2011/03/12/chromium_msysgit_vs_cygwin_git_performance.html</guid>
  191. </item>
  192. <item>
  193. <title>Software - Facebook Friend Export Chrome Extension Internals</title>
  194. <link>http://www.m0interactive.com/archives/2011/01/16/facebook_friend_export_chrome_extension_internals.html</link>
  195. <description><![CDATA[<p>There has been a lot of buzz regarding why Facebook doesn't allow you to export your friends out of Faceboook. Well, the first question many people have stated "Why can't you use Facebook's API to do this". The answer is quite simple but not obvious, Facebook does not allow their API (Application Programming Interface) to export your friends Email addresses that they made public for you to see. Hopefully in this post, I will explain the internals how this Google Chrome Extension was constructed.</p>
  196.  
  197. <h3><a href="#" name="top"></a>Table of Contents</h3>
  198. <ul>
  199.  <li><a href="#introduction">Introduction</a></li>
  200.  <li><a href="#messaging">Messaging</a> to communicate from Facebook to the Worker tab back to the extension.</li>
  201.  <li><a href="#content-scripts">Content Scripts</a> to extract your friends data quickly.</li>
  202.  <li><a href="#web-sql-database">HTML5 Offline Database Storage</a> to cache your friends.</li>
  203.  <li><a href="#sending-data-to-gmail">OAuth</a> to export to GMail</li>
  204.  <li><a href="#conclusion">Conclusion</a></li>
  205. </ul>
  206.  
  207. <h3><a href="#top" name="introduction"></a>Introduction</h3>
  208. <p>You can download the extension from <a href="https://chrome.google.com/webstore/detail/ficlccidpkaiepnnboobcmafnnfoomga">Chrome Web Store</a>
  209.   and the source code is hosted on <a href="https://github.com/mohamedmansour/fb-exporter">GitHub</a>
  210.  (feel free to fork, send patches, and help me improve it)</p>
  211.  
  212. <p>Within the media, it has been concluded that Facebook owns your friends
  213. most valuable data, their email address. In my opinion, the reason why Facebook
  214. became so popular is because of "us", we allowed them to export our friends and
  215. connect to them by extracting our friends from Hotmail, MSN, Gmail, Twitter, etc.
  216. But the reverse was not possible, there was no way for us to export them back.
  217. Absolutely no way! I invited many, many, people to Facebook through this process
  218. because I thought it was a neat concept and I believe in their vision. Facebook
  219. has grown so fast, that most of my in real life friends are on Facebook and not
  220. in my contact app (Outlook, Google Contacts). There is no way for me to connect
  221. back to my friends unless I do that within Facebook. It feels like Facebook
  222. is controlling the data that I have provided for them for free. That quite
  223. bothered me, hence the creation of this extension.</p>
  224.  
  225. <p>Without the original work from an anonymous person who hosted fb-exporter on
  226. Google Code (which is now blocked), I have used the source code, changed it
  227. dramatically, and made it easier, cleaner, faster, and safer to use. So whats
  228. new?</p>
  229.  
  230. <ul>
  231.  <li>A different approach to fetch your friends by directly communicating
  232.      to Facebook's JavaScript Objects, and retrieving your initial friends
  233.      information quickly such as Name, Image, and URL.</li>
  234.  <li>Cached results, since it takes so much time to export your friends, some
  235.      people might accidently close their browser, or want to continue the
  236.      process some other time. That is now possible via HTML5 Web Storage.</li>
  237.  <li>Not only emails are exported, it exports your contacts phone numbers,
  238.      Google Talk, Yahoo, ICQ, Skype, QQ, and MSN (Windows Live Messenger)
  239.      aliases. jQuery is beautiful when it comes to simplicity.</li>
  240.  <li>It uses real time error handling, if something goes wrong, it will
  241.      automatically figure out what that issue is and properly handle it.</li>
  242.  <li>New User Interface, you can see a grid of all your friends with their
  243.      profile photos. When you start the lengthy process, it tells you what is
  244.      CACHED, READY, STARTED, PROCESSED, SUCCESS and FAILED! These status
  245.      messages help the user know what is happening in real time. Thanks to
  246.      Chrome Extensions Message Passing.</li>
  247.  <li>The code is mostly refactored to make it cleaner and easier to use. As
  248.      you noticed, the UI differs too. It is commented fully and restructured
  249.      to make it easier on the external contributions.</li>
  250. </ul>
  251.      
  252. <h3><a href="#top" name="messaging"></a>Communications between Facebook and the Extension process</h3>
  253. <p>In Google Chrome extension, the only way to communicate to the DOM,
  254.   is through <a href="http://code.google.com/chrome/extensions/content_scripts.html">
  255.   Content Scripts</a>. But there is only one problem, they have a couple of
  256.   limitations that we need. Content Scripts cannot:</p>
  257. <ul>
  258.  <li>Use variables or functions defined by their extension's pages</li>
  259.  <li>Use variables or functions defined by web pages or by other content scripts</li>
  260.  <li>Make cross-site XMLHttpRequests</li>
  261. </ul>
  262. <h4>Communication from the Facebook World to the Chrome Extension World</h4>
  263. <p>We need to access our main extension page because we want to transfer our
  264.   data to Google Gmail Contacts through cross-site XMLHttpRequests. No fear,
  265.   we can use Chrome's <a href="http://code.google.com/chrome/extensions/messaging.html">
  266.   Message Passing</a> to send messages back and fourth to our extensions
  267.   <a href="http://code.google.com/chrome/extensions/background_pages.html">
  268.   background page</a>, which is just an HTML page that runs in the extension
  269.   process. It exists for the lifetime of your extension and only one instance
  270.   of it at a time is active. Once we reach the extension process, we
  271.   can send data back and fourth to Gmail as we please (of course, with the proper
  272.   permissions set in the <a href="http://code.google.com/chrome/extensions/manifest.html#permissions">
  273.   manifest</a>. We want our users to know what the extension has access to.)</p>
  274. <p>In fb-exporter, we actually use Chrome's Message Passing to communicate between
  275.   both worlds, since they are isolated. It is really simple to setup. Within the Content Script we add
  276.   add a extension listener, <a href="http://code.google.com/chrome/extensions/extension.html#event-onRequest">
  277.   chrome.extension.onRequest.addListener</a>, which in this case gets fired when
  278.   a request is sent from the extension process (reduced snippet below):</p>
  279. <pre class="code">
  280. chrome.extension.onRequest.addListener(function(request, sender, sendResponse) {
  281.  if (request.getFriendsMap) {
  282.    sendResponse({data: friendsMap});
  283.  }
  284. });
  285. </pre>
  286.  
  287. <h4>Communication from the Extension process back to Facebook</h4>
  288. <p>In the extension world's ( <a href="http://code.google.com/chrome/extensions/background_pages.html">background page</a>
  289. ), we maintain the Facebook's tab ID when the user first clicks on the
  290. "Export Friends" button on Facebook's navigation. When the "Export Friends" button
  291. is clicked, it sends a request to our extension process to tell it to open the
  292. main controller (that controls the steps for exporting your friends)
  293. window. We use <a href="http://code.google.com/chrome/extensions/extension.html#method-sendRequest">chrome.extension.sendRequest</a>
  294. to send the request to the extension process from the content script:</p>
  295. <pre class="code">
  296. chrome.extension.sendRequest({switchToWorkerTab: 1});
  297. </pre>
  298.  
  299. <p>So how does the background page listen for content script requests that comes
  300. from Facebook? The exact same way we discussed before with the content script:</p>
  301. <pre class="code">
  302. chrome.extension.onRequest.addListener(function(request, sender, sendResponse) {
  303.  if (request.switchToWorkerTab) {
  304.    facebook_id = sender.tab.id;
  305.    chrome.tabs.create({url: 'controller.html', selected: true}, function(tab) {
  306.      worker_id = tab.id;
  307.    });
  308.  }
  309. });
  310. </pre>
  311.  
  312. <p>What the above snippet states, when a request is coming in from the Facebook
  313. page via Content Script, we keep track of the ID's  of those pages. We need
  314. those ID's so that we can send information back and fourth between different
  315. pages directly. It becomes easier to handle.</p>
  316.  
  317. <p>Then we can use <a href="http://code.google.com/chrome/extensions/tabs.html#method-sendRequest">
  318. chrome.tabs.sendRequest</a>, to send a request back to Facebook Content Script
  319. and even back to the extension process:</p>
  320.  
  321. <pre class="code">
  322. chrome.tabs.sendRequest(facebook_id, { facebookError: 1 });
  323. chrome.tabs.sendRequest(worker_id, { facebookError: 1 });
  324. </pre>
  325.  
  326. <h3><a href="#top" name="content-scripts"></a>Using content scripts to read the internal JavaScript directly from Facebook</h3>
  327. <p>The traditional way of grabbing information is by screen scraping, but
  328.   we are not doing that. If you used <a href="http://code.google.com/chrome/devtools/">
  329.   Chrome Web Inspector</a>, you will notice an internal JavaScript variable
  330.   hosted in Facebook is running the show. But there are some problems and
  331.   limitations, as we discussed before. We cannot access that variable directly
  332.   from our Extension and even our Content Script!</p>
  333. <p>The only way the our extension can communicate to Facebook is through
  334.   Content Scripts, and the only way Content Scripts can communicate with the
  335.   <a href="http://code.google.com/chrome/extensions/content_scripts.html#host-page-communication">embedding page</a>
  336.   is through the DOM. The DOM is what shared between both worlds, since they
  337.   are completely isolated from each other.</p>
  338. <p>To do this, we inject the Facebook page with a 'script' tag that creates a
  339.   custom event. The custom event dispatches when friends object has been retrieved.
  340.   We use custom events because everything is asynchronous. We want to inform
  341.   the Content Script that we have successfully grabbed the object. If you are
  342.   familiar with how Java does drag and drop, the concept of Transfer nodes
  343.   are similar in this case (kinda).</p>
  344. <pre class="code">
  345. // JS script injection to the facebook's World. This function can only be read
  346. // in the Facebook world, it has absolutely no access to the content script
  347. // nor our extension process.
  348. var postFriendMap = function() {
  349.  // Use events to notify the content script. Replicate the event the content
  350.  // script has, so we can pass this event to that world.
  351.  var exportEvent = document.createEvent('Event');
  352.  exportEvent.initEvent('friendExported', true, true);
  353.  
  354.  // Create a transfer node DOM, since that is the only way two worlds can
  355.  // communicate with each other.
  356.  var transferDOM = document.getElementById('fb-transfer-dom-area');
  357.  transferDOM.innerText = JSON.stringify(FriendSearchPane._data);
  358.    
  359.  // Inform our content script that we have received the object from Facebook.
  360.  window.dispatchEvent(exportEvent);
  361. };
  362. // Create a dummy textarea DOM.
  363. var transferDOM = document.createElement('div');
  364. $(transferDOM).attr('id', 'fb-transfer-dom-area')
  365.              .hide()
  366.              .appendTo($(document.body));
  367.  
  368. // Start injecting the JS script.
  369. var script = document.createElement('script');
  370. script.setAttribute('id', 'fb-inject-area');
  371. script.appendChild(document.createTextNode('(' + postFriendMap + ')();'));
  372. document.body.appendChild(script);
  373. </pre>
  374.  
  375. <p>Our content script listens on that custom event so it will know, asynchronously,
  376.   when we can start processing our list of friends. And the only way to transfer data
  377.   through these two worlds is by the DOM as shown in the snippet above.
  378.   A dummy transfer DIV is created on the DOM that both worlds understand.</p>
  379.  
  380. <pre class="code">
  381. // Listen on the real DOM requests to check if the friends list has been exported.
  382. window.addEventListener('friendExported', function() {
  383.  // Save the map to this content script world, so our extension can read it.
  384.  var transferDOM = $('#fb-transfer-dom-area');
  385.  friendsMap = JSON.parse(transferDOM.text()); // Global
  386.  
  387.  // Clean up since we no longer need this.
  388.  $(transferDOM).remove();
  389.  $('#fb-inject-area').remove();
  390.  
  391.  // Count the number of friends.
  392.  var i = Objects.keys(friendsMap).length;
  393.  
  394.  // Tell the worker tab that we are set!
  395.  chrome.extension.sendRequest({friendListReceived: 1, count: i});
  396. });
  397. </pre>  
  398. <p>The listener reads the contents stored in the transfer node, and deserializes it
  399.   back so we can store it. It does all the cleanup afterwards since we
  400.   no longer need it. Since we are in the content script, it asynchronously
  401.   notifies the extension process that we have received the friends list.</p>
  402.  
  403. <p>Voila, we now have read the internal data Facebook has stored.</p>
  404.  
  405. <h3><a href="#top" name="web-sql-database"></a>Caching of Friends using Web SQL Database</h3>
  406. <p>Caching friends is really important for any lengthy process. If computer
  407.   restarted, tab closed, error happened, or anything strange happens, we don't
  408.   want to redo the whole process again. The obvious way is to continue where
  409.   we left off.</p>
  410. <p>There are two ways to do data caching for a Web Application:</p>
  411. <ul>
  412.  <li>HTML5 Local Storage</li>
  413.  <li>HTML5 Web SQL Database (no longer active, IndexDB currently in Standards)</li>
  414. </ul>
  415. <p>localStorage was initially going to be chosen, but the 5MB (2.5MB UTF) limitation
  416.   was my reason of not choosing it. localStorage is just a key[value] synchronous
  417.   storage mechanism, which is super easy to use. Unfortunately, 2.5MB is not
  418.   enough to store all your friends. </p>
  419. <p>Web SQL Database has the limitation as well, but you can override that limitation
  420.   by specifying in the Chrome Extension manifest <a href="http://code.google.com/chrome/extensions/manifest.html#permissions">unlimitedStorage</a>
  421.   It is an asynchronous API that uses SQL syntax. Take a look at the
  422.   <a href="https://github.com/mohamedmansour/fb-exporter/blob/master/js/database.js">database.js</a>
  423.   file in the codebase on GitHub. The following snippet is how getting a freind
  424.   from the cache is done.</p>
  425. <pre class="code">
  426. FriendDB.prototype.getFriend = function(id, response) {
  427.  this.db.transaction(function(tx) {
  428.    tx.executeSql('SELECT data FROM friend WHERE id = ?', [id],
  429.        function (tx, rs) {
  430.          if (rs.rows.length != 0) {
  431.            response({status: true, data: JSON.parse(rs.rows.item(0).data)});
  432.          }
  433.          else {
  434.            response({status: false});
  435.          }
  436.        }, this.onError
  437.    );
  438.  });
  439. };
  440. </pre>
  441. <p>In the controller page, once the friends list is requested (so I can render
  442.   my friends in the page), I iterate through all the friends, and check if each
  443.   user is in the cache, if they are in the cache, I inform the Facebook page
  444.   that a cache exists. A separate cache map is stored so when it comes to a time
  445.   to process each friend, it skips that friend because the data is already in
  446.   the cached map.</p>
  447. <pre class="code">
  448. chrome.tabs.sendRequest(bkg.facebook_id, {getFriendsMap: 1}, function(response) {
  449.  $.each(response.data, function(key, value) {
  450.    bkg.db.getFriend(key, function(result) {
  451.      if (result.status) {
  452.        chrome.tabs.sendRequest(bkg.facebook_id, ({cached: true, data: result.data}));
  453.      }
  454.    })
  455.  });
  456. });
  457. </pre>  
  458. <p>The beauty of having caching, it will result in a neat progress bar :) It
  459. isn't instant, because I am making an SQL call for each user. Sure, I can
  460. make one big SQL query that checks each ID, but this is cooler since it
  461. shows to the user something is happening :)- But it is on my TODO list.</p>
  462.  
  463. <h3><a href="#top" name="sending-data-to-gmail"></a>Sending the data to Gmail Contacts</h3>
  464. <p>The extension uses <a href="http://oauth.net/">OAuth</a> (which is an open protocol to allow secure API
  465.   authorization), <a href="http://code.google.com/apis/contacts/docs/3.0/developers_guide_protocol.html#CreatingGroups">Google Contacts</a>
  466.   API (yes its public, and you  can import and export), and <a href="http://code.google.com/apis/gdata/">Google Data API</a>
  467.   (to transfer the data in a nice format)</p>
  468. <p>I wont explain much here, all that is done here is saving the data in Gmail Contacts.
  469.   We use the APIs mentioned above to do this securely. Of course, there is absolutely
  470.   no way I can know your authorization, it uses OAuth for secure Authorization.
  471.   The first thing that happens, it creates a group called "Exported from Facebook".
  472.   There is some smart logic there that checks for duplicate friends, and only
  473.   adds friends that are not there. You can later log on Gmail Contacts and organize
  474.   your friends in more groups and merge contacts together (very neat UI).</p>
  475.  
  476. <h3><a href="#top" name="conclusion"></a>Conclusion</h3>
  477. <p>Well, I am not hating on Facebook. My country, Canada, due to privacy laws,
  478.   we own our data. Our data is private information, we cannot let Facebook
  479.   control our data and own it. I like everything to be in the clear, and that
  480.   is why I created this blog post, to show everyone how its done. Sure I spent
  481.   numerous hours designing, developing, and debugging it (I have spent many
  482.   hours behind my computer to make sure its right). But I am happy of the end
  483.   result. All my contacts are safely in Gmail and I can happily contact my friends
  484.   through other means, and synchronize my friends with my phones and computers.</p>
  485. <p>I hope this post helped you understand the technology behind creating this
  486.   extension, and will help you create your own in the near future. The Chrome
  487.   Extension API is very very very amusing to work with :)</p>]]></description>
  488. <pubDate>Sun, 16 Jan 2011 23:21:49 -0500</pubDate>
  489. <guid>http://www.m0interactive.com/archives/2011/01/16/facebook_friend_export_chrome_extension_internals.html</guid>
  490. </item>
  491. <item>
  492. <title>Software - My favourite Google Chrome extensions</title>
  493. <link>http://www.m0interactive.com/archives/2010/12/31/my_favourite_google_chrome_extensions.html</link>
  494. <description><![CDATA[<p>Well, I just want to give my shoutouts to the extensions that I love, and need recognition. I basically use them every single day. Some of them you can see, some of them run in the background. Everywhere I go, I recommend them :) Unfortunately, none on that list is what I created :)</p>
  495.  
  496. <h3>Google Quick Scroll</h3>
  497. <a href="https://chrome.google.com/webstore/detail/okanipcmceoeemlbjnmnbdibhgpbllgc">https://chrome.google.com/webstore/detail/okanipcmceoeemlbjnmnbdibhgpbllgc</a><br />
  498. <img alt="" src="https://chrome.google.com/webstore/img/okanipcmceoeemlbjnmnbdibhgpbllgc/1292260919.53/thumb" style="float:left;width:128px;height:128px;padding: 10px"/>
  499. <p>
  500. My most useful extension ever! I love exploring the web, Googling my way through
  501. many topics and entertainment. One of my most annoying actions for Googling
  502. before was finding out where in that large webpage the content I Googled. Quick
  503. Scroll lets you jump directly to the relevant bits of a Google search results.
  504. I absolutely love it!
  505. </p>
  506.  
  507. <h3 style="clear:left;">Feedly</h3>
  508. <a href="https://chrome.google.com/webstore/detail/ndhinffkekpekljifjkkkkkhopnjodja">https://chrome.google.com/webstore/detail/ndhinffkekpekljifjkkkkkhopnjodja</a><br />
  509. <img  alt="" src="https://chrome.google.com/webstore/img/ndhinffkekpekljifjkkkkkhopnjodja/1291930890.53/logo128" style="float:left;padding: 10px"/>
  510. <p>I subscribe to many RSS feeds, and Feedly is a real clean and easy to figure
  511. out User Interface. It understands my Google Reader account really nicely, and I
  512. enjoy reading my feeds on it in real time. I usually install the
  513. <a href="https://chrome.google.com/webstore/detail/nlbjncdgjeocebhnmkbbbdekmmmcbfjd">RSS Subscription Extension</a>
  514. to figure out if a page has a feed or not, and if I like the author of that webpage,
  515. I know instantaneously know I can subscribe to it. If you have other RSS readers
  516. that connect nicely to Google Reader, please let me know.
  517. </p>
  518.  
  519. <h3 style="clear:left;">Disconnect</h3>
  520. <a href="https://chrome.google.com/webstore/detail/jeoacafpbcihiomhlakheieifhpjdfeo">https://chrome.google.com/webstore/detail/jeoacafpbcihiomhlakheieifhpjdfeo</a><br />
  521. <img  alt="" src="https://chrome.google.com/webstore/img/jeoacafpbcihiomhlakheieifhpjdfeo/1293464345.48/logo128" style="float:left;padding: 10px"/>
  522. <p>I didn't ming when every website added those sharing buttons to every single post.
  523. But I really hated when third parties such as Facebook, Twitter, Yahoo, added some
  524. tracking information the webpages, such as Fans. I get really distracted from
  525. the website itself, and I don't use those features. Disconnect, really disconnects
  526. them from web. It really works! It uses the "beforeload" event handler so it
  527. guarantees those services will never make it to the website. I personally
  528. like to control the information I make public or visible, I don't like
  529. websites tracking me.</p>
  530.  
  531. <h3 style="clear:left;">AdBlock</h3>
  532. <a href="https://chrome.google.com/webstore/detail/gighmmpiobklfepjocnamgkkbiglidom">https://chrome.google.com/webstore/detail/gighmmpiobklfepjocnamgkkbiglidom</a> <br />
  533. <img  alt="" src="https://chrome.google.com/webstore/img/gighmmpiobklfepjocnamgkkbiglidom/1293566228.65/logo128" style="float:left;padding: 10px"/>
  534. <p>This is clearly the most popular Chrome extension. The title says it all,
  535. blocks ads all over the web. I actually enable Google search ads, I find them
  536. relevant. But every other ad, I really don't like, they distract me from the
  537. content, expecially those annoying "like dislike" Facebook Ads. Horrible!
  538. I somehow get offended seeing Facebook ads.</p>
  539.  
  540. <h3 style="clear:left;">IE Tab =)</h3>
  541. <a href="https://chrome.google.com/webstore/detail/hehijbfgiekmjfkfjpbkbammjbdenadd">https://chrome.google.com/webstore/detail/hehijbfgiekmjfkfjpbkbammjbdenadd</a><br />
  542. <img  alt="" src="https://chrome.google.com/webstore/img/hehijbfgiekmjfkfjpbkbammjbdenadd/1292882914.27/thumb" style="float:left;width:128px;height:128px;padding: 10px"/>
  543. <p>So why did I pick IE? Many corporate websites of mine don't work well with Chrome,
  544. or render well with Chrome. Since I am on the Canary version of Google Chrome,
  545. sometimes Chrome messes up rendering. By clicking a simple button, the renderer
  546. switches to an IE renderer on that page. It is useful for testing, to see if your
  547. website is compatible with IE. Would be cool if there were a FireFox and Opera
  548. plugin. Will make testing cross browser easier.</p>
  549. <h3 style="clear:left;">Some other extensions that I use</h3>
  550. <p>I don't use these extensions alot, but they are pretty cool, and I use them
  551. once in a while.</p>
  552. <ul>
  553.   <li><a href="https://chrome.google.com/webstore/detail/ehohhddamheegbbkabfgegbaeminghlb">exfm</a>:
  554.   Pretty neat way to discover music on the web and make it your library!</li>
  555.   <li><a href="https://chrome.google.com/webstore/detail/encaiiljifbdbjlphpgpiimidegddhic">Chromed Bird</a>:
  556.   One of the earliest Twitter clients, and if you use Twitter alot, this is pretty cool.
  557.   It uses the whole Extension stack.</li>
  558.   <li><a href="https://chrome.google.com/webstore/detail/hmdcmlfkchdmnmnmheododdhjedfccka">Eye Dropper</a>:
  559.   I love colors, I am addicted to colours! When I explore the web, any color I see,
  560.   I love saving it to my collection and experiment with it. I used to take a
  561.   screenshot of my webpage, and open up my favourite image editor program and
  562.   extract the colours from it. That becomes a hassle, Eye Dropper, solves it with
  563.   one click.</li>
  564.   <li><a href="https://chrome.google.com/webstore/detail/oadboiipflhobonjjffjbfekfjcgkhco">Chrome to Phone</a>:
  565.    I use my cell phone a lot, I am in a hurry usually, and I don't like interrupting
  566.    what I am doing online when reading an article, watching a video, etc. This
  567.    solves it, sends it to my phone instantaneously. I even like it when I select
  568.    a phone number, it calls it :)
  569.   </li>
  570.   <li><a href="https://chrome.google.com/webstore/detail/chklaanhfefbnpoihckbnefhakgolnmc">JSON View</a>:
  571.    Many webservices are using JSON as their response, it is the easiest to handle
  572.    in JavaScripts. Without this extension, viewing JSON data is ugly, you don't
  573.    see the markup or the structure which makes JSON important. This extension solves
  574.    that, you see a pretty structured JSON document if you visit it.
  575.   </li>
  576. </ul>
  577.  
  578. <h3>The extensions that I developed</h3>
  579. <p>Well, I created some extensions that helped me in some situations, but
  580. helped others greatly. I usually create extensions that people want, not
  581. necessarily what I want. Since I am active in the Chrome mailing lists and
  582. official Chrome forums, the result of these extensions were requested from
  583. users. Believe it or not, they wont use Chrome without these extensions :) Of
  584. course, as usual, everything is open sourced on my <a href="http://github.com/mohamedmansour/">GitHub</a> page!</p>
  585. <ul>
  586.   <li><a href="https://chrome.google.com/webstore/detail/bfenodnbilondijnaionekngdhadmegk">Reload All Tabs</a>:
  587.    Many people wanted a way to reload all tabs in their window, or all windows.
  588.    But you couldn't do that in Chrome, while in other browsers you could. Initially
  589.    I submitted a patch to do this natively, but it was rejected because they
  590.    thought this is not a feature that many people want, which I agreed. When
  591.    they implemented extensions for Chrome, I quickly made that, and it made
  592.    many people happy!
  593.   </li>
  594.   <li><a href="https://chrome.google.com/webstore/detail/ddkmiidlgnkhnfhigdpadkaamogngkin">Set Image as Wallpaper</a>:
  595.    This was the most requested feature, thousands of people wanted it for over a year.
  596.    This was an awesome feature to work with, a pretty complex one too. Every browser has an option to set an image
  597.    as a wallpaper, except Google Chrome. This uses neat HTML5 technology to
  598.    preview before setting it. You have options to Stretch, Center, Fill, Fit, and Tile.
  599.    It was featured on many known blogs and praised by many. Believe it or not,
  600.    I usually never used this feature on other browsers, but now I do :) Kinda
  601.    cool changing your wallpaper every day or so.
  602.   </li>
  603.   <li><a href="https://chrome.google.com/webstore/detail/ofbafcaeafjchlcknlmcgaijglnkdnja">Open link in foreground tab</a>:
  604.    All links in Chrome opens in background, this will add a context menu to open a link in the foreground.</li>
  605.   <li><a href="https://chrome.google.com/webstore/detail/ndmbeogingkjkmmkoomnigifmpajmbkc">Prayer Times</a>:
  606.    Muslim praying times schedule that uses geolocation to get the sunset and sunrise.
  607.    It doesn't require an online connection to get the prayer times, and it has a
  608.    complete month to month prayer schedule by a click of a button.</li>
  609. </ul>]]></description>
  610. <pubDate>Fri, 31 Dec 2010 00:01:40 -0500</pubDate>
  611. <guid>http://www.m0interactive.com/archives/2010/12/31/my_favourite_google_chrome_extensions.html</guid>
  612. </item>
  613. <item>
  614. <title>Software - How to communicate back to JavaScript from NPAPI plugin</title>
  615. <link>http://www.m0interactive.com/archives/2010/10/21/how_to_communicate_back_to_javascript_from_npapi_plugin.html</link>
  616. <description><![CDATA[<p>I might be a bit late learning plugin development for the browser, but I really
  617. wanted to know how to create a plugin. It turned out to be such an easy task and awesome
  618. framework. You can do a lot with NPAPI, and the cool thing about it is the
  619. ability to let your C++ code communicate back to the JavaScript world easily.</p>
  620. <h3>How to send a debug message to the browsers console</h3>
  621. <p>The best way to teach someone how to send a message back to the browser is
  622.    with an example. Afterwards, I will explain each step in detail. So here is the
  623.    JavaScript code that we want to call from our C++ plugin:</p>
  624.  
  625. <pre class="code">
  626.   console.debug("Hello from C++");
  627. </pre>
  628.  
  629. <p>The equivalent C++ code for the above JavaScript would be the following:</p>
  630. <pre class="code">
  631.  // The message we want to send.
  632.  char* message = "Hello from C++";
  633.  
  634.  // Get window object.
  635.  NPObject* window = NULL;
  636.  NPN_GetValue(npp_, NPNVWindowNPObject, &window);
  637.  
  638.  // Get console object.
  639.  NPVariant consoleVar;
  640.  NPIdentifier id = NPN_GetStringIdentifier("console");
  641.  NPN_GetProperty(npp_, window, id, &consoleVar);
  642.  NPObject* console = NPVARIANT_TO_OBJECT(consoleVar);
  643.  
  644.  // Get the debug object.
  645.  id = NPN_GetStringIdentifier("debug");
  646.  
  647.  // Invoke the call with the message!
  648.  NPVariant type;
  649.  STRINGZ_TO_NPVARIANT(message, type);
  650.  NPVariant args[] = { type };
  651.  NPVariant voidResponse;
  652.  NPN_Invoke(npp_, console, id, args,
  653.             sizeof(args) / sizeof(args[0]),
  654.             &voidResponse);
  655.  
  656.  // Cleanup all allocated objects, otherwise, reference count and
  657.  // memory leaks will happen.
  658.  NPN_ReleaseObject(window);
  659.  NPN_ReleaseVariantValue(&consoleVar);
  660.  NPN_ReleaseVariantValue(&voidResponse);
  661. </pre>
  662. <h3>The browser object</h3>
  663. <pre class="code">
  664.  NPObject* window = NULL;
  665.  NPN_GetValue(npp_, NPNVWindowNPObject, &window);
  666. </pre>
  667. <p>The first thing we need is an <span class="code">NPObject</span>
  668.    representation of the browser object. From there, the functions in this API
  669.    can be used to get and set properties and call methods on those browser objects.</p>
  670. <p>The browser object in this case is javascript's <a href="https://developer.mozilla.org/en/DOM/window" class="code">window</a>
  671.    object. The <span class="code">window</span> object represents the window itself, which
  672.    we want to execute a property in our case the <a href="http://getfirebug.com/wiki/index.php/Console_API" class="code">console</a>.
  673.    Console debugging is the default debugging messaging for Google Chrome and Firebug on Firefox.</p>
  674. <p>To access the browser object from NPAPI, it is done by using an extension to
  675.    <a href="https://developer.mozilla.org/en/NPN_GetValue" class="code">NPN_GetValue()</a>. The extension
  676.    is the <span class="code">NPNVWindowNPObject</span> enumeration. By calling <span class="code">NPN_GetValue</span>
  677.    with that enumeration, it will return an <span class="code">NPObject</span> representing the browser object.</p>
  678. <p>An <a href="https://developer.mozilla.org/en/NPObject" class="code">NPObject</a> is a structure that holds
  679.    a pointer to an <span class="code">NPClass</span> which is defined within your plugin. It is the type
  680.    that is used to express objects exposed by the browser in this case. This is the entry point of what
  681.    we need to execute in the JavaScript side.</p>
  682. <p>So now we have access to the <span class="code">window</span> object within the JavaScript DOM.</p>
  683. <h3>Calling the console property</h3>
  684. <pre class="code">
  685.   // Get console object.
  686.  NPVariant consoleVar;
  687.  NPIdentifier id = NPN_GetStringIdentifier("console");
  688.  NPN_GetProperty(npp_, window, id, &consoleVar);
  689.  NPObject* console = NPVARIANT_TO_OBJECT(consoleVar);
  690. </pre>
  691. <p>The plugin now has a pointer to the browser's <span class="code">window</span>
  692.    DOM object. Now time to call its' <span class="code">console</span> property
  693.    which allows us to call its functions such as, <span class="code">console.debug()</span></p>
  694. <p>To do so, we need an <a href="https://developer.mozilla.org/en/NPIdentifier" class="code">NPIdentifer</a>
  695.    that identies the JavaScript string that we want to call. We can use the macro,
  696.    <a href="https://developer.mozilla.org/en/NPN_GetStringIdentifier" class="code">NPN_GetStringIdentifier</a>
  697.    to quickly get the identifier for that string. That is used to tell NPAPI
  698.    what to execute in the <span class="code">NPObject</span>.</p>
  699. <p>Since we have the <span class="code">window</span> object and <span class="code">id</span>
  700.    identifier  we can execute that property to retrieve the console <span class="code">NPVariant.</span>
  701.    The <a href="https://developer.mozilla.org/en/NPVariant" class="code">NPVariant</a> is a struct
  702.    that holds a value and the type of that value. Think of it as a JavaScript representation
  703.    of a variable in NPAPI. It is the response that comes back from the browsers,
  704.    in this case, it is just a console <span class="code">NPObject</span>.
  705.    Since we know its an <span class="code">NPObject</span>, we can extract the
  706.    <span class="code">NPVariant</span> to an <span class="code">NPObject</span>
  707.    with the simple to use macro, <a href="https://developer.mozilla.org/en/NPVariant" class="code">NPVARIANT_TO_OBJECT</a></p>
  708.  
  709. <h3>Invoking debug</h3>
  710. <pre class="code">
  711.  // Get the debug object.
  712.  id = NPN_GetStringIdentifier("debug");
  713.  
  714.  // Invoke the call with the message!
  715.  NPVariant type;
  716.  STRINGZ_TO_NPVARIANT(message, type);
  717.  NPVariant args[] = { type };
  718.  NPVariant voidResponse;
  719.  NPN_Invoke(npp_, console, id, args,
  720.             sizeof(args) / sizeof(args[0]),
  721.             &voidResponse);
  722. </pre>
  723. <p>The final step is to invoke a debug function on the console <span class="code">NPObject</span>
  724.    returned by the previous step. We do the same thing we did before, we need an
  725.    <span class="code">NPIdentifer</span> that idenfies the javascript, in this case
  726.    <span class="code">debug</span>. Note, we reuse the same  <span class="code">NPIdentifier</span> id.</p>
  727. <p>Since <span class="code">window.console.debug(message)</span> requests a string
  728.    parameter, as we discussed before, a parameter is just an <span class="code">NPVariant</span>.
  729.    To convert a <span class="code">char*</span> to a <span class="code">NPVariant</span> string,
  730.    we can use a macro, <span class="code">STRINGZ_TO_NPVARIANT</span>. All the NPAPI macros are defined
  731.    in the <a href="https://developer.mozilla.org/en/NPVariant" class="code">NPVariant</a> docs.
  732.    You should read them, it is great to know which macros are available. Makes your
  733.    life easier to use them. </p>
  734. <p>Once we constructed our <span class="code">NPVariant</span> args array, we can
  735.    finally invoke the console <span class="code">NPObject</span> defined earlier with
  736.    the args we defined right now. The result would just be a void response.</p>
  737.  
  738. <h3>Don't forget to cleanup!</h3>
  739. <pre class="code">
  740.  // Cleanup all allocated objects, otherwise, reference count and
  741.  // memory leaks will happen.
  742.  NPN_ReleaseObject(window);
  743.  NPN_ReleaseVariantValue(&consoleVar);
  744.  NPN_ReleaseVariantValue(&voidResponse);
  745. </pre>
  746. <p>As you all know, cleaning up unused pointers is critical to any C++ application :).
  747.    Failing to do so in NPAPI, will result in buggy plugins that will crash. Yes,
  748.    I said it, it will "really" crash!. So cleanup your window object, and your
  749.    variants as shown in the snippet!</p>
  750. <h3>Conclusion</h3>
  751. <p>I hope you realize how easy it is to call JavaScript directly from NPAPI,
  752.    once you understand the code involves, it becomes easy to do anything.
  753.    Just be careful cleaning up afterwards, or you will have memory leaks and
  754.    plugin crashes. I hope this article will benefit any of you, and I am
  755.    excited to share my NPAPI journey with you in the near future!</p>
  756. <p>My next article will be talking about the design and architecture of a NPAPI
  757.    plugin, and what the future has with NPAPI. Google engineers are working hard
  758.    creating future sandboxes plugins which are safer and easier to use such as
  759.    PEPPER and NaCL (discontinued "kinda" in favor of PEPPER) :)</p>]]></description>
  760. <pubDate>Thu, 21 Oct 2010 20:18:23 -0400</pubDate>
  761. <guid>http://www.m0interactive.com/archives/2010/10/21/how_to_communicate_back_to_javascript_from_npapi_plugin.html</guid>
  762. </item>
  763. <item>
  764. <title>Software - Just released RhymeBrain on Android</title>
  765. <link>http://www.m0interactive.com/archives/2010/09/12/just_released_rhymebrain_on_android.html</link>
  766. <description><![CDATA[<p>Want to figure out which words sound similar? RhymeBrain, rhyme's any word you give it
  767. intelligently. You can drill down on every word to find out which word sounds best for you.
  768. You can filter the words based on offensive words and you can show only real words.</p>
  769.  
  770. <h3>About</h3>
  771. <p>Using <a href="http://stevehanov.ca">Steve Hanov's</a> awesome RhymeRank<sup>TM</sup> system,
  772. we created this simple to use Android App. Each result is grouped by a score that is separated
  773. by an orange line. </p>
  774.  
  775. <p>FAQ from RhymeBrain:</p>
  776. <pre style="overflow:hidden; border-left: 10px solid #eee; padding-left: 5px">
  777. <span style="font-size: 1.5em; color: #ccc;font-weight:bold">Quote...</span>
  778. Using advanced artificial intelligence techniques, RhymeBrain
  779. figures out how to pronounce a word even if it has never seen
  780. it before. RhymeBrain then compares it to the 260,000 words
  781. in its database to find ones that sound similar.
  782. Finally, it sorts them so that the best rhymes appear first, using
  783. our RhymeRank<sup>TM</sup> system.
  784.  
  785. <a href="http://rhymebrain.com/about.html">Source</a>
  786. </pre>
  787.  
  788. <h3>Screenshots</h3>
  789. <img src="http://www.m0interactive.com/images/blog/rhymebrain/device.png" alt="RhymeBrain" />
  790. <img src="http://www.m0interactive.com/images/blog/rhymebrain/device2.png" alt="RhymeBrain" />
  791. <br />
  792. <img src="http://www.m0interactive.com/images/blog/rhymebrain/device3.png" alt="RhymeBrain" />
  793. <img src="http://www.m0interactive.com/images/blog/rhymebrain/device4.png" alt="RhymeBrain" />
  794.  
  795. <h3>Download</h3>
  796. <p>It is on the Market right now, for free so please help me test it with all the
  797. unique devices out there. Scan this QR Code to download the app! Or search for <a href="market://details?id=com.mindtechnologies.rhymebrain">RhymeBrain</a> from the Market.</p>
  798. <img src="http://chart.apis.google.com/chart?cht=qr&chs=400x400&chl=market://details?id=com.mindtechnologies.rhymebrain" />]]></description>
  799. <pubDate>Sun, 12 Sep 2010 10:34:33 -0400</pubDate>
  800. <guid>http://www.m0interactive.com/archives/2010/09/12/just_released_rhymebrain_on_android.html</guid>
  801. </item>
  802. <item>
  803. <title>Javascript - How to preload an IFrame</title>
  804. <link>http://www.m0interactive.com/archives/2010/06/10/how_to_preload_an_iframe.html</link>
  805. <description><![CDATA[<p>One of the annoyances of an IFrame is that if the frame takes time to load, you will see white space for a short moment then the page will appear. The reason why I did this is for Google Docs. I had a presentation I want to embed on my <a href="http://mohamedmansour.com/">website</a>, and it was taking time. I would like to share what I have done, although simple, but yet powerful, and hope it will help you. In this post, I will explain to you how to make a simple textual preloader for your iframe</p>
  806.  
  807. <h3>Why do we need a preloader?</h3>
  808. <p>Sure some websites are efficient at loading, but the download time might vary. If you are adding widgets as I explained earlier, you will see that it is quite annoying having this white space in the middle and just reappear. It degrades user experience and just does not feel right. By adding a very simple preloader to that iframe, the user will know something is going to happen in that area.</p>
  809.  
  810. <h3>Small demo</h3>
  811. <p>Refresh this page and look at this area to see how it works.</p>
  812. <iframe src="http://yahoo.com" width="100%" height="342" id="testFrame"></iframe>
  813.  
  814. <h3>The idea</h3>
  815. <p>It is pretty simple and straight forward! We create some sort of placeholder that contains our textual loader, in this case it will be just a div with some loading text.</p>
  816.  
  817. <p>At the end of the webpage, right before the body tag, we inject some Javascript to hide the frame and show the preloader. With a simple <a href="https://developer.mozilla.org/en/DOM/element.addEventListener">window load listener</a>, we can know when the page has been fully loaded.</p>
  818.  
  819. <p>Once the page loads, we can hide the preloader and show the iframe. Voila, a simple preloader!</p>
  820.  
  821. <h3>The code</h3>
  822. <p>On your main page, right before the end body tag you insert the following:</p>
  823. <pre>
  824.  <script>
  825.    var preloader = new IFramePreloader('testFrame');
  826.    preloader.init();
  827.  </script>
  828. </pre>
  829.  
  830. <p>The iframepreloader.js is below:</p>
  831. <pre>
  832. /**
  833. * Global Static Event Utilities helper that assists us to add
  834. * cross platform events to any DOM object as well retrieving
  835. * their target if any exists.
  836. *
  837. * @author Mohamed Mansour (http://mohamedmansour.com)
  838. */
  839. var event_utils = {
  840.  add: function(obj, type, callback) {
  841.    if (obj.addEventListener)
  842.      obj.addEventListener(type, callback, false);
  843.    else if (obj.attachEvent)
  844.      obj.attachEvent("on" + type, callback);
  845.  }
  846. }
  847.  
  848.  
  849. /**
  850. * Preloads an IFrame since it may take time to load.
  851. *
  852. * @param {string} id The identifier for the Iframe you want preloaded.
  853. * @author Mohamed Mansour (http://mohamedmansour.com)
  854. */
  855. IFramePreloader = function(id)
  856. {
  857.  that = this;
  858.  this.id = id;
  859.  this.iframe = document.getElementById(id);
  860.  this.placeholder = this.createPlaceholder();
  861. }
  862.  
  863. IFramePreloader.prototype = {
  864.  
  865.  /**
  866.  * Initializes the preloader to wait till a load event occurs in the iframe.
  867.  */
  868.  init: function()
  869.  {
  870.    this.iframe.style.display = 'none';
  871.    event_utils.add(this.iframe, 'load', function(e) { that.handleLoad(e); });
  872.  },
  873.  
  874.  /**
  875.  * Creates the placeholder for that Iframe.
  876.  * @return {Element} The placeholder element.
  877.  */
  878.  createPlaceholder: function()
  879.  {
  880.    // Create placeholder.
  881.    var newElement = document.createElement('div');
  882.    newElement.id = this.id + '-placeholder';
  883.    newElement.appendChild(document.createTextNode('Loading ...'));
  884.  
  885.    // Adding that placeholder right after the iframe. We first check if its the
  886.    // last child, if so, we just append it. Otherwise, we insert it before the
  887.    // next sibling.
  888.    var parent = this.iframe.parentNode;
  889.    if (parent.lastChild == this.iframe) {
  890.      parent.appendChild(newElement);
  891.    } else {
  892.      parent.insertBefore(newElement, this.iframe.nextSibling);
  893.    }
  894.    return newElement;
  895.  },
  896.  
  897.  /**
  898.  * Show the frame, hide the preloader. Since it has been loaded!
  899.  */
  900.  handleLoad: function()
  901.  {
  902.    this.iframe.style.display = 'block';
  903.    this.placeholder.style.display = 'none';
  904.  }
  905. }
  906.  
  907. </pre>
  908.  
  909. <h3>Conclusion</h3>
  910. <p>As you have seen, it is very straight forward adding an indeterminate preloader to your iframe. Some may want images, flash, movies, or anything else. It is very easy to customize it. From the code above, <code>createPlaceholder</code> creates that placeholder. If you want an image, you can add an image easily there, same goes for any types.</p>
  911.  
  912. <p>If you have any questions, please let me know in the comments below or through email!</p>
  913.  
  914. <script>
  915. /**
  916. * Global Static Event Utilities helper that assists us to add
  917. * cross platform events to any DOM object as well retrieving
  918. * their target if any exists.
  919. *
  920. * @author Mohamed Mansour (http://mohamedmansour.com)
  921. */
  922. var event_utils = {
  923.  add: function(obj, type, callback) {
  924.    if (obj.addEventListener)
  925.      obj.addEventListener(type, callback, false);
  926.    else if (obj.attachEvent)
  927.      obj.attachEvent("on" + type, callback);
  928.  }
  929. }
  930.  
  931.  
  932. /**
  933. * Preloads an IFrame since it may take time to load.
  934. *
  935. * @param {string} id The identifier for the Iframe you want preloaded.
  936. * @author Mohamed Mansour (http://mohamedmansour.com)
  937. */
  938. IFramePreloader = function(id)
  939. {
  940.  that = this;
  941.  this.id = id;
  942.  this.iframe = document.getElementById(id);
  943.  this.placeholder = this.createPlaceholder();
  944. }
  945.  
  946. IFramePreloader.prototype = {
  947.  
  948.  /**
  949.  * Initializes the preloader to wait till a load event occurs in the iframe.
  950.  */
  951.  init: function()
  952.  {
  953.    this.iframe.style.display = 'none';
  954.    event_utils.add(this.iframe, 'load', function(e) { that.handleLoad(e); });
  955.  },
  956.  
  957.  /**
  958.  * Creates the placeholder for that Iframe.
  959.  * @return {Element} The placeholder element.
  960.  */
  961.  createPlaceholder: function()
  962.  {
  963.    // Create placeholder.
  964.    var newElement = document.createElement('div');
  965.    newElement.id = this.id + '-placeholder';
  966.    newElement.appendChild(document.createTextNode('Loading ...'));
  967.  
  968.    // Adding that placeholder right after the iframe. We first check if its the
  969.    // last child, if so, we just append it. Otherwise, we insert it before the
  970.    // next sibling.
  971.    var parent = this.iframe.parentNode;
  972.    if (parent.lastChild == this.iframe) {
  973.      parent.appendChild(newElement);
  974.    } else {
  975.      parent.insertBefore(newElement, this.iframe.nextSibling);
  976.    }
  977.    return newElement;
  978.  },
  979.  
  980.  /**
  981.  * Show the frame, hide the preloader. Since it has been loaded!
  982.  */
  983.  handleLoad: function()
  984.  {
  985.    this.iframe.style.display = 'block';
  986.    this.placeholder.style.display = 'none';
  987.  }
  988. }
  989. var preloader = new IFramePreloader('testFrame');
  990. preloader.init();
  991. </script> ]]></description>
  992. <pubDate>Thu, 10 Jun 2010 12:54:45 -0400</pubDate>
  993. <guid>http://www.m0interactive.com/archives/2010/06/10/how_to_preload_an_iframe.html</guid>
  994. </item>
  995. <item>
  996. <title>Software - Chromium OS compiled in Debian running in VirtualBox</title>
  997. <link>http://www.m0interactive.com/archives/2009/11/20/chromium_os_compiled_in_debian_running_in_virtualbox.html</link>
  998. <description><![CDATA[<p>So today Google announced Chrome OS and released the source code. So I grabbed the source and started lurking! I have compiled it with Debian and created the chromiumos.vmdk drive that you can load in VirtualBox or VMWare Player. In this article, I will explain what extra stuff I needed to make it compile on Debian and you can download the working image at the end of this article.</p>
  999.  
  1000. <h3>How to compile in Debian</h3>
  1001. <p>Make sure you read the dev docs here <a href="http://dev.chromium.org/chromium-os/building-chromium-os" > http://dev.chromium.org/chromium-os/building-chromium-os </a>, it works perfectly. So make sure you install: </p>
  1002. <ol>
  1003. <li>Prerequisites</li>
  1004. <li>Download the Source Code http://src.chromium.org/git/chromiumos.git</li>
  1005. <li>And build!</li>
  1006. </ol>
  1007.  
  1008. <p>In Debian make sure you have "qemu" installed (sudo apt-get install qemu), that is needed to create the image. And make sure you add sbin to your path (export PATH=/sbin:/usr/sbin:"$PATH" or it wont work. Thats all!</p>
  1009.  
  1010.  
  1011. <h3>How it feels</h3>
  1012. <p>Okay, it booted in 2 seconds in a VM! That is bloody quick. It seems slow in the VM but that is normal. It really seems like an unfinished product and many stuff hasn't been cleaned. Such as, many broken links (internal Google sites) still in tact. Keyboard shortcuts doesn't for many. The only thing you can make working is use the internet. Good for a start, I guess!</p>
  1013.  
  1014. <h3>How to run Chromium OS</h3>
  1015. <p>I used VirtualBox and VMWare Player, VirtualBox was very simple to use. All you had to do is follow these steps. Remember to download the virtual disk below:</p>
  1016. <div style="border: 1px dotted #ccc">
  1017.  <a href="http://mohamedmansour.com/" style="color:black">
  1018.    <img src="http://mohamedmansour.com/chrome/chrome-icon.png" alt="Download ChromeOS" style="float:left"/>
  1019.    <p>
  1020.      <strong><del>Download Chromium OS (Root password: chrome)</del>No longer exists.</strong><br />
  1021.      Virtual Machine Disk Format (works on VirtualBox and VMWare Player)
  1022.    </p>
  1023.  </a>
  1024. </div>
  1025. <p>Steps to run it:</p>
  1026. <ol>
  1027. <li>Download from <a href="http://www.virtualbox.org/">http://www.virtualbox.org/</a>, its free and Open Source! Install it.</li>
  1028. <li>Create a new Virtual Machine.</li>
  1029. <li>Type any name (ChromeOS)</li>
  1030. <li>The Operating System should be Linux, and the Version could be Other.</li>
  1031. <li>Use an existing hard disk, point to the chromiumos.vmdk file </li>
  1032. <li>Thats it, press start!</li>
  1033. </ol>
  1034.  
  1035. <img src="http://www.m0interactive.com/images/blog/chromeos/chromeos_start.jpg" alt=""/>
  1036.  
  1037. <img src="http://www.m0interactive.com/images/blog/chromeos/chromeos_inside.jpg" alt=""/>
  1038. ]]></description>
  1039. <pubDate>Fri, 20 Nov 2009 02:45:54 -0500</pubDate>
  1040. <guid>http://www.m0interactive.com/archives/2009/11/20/chromium_os_compiled_in_debian_running_in_virtualbox.html</guid>
  1041. </item>
  1042. <item>
  1043. <title>Technology - How to Enable WebGL on Google Chrome</title>
  1044. <link>http://www.m0interactive.com/archives/2009/10/26/how_to_enable_webgl_on_google_chrome.html</link>
  1045. <description><![CDATA[<p>This month, WebGL has been released in its pre-state in Firefox, Safari, and Chromium. WebGL is a cool way to display 3D content in the Web using OpenGL.</p>
  1046.  
  1047. <fieldset>
  1048. <legend>UPDATE</legend>
  1049. <p>It has been almost a year since we released WebGL, to enable that in Chrome / Chromium, you just need the command line argument: </p>
  1050. <p> Windows: </p>
  1051. <pre>
  1052. chrome.exe   --enable-webgl
  1053. </pre>
  1054. <p> Linux / Mac: </p>
  1055. <pre>
  1056. ./chrome --enable-webgl
  1057. </pre>
  1058.  
  1059. <p>To edit it permanently, edit your shortcut properties by right clicking on the Chrome shortcut and choosing 'Properties', and append the command line argument above (--enable-webgl) within the target.</p>
  1060. </fieldset>
  1061.  
  1062.  
  1063. <h3>What is WebGL</h3>
  1064. <p>WebGL is a JavaScript binding to OpenGL ES 2.0 for 3D Web Graphics without installing any web plugins. The Khronos Group (the creators/maintainers of OpenGL standard) working group includes many industry leaders such as Google, Mozilla, NVIDIA, Opera, AMD, etc to work on standardizing WebGL. The press release can be read <a href="http://www.khronos.org/news/press/releases/khronos-webgl-initiative-hardware-accelerated-3d-graphics-internet">here</a>. WebGL is currently still in development, if you want to keep track of its status, you can star this issue: <a href="http://code.google.com/p/chromium/issues/detail?id=21852"> http://code.google.com/p/chromium/issues/detail?id=21852</a></p>
  1065.  
  1066. <h3>But how is it different than Google's O3D?</h3>
  1067. <p>Update: O3D decided to concentrate making HTML5 WebGL faster and better. O3D is now an open-source web API for creating rich, interactive 3D applications in the browser.</p>
  1068. <del>
  1069. <p>The main reason is that WebGL is still very slow, O3D is a plugin which is native. There is an interesting thread going on and some Googlers from the O3D team discussed the differences: </p>
  1070. <pre style="overflow:hidden; border-left: 10px solid #eee; padding-left: 5px">
  1071. <span style="font-size: 1.5em; color: #ccc;font-weight:bold">Quote...</span>
  1072. O3D is not going away. WebGL is a very cool initiative but it has
  1073. a lot of hurdles to overcome. The direction of WebGL is trying to
  1074. just expose straight OpenGL ES 2.0 calls to JavaScript.
  1075.  
  1076. JavaScript is still slow in the large scheme of things. Maybe at
  1077. sometime in the future WebGL will have added enough features over
  1078. basic OpenGL to be more powerful or JavaScript  will have gotten
  1079. a few orders of magnitude faster but at the moment\x85
  1080.  
  1081. \x85
  1082. The WebGL team at Google and the O3D team are currently the same
  1083. team. We have every interest in seeing both WebGL and O3D succeed.
  1084.  
  1085. <a href="http://groups.google.com/group/o3d-discuss/browse_thread/thread/7bfa31efcc03b5f6">Read Original Source ...</a>
  1086. </pre>
  1087. </del>
  1088.  
  1089. <h3>How to enable WebGL on Google Chrome / Chromium?</h3>
  1090. <del><p>Make sure you are in the dev channel. Google Chrome releases updates to different release channels. Currently, there are three channels; Stable (everyone is on this when the first install Google Chrome), Beta (every month or so, you will get an update), and Dev (the developer preview channel where stuff gets tested, like WebGL). In order to use WebGL at this current time, you would need the "Beta/Dev" release channel. </p>
  1091. <ol>
  1092. <li>First, subscribe here: <a href="http://www.google.com/chrome/eula.html?extra=devchannel"> http://www.google.com/chrome/eula.html?extra=devchannel </a></li>
  1093. <li>Make sure all other windows are closed.</li>
  1094. <li>Install</li>
  1095. <li>Now you are subscribed to the Development Channel, yay!</li>
  1096. </ol>
  1097. </del>
  1098. <p>After you have subscribed to the Development channel, you would need to put some command line parameters when you launch Google Chrome. </p>
  1099. <ol>
  1100. <li>Right click on your "Chrome" icon.</li>
  1101. <li>Choose properties</li>
  1102. <li>At the end of your target line, place these parameters:
  1103. <ul>
  1104. <li>--enable-webgl</li>
  1105. </ul>
  1106. </li>
  1107. <li>It should look like: "chrome.exe --enable-webgl"</li>
  1108. </ol>
  1109.  
  1110. <p>Now you have WebGL installed! Lets look at a cool example! This example of Escher Droste effect, where one image, rotated, thinking its zooming in forever. So cool! Take a look here: <a href="http://wakaba.c3.cx/w/escher_droste.html"> http://wakaba.c3.cx/w/escher_droste.html </a></p>
  1111.  
  1112. <p>Have fun WebGL'n!</p>]]></description>
  1113. <pubDate>Mon, 26 Oct 2009 23:53:56 -0400</pubDate>
  1114. <guid>http://www.m0interactive.com/archives/2009/10/26/how_to_enable_webgl_on_google_chrome.html</guid>
  1115. </item>
  1116. </channel>
  1117. </rss><!-- m0|XML Creator v1 -->
  1118.  

If you would like to create a banner that links to this page (i.e. this validation result), do the following:

  1. Download the "valid RSS" banner.

  2. Upload the image to your own server. (This step is important. Please do not link directly to the image on this server.)

  3. Add this HTML to your page (change the image src attribute if necessary):

If you would like to create a text link instead, here is the URL you can use:

http://www.feedvalidator.org/check.cgi?url=http%3A//www.m0interactive.com/rss/articles.xml

Copyright © 2002-9 Sam Ruby, Mark Pilgrim, Joseph Walton, and Phil Ringnalda