How do you say “Welcome” in over 150 languages?

Today, out of nowhere, I got the urge to make some changes to my website. I wanted to show the word “Welcome” in a few different languages (just showing it in English all the time was getting a bit boring).

Now, via a few alterations, every time you refresh the site, “Welcome” will be shown in a new language. I use the following list (reference: Omniglot) for this feature:

'Afrikaans': 'Welkom',
'Albanian': 'Mirë se vjen',
'Alsatian': 'Willkumme',
'Amharic': 'እንኳን ደህና መጣህ',
'Arabic (Modern Standard)': 'أهلاً و سهلاً ',
'Aragonese': 'Bienvenius',
'Armenian(Eastern)': 'Բարի գալուստ',
'Armenian(Western)': 'Բարի՜ եկաք',
'Aromanian': 'Ghini vinit',
'Assamese': 'আদৰণি',
'Asturian': 'Bienvenida',
'Azeri': 'Xoş gəlmişsiniz',
'Banjar': 'Salamat datang',
'Basque': 'Ongi etorri',
'Batak': 'Menjuah-juah! Horas',
'Belarusian': 'Прывiтанне',
'Bengali': 'স্বাগতম',
'Bhojpuri': 'स्वागत बा',
'Bosnian': 'Dobrodošli',
'Breton': 'Degemer mat',
'Catalan': 'Benvingut Benvinguts',
'Cebuano': 'Maayong pag-abot',
'Chamorro': 'Bien binidu',
'Cherokee': 'ᎤᎵᎮᎵᏍᏗ',
'Chinese (Cantonese)': '歡迎',
'Chinese (Hakka)': '歡迎',
'Chinese (Mandarin)': '歡迎光臨 [欢迎光临]',
'Chinese (Shanghainese)': '欢迎',
'Chinese (Taiwanese)': '歡迎光臨',
'Cornish': 'Dynnargh dhywgh',
'Cuyonon': 'Malipayeng Pag-abot',
'Czech': 'Vítáme tĕ',
'Danish': 'Velkommen',
'Dutch': 'Welkom',
'Efik': 'A me di o',
'English': 'Welcome',
'Esperanto': 'Bonvenon',
'Estonian': 'Tere tulemast',
'Ewe': 'Woezor',
'Faroese': 'Vælkomin',
'Finnish': 'Tervetuloa',
'French': 'Bienvenue',
'Frisian (North)': 'Wäljkiimen',
'Frisian (West)': 'Wolkom',
'Friulian': 'Agradît',
'Galician': 'Benvido',
'German': 'Willkommen',
'Greek (Modern)': 'Καλώς Ήρθες',
'Greenlandic': 'Tikilluarit',
'Guarani': 'Eguahé porá',
'Gujarati': 'પધારો',
'Haitian': 'Creole Byen venu',
'Hausa': 'Sannu da zuwa',
'Hawaiian': 'Aloha',
'Hebrew': 'ברוכים הבאים',
'Hindi': 'स्वागत',
'Hungarian': 'Üdvözlet',
'Icelandic': 'Velkomin',
'Igbo': 'nno',
'Indonesian': 'Selamat datang',
'Iñupiaq': 'Qaimarutin',
'Inuktitut': 'ᑐᙵᓱ',
'Irish (Gaelic)': 'Céad míle fáilte',
'Italian': 'Benvenuto',
'Japanese': 'ようこそ',
'Javanese': 'Sugeng rawuh',
'Jèrriais': 'Séyiz les beinv\'nus',
'Kannada': 'ಸುಸ್ವಾಗತ',
'Kazakh': 'Қош келдіңіз',
'Khmer': 'សូម​ស្វាគមន៍',
'Kinyarwanda': 'Murakaza neza',
'Korean': '환영합니다',
'Kurdish (Kurmanji)': 'Be kher hati',
'Kurdish (Sorani)': 'Bi xêr bî',
'Kyrgyz': 'Kosh kelinizder',
'Lakota': 'Taŋyáŋ yahípi',
'Lao': 'ຍິນດີຕ້ອນຮັບ',
'Latin': 'Salve',
'Latvian': 'Laipni lūdzam',
'Limburgish': 'Wilkóm',
'Lithuanian': 'Sveiki atvykę',
'Lojban': 'coi ro do',
'Lozi': 'Mu amuhezwi',
'Luxembourgish': 'Wëllkomm',
'Macedonian': 'Добредојде',
'Malagasy': 'Tonga soa e',
'Malay': 'Selamat datang',
'Malayalam': 'സ്വാഗതം',
'Maltese': 'Merħba',
'Manx (Gaelic)': 'Failt royd',
'Māori': 'Nau mai',
'Marathi': 'स्वागत आहे',
'Mazanderani': 'خش به‌مونی',
'Mi\'kmaq': 'Weltasualuleg',
'Mongolian': 'Тавтай морилогтун',
'Nahuatl': 'Ximopanōltih',
'Ndebele (Northern)': 'Siyalemukela',
'Nepali': 'स्वागतम्',
'Newari / Nepal Bhasa': 'लसकुस',
'Northern Sámi': 'Buresboahtin',
'Norwegian': 'Velkommen',
'Oriya': 'Swaagata',
'Papiamento': 'Bon bini',
'Pashto': 'پخير',
'Persian (Farsi)': 'خوش آمدید',
'Polish': 'Witam',
'Portuguese': 'Bem-vindos',
'Punjabi': 'ਜੀ ਆਇਆ ਨੂੰ।',
'Romanian': 'Bine ai venit',
'Romansh': 'Bainvegni',
'Russian': 'Добро пожаловать',
'Samoan': 'Maliu mai',
'Sardinian': 'Ennidos',
'Scots': 'Welcum',
'Scottish Gaelic': 'Fàilte',
'Serbian': 'Добродошли',
'Sesotho': 'Kenang ka kgotso',
'Shona': 'Mauya',
'Sicilian': 'Binvinutu',
'Sinhala': 'සාදරයෙන් පිලිගන්නවා',
'Slovak': 'Vitaj',
'Slovenian': 'Dobrodošli',
'Somali': 'Soo dhowow',
'Spanish': 'Bienvenidos',
'Swahili': 'Karibuni',
'Swedish': 'Välkomna',
'Swiss German': 'Wilkomme',
'Tagalog': 'Mabuhay',
'Tahitian': 'Mānava',
'Tamil': 'வாங்க (vaangha)',
'Tatar': 'Räxim itegez',
'Telugu': 'సుస్వాగతం (susvāgatam)',
'Tetum': 'Bemvindu',
'Thai': 'ยินดีต้อนรับ (yin dee dtôn ráp)',
'Tibetan': 'ཕེབས་པར་དགའ་བསུ་ཞུ།',
'Tok': 'Pisin Welkam',
'Tongan': 'Talitali fiefia',
'Tswana': 'Le amogetswe',
'Turkish': 'Hoş geldin',
'Ukrainian': 'Ласкаво просимо',
'Urdu': 'خوش آمديد',
'Uzbek': 'Xush kelibsiz',
'Venetian': 'Benvignùo',
'Vietnamese': 'Hoan nghênh',
'Volapük': 'Vekömö',
'Võro': 'Tere tulõmast',
'Walloon': 'Benvnuwe',
'Welsh': 'Croeso',
'Wolof': 'Merhbe',
'Xhosa': 'Siya namkela nonke',
'Yiddish': 'ברוכים־הבאים',
'Yorùbá': 'Ẹ ku abọ',
'Yucatec': 'Maya Kíimak \'oolal',
'Zulu': 'Ngiyanemukela'

Believe it or not, I don’t speak all the languages listed above. So, if you see a mistake or if your language is missing, please leave a comment so that I can keep this list accurate and up-to-date.

Posted in Languages, Web Development | Leave a comment

CSS Flexbox Tips

Lately, I’ve been using CSS flexboxes quite a bit. They are a really powerful tool for aligning elements. However, understanding how they work took a bit of time for me but with experience, I think I’ve become quite adept at using them when performing layout related tasks.

So, without further ado, here are some tips:

1) Work with the latest specification

The flexbox specification is in flux and so there are old implementations floating around on the Internet. So, before starting to work with them you should understand what the latest spec outlines. Basically, the latest spec uses display: flex, anything else is old. For more info, have a look at this post: http://css-tricks.com/old-flexbox-and-new-flexbox/

2) Use containers

When designing your html layout, make sure to use containers. They are the key to using flexboxes properly. Some flex properties are meant for your container/parent element while others are meant for the child elements. For example, align-items and justify-content should be applied to the container while properties such as flex and align-self should be applied to the child elements. Here is a very simple example: http://jsfiddle.net/JQZ34/1

3) Browser support

Flexboxes are still very new and thus browser support can be an issue. They are very powerful but before you commit to using them make sure you understand your target audience. What browser versions are your users working with? If your product is meant for the wider web then maybe flexboxes are not the right fit for you. See the ‘Browser Compatibility” section for more details: https://developer.mozilla.org/en-US/docs/Web/Guide/CSS/Flexible_boxes#Browser_compatibility

4) Performance

Flexbox performance is improving as time passes and browser support increases but they can be slow so context matters. You must ask yourself whether the slight performance loss is worth it for your particular situation. If the answer is no, then other options such as CSS table layouts might be a better fit. Here are some links if you are interested in the performance aspect of flexboxes:

http://updates.html5rocks.com/2013/10/Flexbox-layout-isn-t-slow
http://benfrain.com/css-performance-test-flexbox-v-css-table-fight/
http://css-tricks.com/does-flexbox-have-a-performance-problem/

Posted in Web Development | Tagged , , | Leave a comment

Resurrection!

My blog has been inactive for quite a while. Well, as it happens I noticed that it had been compromised. Unsurprisingly, the hackers decided to use my blog to display various items they wanted to promote. Unfortunately, I was preoccupied with other things in life and did not notice soon enough. I brought down the site the minute this mishap came to my attention. I do apologize to anyone that was adversely impacted by this.

Now that I’ve cleaned up my site and removed the malicious elements, I hope to get back into regular blogging mode. It feels good to have my blog up and running again and I look forward to churning out some new posts in the near future.

Posted in Uncategorized | 1 Comment

Setting up a Linux environment, Why so Arduous?

So lately I’ve been trying to setup a linux environment on a shared hosting web server. Let me just begin by saying that it has not been easy, in fact it has been quite frustrating. The following are some of the technologies that I’ve tried to install:

  • Python 2.7
  • Django
  • Trac
  • SVN
  • Hg
  • setuptools
  • libtool
  • autoconf
  • apr
  • apr-util
  • m4

The documentation has been lacking in every sense of the word. Most Linux technologies are compiled and built with the usual configure, make and make install show, but dealing with all the dependencies that these technologies employ is the biggest headache. For example when trying to setup svn many of the required dependencies were missing and I had to hunt them down and get them working before I could even start installing the tool itself. Dependencies themselves are fine. However, not notifying the user about them in advance is not. The way I found out about the dependencies for svn was via the errors I received when I tried to build it.

It would be much easier to install a module or package if all the required dependencies came packaged with the software. I mean, problems like these reduce your potential user base. I’m sure for a Linux guru these issues would be trivial. But it takes years of work to reach such a position of knowledge. The common man is going to give up after a few hours.

I like what python has done with setuptools. This tool literally does all the work for you. It downloads, compiles, builds and installs any python module that you request. It doesn’t get simpler then that. Once the module is installed then the user can get down to what they really want to do.

Maybe it’s time for Linux packages to move to a more user friendly installation experience. Ideally, a user wants to be presented with a list of options which he can then choose one. If he is unable to choose then the default option is selected. Basically, I’m thinking of a scenario where the Microsoft step by step GUI installation process is replicated in a command line environment. This would involve extra work but in the end usability matters, even when dealing with development tools. The extra effort must be applied to stay competitive, otherwise users will certainly migrate to other products.

Posted in Linux, Open Source | Tagged , , , , , , , | 12 Comments

v1.0 Release Complete

Well this is it guys. My last release for the open source course is finished! It has come at the right time too as this is my 100th blog post. So for this release I ended up doing quite a bit more than I originally planned to. It all kind of became hectic in the end because ted suddenly reviewed my OnScroll patch yesterday, which meant that I immediately had to fix the changes he outlined.

First of all I tackled the files touched patch. Jorendorff has mentioned a few times that it is important to show information in an unobtrusive manner. Now, this wasn’t being accomplished earlier and so I decided to put expand/collapse functionality for the files touched.

Next I tackled a new bug where I only wanted  >5 merges to get expand/collapse functionality. Anything less than that should be left as it is. I’ve got that working now.

As I mentioned earlier, I also tackled the OnScroll patch. Over the course of these last eight months this has been the patch that I’ve worked on the most. In this release I’ve done a major rehaul. The client-side code looks very different now as I had to change it to work with the new hg_templates/pushlog.tmpl. Also, I’ve used a lot of jquery to make the code shorter and simpler. Furthermore, I’ve improved bug functionality even further and got the “To Local” link to work with the new entries that load on scroll. These were the goals that I had originally set out to meet and I’ve accomplished them

Now, as I said earlier ted, djc and Pike all gave me some feedback yesterday regarding my OnScroll patch. Thus, I had to make some last minute changes to my release. First of all I tackled a problem with the server side code that handles merges. Previously I was using a long string delimited by a random separator. Now, I’ve replaced that with a nice and simple dictionary data structure. This took a bit of time as I had to rehaul major parts of my client-side implementation so that it could properly read the data that was now being stored in dictionaries.

Furthermore, I moved all my JavaScript code to an external file since the amount of code was just getting to be too much to handle in script tags. I also changed how I deal with the parity counter and how I retrieve the maximum number of entries in the database. Lastly, I made a change in regards to pushlog queries. Beforehand, my code was causing more entries to load even when a user executed a query. That should not have been happening and now I’ve managed to fix that problem.

The following are the important links from this release:

Finally, the release is done! All the patches have been posted to their respective bug pages. Please view the project page for more information.

Posted in DPS911, Mercurial Project, Open Source | Tagged , , , | Leave a comment

v1.0 Release – Various Changes to Improve the OnScroll Patch

Yesterday, ted reviewed my onScroll patch and and he outlined some changes that I should look to make. Lets dive in right away and have a look at what I have changed according to his recommendations:

Move javascript to an external file

ted recommended that I move all my JavaScript to an external file since the amount of lines was just too much. Having all that code in the script tags is not ideal. Thus, I’ve now moved all my JavaScript code to an external file leaving only the following in hg_templates/pushlog.tmpl:

var start = 0;
if(window.top.location.search.substring(1) == "")
  fillPage({max});
 
$(window).scroll(function() {
  if(start > 0) {
    if($(window).scrollTop() == $(document).height() - $(window).height()) {
      loadEntries(1);
    }
  }
});

Change the parity counter

I’ve also changed how I deal with the parity counter which is responsible for setting up the class of each row. In the pushlog the class value of the rows alternates between ‘parity0′ and ‘parity1′. Beforehand I was using an if/else block to replicate this functionality for the new entries loaded on scroll. However, this has now been changed to the following which is a better implementation:

var row = $(document.createElement('tr'));
row.addClass('parity' + counter % 2);
counter++;

So basically if the counter is an odd value counter % 2 will return 1, otherwise it will return 0. For each row I increment the counter by 1, which means that it alternates between an even and odd value.

Change how max entries is retrieved

ted was wondering why I was doing the following to retrieve the total amount of entries in the database:

<div id="page_header" class="page_header" maxentries="#max#">
start = ($("#page_header").attr("maxentries")) - 10;

I know that the above implementation was a very round about way of doing this. In my earlier implementations I had tried doing what he recommended, which is to just declare var start = {max} but for some odd reason that didn’t work for me initially. I tried this again today and now somehow it works. This had me very confused and frustrated. I thought a bit harder about this oddity and realized that what I had probably tried beforehand was this: var start =’{max}’ which sets start to an empty string. Anyways, I’ve rectified the situation and now I do the following to set the value of the ‘start’ variable:

fillPage({max});
function fillPage(max) {
  start = max - 10;
  var vHeight = 0;
 
  if (document.documentElement)
    vHeight = document.documentElement.clientHeight;
  else
    vHeight = document.body.clientHeight;
 
  if (document.body.offsetHeight &lt; vHeight) {
    for(var count = 0, offSetHeight = document.body.offsetHeight; offSetHeight &lt; vHeight; count++)
      offSetHeight += 180;
 
    if(count &gt; 0)
      loadEntries(count);
  }
}

OnScroll should not work with any pushlog queries

ted brought up a very good point in one of his comments on the bug page. He asked what would happen if a user ran a query? Would the pushlog try to load more entries even though the user just wants to see his query data? Well, for my previous implementation the answer was yes. The function fillPage() would get called when the query is loaded and it would try to load more entries. I needed to fix this situation and I did so by adding the following code:

if(window.top.location.search.substring(1) == "")
    fillPage({max});

window.top.location.search.substring(1) will return an empty string if no query has been executed. Thus I can just perform a simple if statement to check whether the pushlog is being loaded normally or is a query being executed. If it happens to be the former then fillPage() is called, otherwise it isn’t.

Posted in DPS911, Mercurial Project, Open Source | Tagged , | 1 Comment

v1.0 Release – Changing the Data Structure that Handles Merges for the OnScroll Patch

Today ted unexpectedly reviewed my onScroll patch. Now, suddenly I have a ton of changes that I have to make and my release is due tomorrow (technically today since the clock just hit midnight).

One of the problems that ted focused on was how I passed merge changesets from the server-side to the client-side. I was using a weird and ugly string which is delimited by a random separator. I’ve known all along that this was a very ugly solution. When I had come up with this implementation I didn’t know python very well and thus did not use the dictionary data structure, which is an integral part of the language. I don’t know why I haven’t changed this implementation until now. I guess I decided to give other things more priority because although the solution was ugly, it worked.

Nonetheless, I have made the appropriate changes and the following is what my server-side solution looks like now:

1
2
3
4
5
def getMergeData(ctx, mergeData):
    for cs in ctx.parents():
        mergeData.append({'node': ctx.hex(), 'user': clean(person(cs.user())), 'desc': clean(cs.description())})
        if len(cs.parents()) > 1:
            getMergeData(cs, mergeData)

The client side implementation:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
function loadEntries(iterations) {
  for(var t = 0; t < iterations; t++) {
    var end = start;
 
    $('.loader').html('<img src="static/ajax-loader.gif" align="right" />');
    start -= 20;
    $.get('/json-pushes?startID=' + start + '&endID=' + end, function(responseText) {
      var pushData = JSON.parse(responseText), counter = 0;
      for(var i = end; i > start; i--) {
        var row = $(document.createElement('tr'));
        row.addClass('parity' + counter % 2);
        counter++;
 
        // user column
        $('<td>' + pushData[i].user + '<br /><span class="date">' + pushData[i].formattedDate + '</span></td>')
          .appendTo(row)
          .width(184);  
        // rev column
        $('<td>' + createRevTd(pushData[i].individualChangeset) + '</td>').appendTo(row);
        // author and description column
        $('<td>' + '<strong>' + pushData[i].author + ' &mdash ' + createBuglink(pushData[i].desc) + '</strong>' + '</td>').appendTo(row);
        $('.loader').html("");
        $('.titlePush').append(row);
 
        //Check whether it is a merge changeset or not
        if(pushData[i].mergeData != []) {
          for(var j = 0; j < pushData[i].mergeData.length; j++) {
            if(pushData[i].mergeData[j] != "") {
              var node = pushData[i].mergeData[j]['node'];
              var user = pushData[i].mergeData[j]['user'];
              var desc = pushData[i].mergeData[j]['desc'];
 
              createMergeRows(node, user, desc, row);
 
              // nested lists
              if(pushData[i].mergeData[j] instanceof Array) {
                for(var k = 0; k < pushData[i].mergeData[j].length; k++) {
                  node = pushData[i].mergeData[j][k]['node'];
                  user = pushData[i].mergeData[j][k]['user'];
                  desc = pushData[i].mergeData[j][k]['desc'];
 
                  createMergeRows(node, user, desc, row);
                }
              }
            }
          }
        }
      }    
    });
  }
}

So, as you can see now I’m using a list that holds a bunch of dictionaries. Each dictionary has 3 keys: node, user and desc. I pass the the list, mergeData over the to the client side via JSON. Then on the client side I go through the list and retrieve the values of each key (lines 29-32)

However, you must have noticed that the client-side implementation has a nested for loop (line 38). Ted was wondering why it takes a maze of nested loops to display merges (although now it is down to only one nested loop). The reason I have to do it this way is because sometimes mergeData can contain nested lists. Especially when dealing with large merges that tracemonkey usually has.

So there you have it. I’ve replaced the long and ugly string that previously held merge information with a nice and beautiful dictionary.

Posted in DPS911, Mercurial Project, Open Source | Tagged , , | Leave a comment

v1.0 Release – Further Improving Bug Functionality for the OnScroll Patch

In my earlier blog post I outlined that I wanted to further improve bug functionality for the OnScroll patch. Bugs that were only 5 digits long were not being converted into proper links. For example bug 12345 or b#12345 or 12345. I was only covering bugs that were 6 digits in length. The following is my old bug regexps:

1
2
3
re = new RegExp("[bB][uU][gG]\s\d{6}");
re2 = new RegExp("b=\d{6}");
re3 = new RegExp("\d{6}\W");

Now have a look at my new bug regexes:

1
2
3
re = new RegExp('[bB][uU][gG]sd{5,}');
re2 = new RegExp('b=d{5,}');
re3 = new RegExp('d{5,}W');

As you can see the regexp implementation hasn’t changed very much. The only change was that ’6′ became ’5,’ and now the 5 digit bugs are converted into bug links properly. Basically ’5,’ specifies that anything >=5 digits should be converted into bug links. Furthermore, 7 digit bugs will start to appear eventually (if they haven’t already, not sure). This implementation automatically takes care of that situation.

Another thing you will notice in my new implementation is that the regexps no longer use double quotes. Instead, I’m now using single quotes which means that I don’t need to use the double backward slash as an escape character.

Posted in DPS911, Mercurial Project, Open Source | Tagged , , | 2 Comments

v1.0 Release – Getting the Localize Dates Link Working with the New Entries Loaded On Scroll

One of my goals for this release was getting the localize dates link working with the new entries loaded on scroll. This wasn’t happening beforehand. Only the entries that were displayed with the initial page load worked with the localize dates link.

I examined why this was happening by looking at how the dates are localized. The following is the code for this functionality:

14
15
16
17
18
19
20
21
// add click handler to the localize dates link
$('#localize').show().click(function () {
   $(this).hide();
   $('.date').each(function (i) {
     $(this).text(new Date($(this).text()).toLocaleString());
   });
   return false;
});

As you can see above when the localize date link is clicked the function goes through each element that has a class called ‘date’ and transforms the text within the element to a local date string via toLocalString(). Now my problem was that none of the new entries loaded on scroll have any elements that contain a class called ‘date’. Thus, none of the dates for the new entries got translated into their local versions.

In order to rectify this situation I decided to make the following change within loadEntries():

77
78
79
80
// user column
$('<td>' + pushData[i].user + '<br /><span class="date">' + pushData[i].formattedDate + '</span></td>')
  .appendTo(row)
  .width(184);

So basically now I put the date text within a a span. This span has a class called ‘date’ thus when the To Local link is clicked it will go through all the elements that have the class date, which now includes the new entries. There we have it, problem fixed!

Posted in DPS911, Mercurial Project, Open Source | Tagged , , | 2 Comments

v1.0 Release – Fixing the OnScroll Patch to Work with the New Version of hg_templates

In my earlier blog post I outlined my plan to update my OnScroll patch to work with the new version of hg_templates, which was updated recently. Now, it has access to the jquery library, which can be used to do more fun stuff. I was using jquery beforehand too but now I’ve decided to take it a step further and take advantage of jquery even more. I believe by doing this I can significantly reduce the number of lines of code and still retain the same functionality.

The Old Code

For comparisons purposes I’ve posted the old code below:

57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
function loadEntries(iterations) 
{
  for(var t = 0; t < iterations; t++) {
    var loader = document.getElementById("loader");
    var end = start;
    var req = new XMLHttpRequest();
 
    loader.innerHTML = '<img src="{url}static/ajax-loader.gif" align="right" />';
    start = start - 20;
    req.open('GET', '/json-pushes?startID=' + start + '&endID=' + end, true);
    req.onreadystatechange = function() {
      if(req.readyState == 4) {
        if(req.status != 404) {
          var pushData = JSON.parse(req.responseText), counter = 0;
          //var counter = 0;
          for(var i = end; i > start; i--) {
            var row = document.createElement("tr");
            if(counter == 0) {
              row.className = "parity0"; 
              counter = 1;
            } else {
                row.className = "parity1";
                counter = 0;
            }  
            var userCol = document.createElement("td");
            var revCol = createRevTd(pushData[i].individualChangeset);
            var bugLink = createBuglink(pushData[i].desc);
            var authDescCol = document.createElement("td");
 
            userCol.width = "184px";
            userCol.innerHTML += pushData[i].user + '<br />' + pushData[i].formattedDate;
            authDescCol.innerHTML += '<strong>' + pushData[i].author + ' &mdash ' + bugLink + '</strong>';
            row.appendChild(userCol);
            row.appendChild(revCol);
            row.appendChild(authDescCol);
            loader.innerHTML = "";
            document.getElementById("titlePush").appendChild(row);
 
            //Check whether it is a merge changeset or not
            if(pushData[i].MergeData != []) {
              for(var j = 0; j < pushData[i].mergeData.length; j++) {
                if(pushData[i].mergeData[j] != "") {
                  var mergeStr = pushData[i].mergeData[j];
                  for(var k = 0; k < pushData[i].mergeData[j].length; k++) {
                    if(pushData[i].mergeData[j] instanceof Array) {
                      var actualMergeStr = mergeStr[k].split('|-|');
                      var mergeRev = actualMergeStr[0];
                      var mergeUser = actualMergeStr[1];
                      var mergeDesc = actualMergeStr[2];
                    }
                    else {
                      var actualMergeStr = mergeStr.split('|-|');
                      var mergeRev = actualMergeStr[0];
                      var mergeUser = actualMergeStr[1];
                      var mergeDesc = actualMergeStr[2];
                      k = pushData[i].mergeData[j].length;
                    }
                    if(mergeDesc != pushData[i].desc) {
                      var mergeRow = document.createElement("tr");
                      var mergeUserCol = document.createElement("td");
                      var mergeRevCol = createRevTd(mergeRev);
                      var merge_bugLink = createBuglink(mergeDesc);
                      var mergeAuthDescCol = document.createElement("td");
 
                      mergeRow.className = row.className;
                      mergeUserCol.width = "184px";
                      mergeAuthDescCol.innerHTML += '<strong>' + mergeUser + ' &mdash ' + merge_bugLink + '</strong>';
                      mergeRow.appendChild(mergeUserCol);
                      mergeRow.appendChild(mergeRevCol);
                      mergeRow.appendChild(mergeAuthDescCol);
                      document.getElementById("titlePush").appendChild(mergeRow);
                    }
                  }
                }
              }
            }
          }    
        }  
      }
    }
    req.send(null);
  }
}
 
function createRevTd(rev) 
{
  var td = document.createElement("td");
  td.innerHTML +=
    '<a href="/rev/' +
    rev.substring(0, 12) + 
    '">' + 
    rev.substring(0, 12) +
    '</a>'; 
 
  return td;
}

The New Code

59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
function loadEntries(iterations) {
  for(var t = 0; t < iterations; t++) {
    var end = start;
 
    $('.loader').html('<img src="{url}static/ajax-loader.gif" align="right" />');
    start -= 20;
    $.get('/json-pushes?startID=' + start + '&endID=' + end, function(responseText) {
      var pushData = JSON.parse(responseText), counter = 0;
      for(var i = end; i > start; i--) {
        var row = $(document.createElement('tr'));
        if(counter == 0) {
          row.addClass('parity0');
          counter = 1;
        } 
        else {
          row.addClass('parity1');
          counter = 0;
        }
        // user column
        $('<td>' + pushData[i].user + '<br /><span class="date">' + pushData[i].formattedDate + '</span></td>')
          .appendTo(row)
          .width(184);  
        // rev column
        $('<td>' + createRevTd(pushData[i].individualChangeset) + '</td>').appendTo(row);
        // author and description column
        $('<td>' + '<strong>' + pushData[i].author + ' &mdash ' + createBuglink(pushData[i].desc) + '</strong>' + '</td>').appendTo(row);
        $('.loader').html("");
        $('.titlePush').append(row);
 
        //Check whether it is a merge changeset or not
        if(pushData[i].MergeData != []) {
          for(var j = 0; j < pushData[i].mergeData.length; j++) {
            if(pushData[i].mergeData[j] != "") {
              var mergeStr = pushData[i].mergeData[j];
              for(var k = 0; k < pushData[i].mergeData[j].length; k++) {
                if(pushData[i].mergeData[j] instanceof Array) {
                  var actualMergeStr = mergeStr[k].split('|-|');
                  var mergeRev = actualMergeStr[0];
                  var mergeUser = actualMergeStr[1];
                  var mergeDesc = actualMergeStr[2];
                }
                else {
                  var actualMergeStr = mergeStr.split('|-|');
                  var mergeRev = actualMergeStr[0];
                  var mergeUser = actualMergeStr[1];
                  var mergeDesc = actualMergeStr[2];
                  k = pushData[i].mergeData[j].length;
                }
                if(mergeDesc != pushData[i].desc) {
                  var mergeRow = $(document.createElement('tr')).addClass(row.attr('class'));
                  $(document.createElement('td')).appendTo(mergeRow).width(184);
                  $('<td>' + createRevTd(mergeRev) + '</td>').appendTo(mergeRow);
                  $('<td>' + '<strong>' + mergeUser + ' &mdash ' + createBuglink(mergeDesc) + '</strong>' + '</td>').appendTo(mergeRow);
                  $('.titlePush').append(mergeRow);
                }
              }
            }
          }
        }
      }    
    });
  }
}
 
function createRevTd(rev) {
  return '<a href="/rev/' +
    rev.substring(0, 12) + 
    '">' + 
    rev.substring(0, 12) +
    '</a>';
}

As you can see above is the new code. Lots of changes to look at it. First of all the JavaScript code has been shifted within $document.ready(). The major changes have occurred in loadEntries(). The jquery library makes it extremely simple to retrieve an object by its class or id name. Instead of having to use document.getElementbyId() I can use $(‘<class_name>’). I’ve used this way extensively throughout my new implementation. Furthermore, I don’t need to use document.createElement() anymore either. I can just use the  $(‘<html>’) syntax to create an element (e.g. line 84). Another great little change is how jquery creates an xmlHttpRequest(). The syntax is so much simpler. Only a simple $.get(url, function(responseText) {} ) is needed (line 65). Another little quirk that makes jquery awesome can be seen on lines 78-80. I can create the new element, append it to another element and change its attributes all in one go.

One important thing to note, however, is that some things didn’t change much at all. Such as the string manipulation functionality found at lines 88-107. All in all the end result of all these changes is that I have manged to reduce the total number of lines by 19. On top of that I have also added 2 extra pieces of functionality (I’ll be discussing them in future posts) without having to add any additional lines of code. A very good result, in my opinion.

Posted in DPS911, Mercurial Project, Open Source | Tagged , , | 6 Comments