Schedule Url Chrome Extension

What:
Write a simple chrome extension. Get some simple stuff done as well as learn new things.

Idea:
Wanted to write a small chrome extension to schedule my browser to go to a particular url at a particular time. I also wanted it to sometimes cycle through a list of urls.

Resources:
The Chrome extension framework is pretty simple to use and very straight forward. The getting started tutorial:
https://developer.chrome.com/extensions/getstarted
pretty much gets you started.
From there on its only a matter of checking out their API to know what functionality is available to you.
https://developer.chrome.com/extensions/api_index

Finished product:
https://chrome.google.com/webstore/detail/schedule-urls/ckncmlnmaojinlkgmbbneempofkkogom/reviews?utm_source=chrome-ntp-icon

code:
https://code.google.com/p/schedule-urls-chrome-extension/source/browse/

The whole story:

Today at work I while trying to setup a dashboard I needed a chrome extension which will schedule some urls in a tab. Strangely I did not find one for it in the store. It seemed like a simple few lines of code but I know generalizing the problem and getting a fully working extension will be some work. Yet I feel this will be some learning experience. Hence lets get started.

The first thing to do is ofcourse to search on google "how to write a chrome extension" :)
This points to Google's official "Getting Started" page for chrome extension. This is a simple guide with a sample extension. I just followed it and got it working.

It seems chrome extensions are fairly easy to install from a directory. Just put all needed files in a directory and point to it from Chrome->Settings->Extensions->Load Unpacked Extension.

Okay Now to come back to our problem. I wanted some extension which will let me add new rules to schedule urls at particular time. I know loading urls would be fairly easy-Just a matter of calling some load() function on a tab object which chrome API must be providing. We will take a look at their API in some time.

The major portion of my work will be taken up in getting a nice UI and deciding how the scheduling rules will be parsed and implemented.

Lets step back a bit and list down our requirements :D
My use case is something like this: I need to cycle between a list of urls(mostly different graphs) but at specific times I need to break the cycle show a different URL. So lets try to make it a bit generic so that we have an extension which might be useful to some people other than me :)
Functionality:
  1. Schedule things. Schedules can be: "Daily At a particular time", "Daily Between times", "Always", "Every n Minutes"
  2. Can specify multiple urls for a rule. This will allow me to cycle through the various graphs.
  3. Have a priority: Lets assume that the latest rule will take priority. This will help us to break the cycle once in a while.
Enough talk. Lets write some code. So as not to get caught in too much design detail lets get an initial iteration implemented so that we feel we have accomplished something before we get bored. Lets now worry about the UI now. We will assume that we read the rules somehow from the UI. Lets decide on a data-structure to store the rules and parse and schedule something. We can store the rules in an object like:
Rule {
 rule_type, // always,at,between,every.
 time1, // Store the time(in case of at, between) 
 time2, // Store the end time in case of between.
 interval,
 last_time,
 urls, // Array of urls to load.
 last_loaded_url_index, // The index of last loaded url for cycling. Should initialize to 0.
}
To read and implement rules we can have an minimum interval at which we will check the rules and decide which to apply. Lets take 1 sec for starters. So everything will have a granularity of 1 sec. Every one second we will have a timer go and will evaluate the rules and decide if we need to change the url. If yes navigate to that url. For now we will just output that url. Lets get going.

Just to warm up and see some output I put this hello world code in a html file and load it in Chrome.

<html>
<script>
console.log("hello");
</script>
</html>

I see "hello" printed in chrome console(RightClick->Inspect Element->Console). Sweet. Now some actual code. After 15 minutes of coding and fiddling and googling I came up with this which evaluates the "AT" code and returns the status of the rule every 1 second. It prints the output in to the console.

<html>
<script>
console.log("hello");
rule = {rule_type: "at",
        date1: new Date(2001, 0, 1, 0, 28),
        urls: ["http://www.google.com"],
        last_loaded_url_index: 0}

function evalRule(rule) {
  now = new Date();
  switch(rule.rule_type) {
    case "at":
      if (now.getHours() == rule.date1.getHours() &&
          now.getMinutes() == rule.date1.getMinutes()) {
        return true;
        break;
      }
  }
  return false;
}
setInterval(function() {
  console.log(evalRule(rule));
}, 1000);
  
</script>
</html>


 Next I will implement the rest of the rule types and then try to implement the cycle part.



-----------------------------------------------------------------------------------------------------------------------------------

I didn't get any time/interest to work on this last couple of days. But again in the weekend when I felt I must do something useful rather than slacking around I opened up the code and took a look at the last blog post. So ya. We need to implement the rest of the rule types. There is nothing complicated here. I put down the following code. I still haven't tested it so there might be some bugs there.
 function evalRule(rule) {  
  now = new Date();  
  switch(rule.rule_type) {  
   case "at":  
    if (now.getHours() == rule.date1.getHours() &&  
      now.getMinutes() == rule.date1.getMinutes()) {  
     break;  
    }  
   case "between":  
    if (now.getHours() >= rule.date1.getHours() &&  
      now.getHours() <= rule.date2.getHours()) {  
     if (now.getMinutes() >= rule.date1.getMinutes() &&  
       now.getMinutes() <= rule.date2.getMinutes()) {  
      return true;  
     }  
    }  
   case "always":  
    return true;  
   case "every":  
    if (rule.seconds_elapsed > rule.interval) {  
     rule.seconds_elapsed = 0;  
     return true;  
    } else {  
     rule.seconds_elapsed++;  
    }  
  }  
  return false;  
 }  
So now we have a function which will evaluate a rule depending on the current time and return true if the rule should be applied now. Now for the rule that applied we need a URL. Lets define a function which will give us a URL for a rule. It should cycle the URLs as well.

 function getUrl(rule) {  
  if (rule.urls.length <= 0) {  
   return null;  
  }  
  if (!rule.hasOwnAttribute(last_url_index)) {  
   rule.last_url_index = 0;  
  }  
  url_index = rule.last_url_index + 1;  
  if (url_index >= rule.urls.length) {  
   url_index = 0;  
  }  
  rule.last_url_index = url_index;  
  return rule.urls[url_index];  
 }  
Now we need something which will check the rules every second and change the page url if it need to be. We need to do some global book keeping as well so that we do not keep changing URLs every second.
After a hour of coding and testing.( I made some silly mistakes) I had some code which worked as I expected. Also I realized that I need to add a interval to all rules. SO now my requirements are modified as:

  1. Schedule things. Schedules can be: "Daily At a particular time", "Daily Between times", "Always".
  2. Can specify multiple urls for a rule and the program will cycle between urls after the specified interval. This will allow me to cycle through the various graphs.
  3. Have a priority: Lets assume that the latest rule will take priority. This will help us to break the cycle once in a while.
So here's the code. It can be opened in chrome and will output the log in the console.
 <html>  
 <script>  
 console.log("hello");  
 rule1 = {rule_type: "at",  
     date1: new Date(2001, 0, 1, 0, 28),  
     urls: ["http://www.google.com"]}  
 rule2 = {rule_type: "always",  
     interval: 2,  
     urls: ["http://www.google1.com", "http://www.google2.com", "http://www.google3.com"]}  
 rule3 = {rule_type: "between",  
     date1: new Date(2001, 0, 1, 1, 30),  
     date2: new Date(2001, 0, 1, 1, 52),  
     urls: ["http://www.t1.com", "http://www.t2.com"]}  
 last_url = ""  
 rules = [rule1,rule2,rule3]  
 function evalRule(rule) {  
  now = new Date();  
  switch(rule.rule_type) {  
   case "at":  
    if (now.getHours() == rule.date1.getHours() &&  
      now.getMinutes() == rule.date1.getMinutes()) {  
     return true;  
    }  
    break;  
   case "between":  
    if (now.getHours() >= rule.date1.getHours() &&  
      now.getHours() <= rule.date2.getHours()) {  
     if (now.getMinutes() >= rule.date1.getMinutes() &&  
       now.getMinutes() <= rule.date2.getMinutes()) {  
      return true;  
     }  
    }  
    break;  
   case "always":  
    return true;  
  }  
  return false;  
 }  
 function getUrl(rule) {  
  if (rule.urls.length <= 0) {  
   return null;  
  }  
  if (!rule.hasOwnProperty("url_index")) {  
   rule.url_index = 0;  
  }  
  if (!rule.hasOwnProperty("seconds_elapsed")) {  
   rule.seconds_elapsed = 0;  
  }  
  if (!rule.hasOwnProperty("interval")) {  
   rule.interval = 1;  
  }  
  rule.seconds_elapsed++;  
  if (rule.seconds_elapsed >= rule.interval) {  
   rule.seconds_elapsed = 0;  
   cycleUrl(rule)  
  }  
  return rule.urls[rule.url_index];  
 }  
 function cycleUrl(rule) {  
  rule.url_index++;  
  if (rule.url_index >= rule.urls.length) {  
   rule.url_index = 0;  
  }  
 }  
 function setUrl(rule) {  
  url = getUrl(rule);  
  if (url != last_url) {  
   console.log(url);  
   last_url = url;  
  }  
 }  
 function evalRules(rules) {  
  success_rule_num = -1;  
  for (i=0; i < rules.length; i++) {  
   if (evalRule(rules[i])) {  
    success_rule_num = i;  
   }  
  }  
  if (success_rule_num >= 0) {  
   setUrl(rules[success_rule_num]);  
  }  
 }  
 setInterval(function() {  
  evalRules(rules);  
 }, 1000);  
 </script>  
 </html>  

This really sucks. I somehow finished the extensions. But didn't document the progress. I wont be explaining much also. Writing the extension part was not very difficult. Lots of time I had to look up the syntax or stuff like that. Also because I am not that good at Javascript. But the chrome developer tools are super awesome and really helpful to play around with Js.

 Another notable thing here is that the extension consists of a background.js (Js which runs all the time) and a popup.js(Js which runs on the popup page which drops down when you click an extension). To communicate between these two pages I needed to use ports. Also chrome extensions do not allow inline scripts. So you need to put all js in the popup.js including click handlers. Which is a good thing but makes a bit difficult to just test out stuff.

Anyways I uploaded the code to chrome web store here. They charge a one time fee of $5. Which I paid although I know I will not make money out of it. :)

Also I uploaded the source to google code-search here.

Although this might be useful for somebody it was not for me actually as I could not use it at work due to some other issue. Still gives the satisfaction of building something. That is what we engineers are here for. :)