Thursday, August 20, 2009

Google Code Page

Check out my google code page at: http://code.google.com/p/breadcrumbsplugin/

In the Downloads tab there is a link to my BreadCrumbs poster, shown at the U of T Department of Computer Science's Undergraduate Poster Session, along with a downloadable screencast showing the basics of BreadCrumbs.

Monday, August 10, 2009

Updated Version and Future Plans

    I'm finally back to work after a weeks vacation, and I've spent the day busily reorganizing/cleaning/documenting my code. I've tried to split the code into more files to make it more comprehendible, along with giving better explanations for each function and pointers to related functions/JavaScript files. Luckily after moving everything around nothing ended up breaking, and it all seems to work exactly as it used to.

    Throughout my vacation I was still working a bit, from home, because there were a few things I wanted to add. First of all, I changed the layout of the buttons on the graph page. It was getting a bit too cluttered for me, so I added tabbed menus - Graph, Filter, and Options. I've moved the Save and Load options into the graph page, instead of being in the context menu of the Idle/Recording button on the browser overlay. I think it looks nicer, functions better, and it wasn't too much of a problem to add, I just found a script for making tabs called JavaScript Tabifier. It has an MIT license and says I can "modify and use in commercial products", so I don't think I'll have any legal problems using it. That's something I have to check out soon though, not specifically for this tabbing script but what kind of licensing information I have to provide with my extension. 

    The last thing I did over my vacation was add another column to the database which stores the coordinates of each node. If you reload the graph for any reason, prior to reloading the database gets updated and saves the coordinates per node. This was a feature that I few people thought would be useful; even I was getting annoyed with losing my bearings, after I move the graph around to exactly how I wanted it. Since I already have this part done I think one of my last features for the summer will be to allow users to label certain areas of the graph. I'm not yet sure how to do this, but now that I have the positions of each node saved it shouldn't be too hard, possibly add another table to the database that stores the labels contents and location then just copy the context into a div tag to attach to the graph. If I can finish this the last thing I'd like to do would be to add a rating feature, has Nelle suggested, to signify the importance of the page. Originally I was going to do it by scaling the size of the corresponding node based on the length of time the page was viewed for, but that’s a very error prone solution. So I'll probably end up adding a 1 to 5 rating for each node for the user to manually enter.

    Finally, the updated version:

    BreadCrumbs v1.5

    Note: The database table has been changed, so if you have an older version you have to navigate to your Firefox profile directory and delete breadCrumbs.sqlite prior to installing this updated version. This website should tell you how to find your profile directory.

    There are also two buttons to add, the View Graph button and Quick Title Edit button. The View Graph button opens the graph page, efficiently the same as right clicking the Idle/Record button and selecting View Graph. It's hotkey is alt+g. The Quick Title Edit button can only be clicked if BreadCrumbs is Recording. It opens the menu to annotate the title of the current website, and it's hotkey is alt+q. To add these buttons to your toolbar select View -> Toolbar -> Customize, then drag and drop the buttons.

Thursday, July 23, 2009

Progress, Problems, and Another Release (soon)

    The problem with thumbnails not always extracting still hasn't been completely solved. I wanted to use a timer, but a timer doesn't work the same as, say, wait or sleep in C programming. I tried making it recurse on itself until the thumbnail that at least 1000 characters, but that exceeded the recurssion depth too easily when websites load extremely slow. Also, I can't simply put it in a loop because Firefox will think my script is frozen while it loops until the web page sort of loads, and then Firefox freezes. The Mozilla Developer Center says here that it isn't a good idea to extract page thumbnails during the onload event, and I would have to use the MozAfterPaintEvent. But, that is only in Firefox 3.5. Right now I just have a default image if the thumbnail could not be extracted properly.
    Yesterday Jon Pipitone was giving me lots of suggestions for improvement. One idea that I really like is to add a filtering function, to remove all pages of a certain website. I don't think this would be too hard to implement, using a simple regular expression comparison while building the graph can easily filter out nodes of the chosen type. 
  The big issue to add is some sort of relevant ordering to the nodes. This is hard to do because I have to work around the limitations of JSViz. However, what I think I can do - for now - make root nodes appear in a sensible manner. Currently they spawn randomly across the screen, which isn't helpful. I can alter it so that root nodes appear in chronological order, either lined up vertically or horizontally. This would only help with at least a few root nodes, but it is better than nothing. For now, I'll try this and see if people think it helps.
    Two recent features I've added: Quick Edit Title button and Live Updates. The Quick Edit Title button is a button on the browser toolbar to instantly open up the window for editing the title, it is basically a shortcut. Instead of getting to a site, thinking of a little note to leave, opening the graph, finding that node, right clicked and entering the new note, you can just click this button and do it fast. I've implemented Live Updates so that you no longer have to reload the graph any time you log a new site, it will update itself in real time. 
    I haven't gotten much feedback from posting my extension a few weeks ago, but then again there were only 10 downloads. Soon I'll be putting it up on my CSLab page, with all the latest features. I think before I do that I want to have some more reliable way to organize nodes (or at least root nodes) along with testing everything even more. And I have to write up another set of release notes - that's never fun though.

    And finally, I found out that JSViz has a limitation for the number of edges come from/to a single node: 18. 18 edges are fine, but adding a 19th edge causes everything to catastrophically fail. The only reason I discovered this was because Ainsley tested her Trac plug-in on a large repository and it failed spectacularly, so I tested this by going to Wikipedia and opening every link on one page in new tabs until it crashed. My theory is that the minimum distance set for the magnetic forces can not be maintained once the 19th node is created, since there is not enough room for all of the nodes to evenly spread out around it. It could also be that the magnetic repulsive forces are so compacted and balanced that the 19th node throws the center node off center, causing it to rocket accross the screen. It has to do with the forces because a root node can have more than 19 edges attached to it. Here are so example files:

    Latest Version of BreadCrumbs

    (Note: I'm providing it early with no release notes just to illustrate this edge/node limit per node, some features still are not completely ready [ Stop Animation still has bugs, along with some deleting issues]. If you have an older version of BreadCrumbs please go to your Firefox Profile directory and delete "breadcrumbs.sqlite" as that old database does not have a thumbnail column.)

    Session file that illustrates the breaking [Copy into a text document and save as .session]

    Session file that shows a root with ober 19 nodes/edges [Copy into a text document and save as .session]

Friday, July 17, 2009

Thumbnails

  I'm revisiting the the idea of having thumbnails - not as each node, since that would cause the graph to be far too large, but to appear upon mousing over a node. Now that I know more about Javascript and Firefox it wasn't very hard to be able to get a thumbnail of each page. However, ever a thumbnail that's 20% the size of the real webpage will be huge when converted to a string using the Canvas method .toDataURL(). I was making some thumbnails that were, again, nearly 10,000 characters. So, there was no way I could reliably insert a string that long into the SQLite database for each node. 
  After talking it over with Steve he suggested trying to use the cache to store the thumbnails in temporarily, and then when the user saves the result would be a directory stucture. The idea is similiar to how Firefox can save webpages, by creating the directory structure of the page and saving all of the images in that directory. I figured this would work, but I didn't like the idea of having to save a folder instead of just one file. 
  I spent at least three hours searching around the documentation about Cache on the Mozilla Development Center but there were no examples of how to use it, and the descriptions of all the Cache related functions were very vague. However, I used this site Mozilla Cross Reference that Blake Winton suggested and found this which was exactly what I needed, to understand how the function can work together. However, I still wasn't sold on the idea of storing in the cache and having to save a directory, not just a simple file. 
  Before trying to manipulate the cache I tried searching for more extensions that deal with thumbnails. I found that WebReview, one of the first extensions I looked at, had been updated. I looked a lot at the source code and it had changed a lot, so I hoped to get some more information about how it stored thumbnails. Unfortunatly, when I installed it it didn't work at all, the graph portion just kept raising exceptions whenever I tried to open it. And the part that I really wanted to know, what it inserted into the database, wouldn't execute either. So, no hope for finding out how WebReview stores thumbnails.
  The next extension I tried was Thumbstrip, and luckily this also has to save a lot of thumbnails. I played around with it for a bit, and saved a session. When I looked at the saved file it turns out that they did it exactly how I initially planned on doing it - the thumbnails were saved in their text form and they were very long. I tested it, going to about 25 sits, then saving it. The resulting save file was over 5mb with nearly 40,000 lines and 6 million characters. But it worked, so I figured I might as well try my initial idea.

  I altered my database and tried inserting a few thumbnails - in text form - into it, and also changed the each node's mouse over div tag to show the thumbnail instead of the title. The result?

  

  It does exactly what I wanted it to. Now I just have to flush out a few bugs, since sometimes the page isn't fully loaded and the thumbnail is incomplete, but that shouldn't be too hard to fix with a timer.

New Approach

I've been trying to change the way I program, and this is the first real attempt I've made.  It's a simple function - deleting an edge. However, instead of just diving in and coding and running and see what works and what errors I get, I sat down and wrote out on paper what I'd have to do. I took into account as many possible boundary cases as I could think of and sketched exactly what I had to do. Here's the final version of what I wrote out:

deleteEdge(edge, edgeContainer)  

    1) Remove edge from edgeContainer (SVG object) - it can't be seen now.
    2) Extract row edge.idx from the database, getting its Source, Destination, and Title. [destination, source, and title are column names of my database]
    3) Attempt to extract similarEntry from the database, where destination = Destination (from step 2) and source != Source (from step 2). This is checking for if there is another edge that leads to Destination.
  
    if (similarEntry is not NULL) {
        // Then the Destination of this edge to be deleted has at least one other edge leading to it, so after deletion it will NOT become a root node.
        if (similarEntry's row > edge.idx [it's row number]) {
            // This means that the edge to be deleted is the first entry in the database where Destination (from step 2) is in the destination column. This is important because the user altered title for each website is stored in that websites first occurance in the database. So, we have to copy the title from this entry to the next reference of Destination - similarEntry (since we know it comes after it since its row number is greater).
            4) Update row similarEntry to have title = Title (from step 2).
        }
        5) Delete row edge.idx from the database.
    } else { 
        // There is NO other entry in the database where destination = Destination and source != Source, so the Destination of this edge we're deleting will become a root node.
       6) Update row edge.idx so that source='NULL', making it a root. 
    }
   
    7) Reload the graph_page.html, to reset the forces.
    8) Done.

    Once I had this all written up writing the code was simple, and aside from a few typos it worked perfectly on the first run. This is a major improvement over my standard method of writing the first idea that comes in to my head, then running it and fixing bugs over and over until it works.

Friday, July 10, 2009

Features to Come

    So I've gotten the first draft of the release notes up. I hope they'll suffice. It took a lot longer to do than I originally had planned due to testing and documentation. I'm pretty picky on documentation and trying to rename variables/functions to make everything more understandable. 
    The testing process mostly found errors where I had variable names changed. The frustrating bugs came from boundary cases and specific series of events that had to occur which would lead to an error or a site not getting logged. One example is that having multiple tabs open prior to turning logging on causes problems since those sites didn't get logged (no Load event occured to log the url) and thus would create unbounded non-root nodes. In order to solve these I had to add multiple if-else statements to make sure that each property required to successfully log the URL was present. This is extremely hard to do in the case of the user opening pages that get loaded in background tabs, through "open in new tab". The problem arises from the fact that background loading does not trigger any progress or event listeners, so I have no reliable way to log the site. For now I have it set on a timer of 500 milliseconds - it just needs to load the URL so that it does not default to 'about:blank'. However this does not always work. I could possibly change how the "open in new tab" function works, by automatically switching focus to the newly opened tab, but users may not like an extension altering default Firefox functionality. I at least have a backup safeguard so that the extension dosen't break. Whenever a tab is selected if, if the URL has not already been logged it gets entered into the database, but as a root node. This does not accurately re-create the user's browsing session, but it keeps things stable.
    Lately I've been reading some textbooks in my free time, the most recent one being "Artifical Intelligence and Software Engineering - Understanding the Promise of the Future". After reading the first couple chapters - a general overview of Software Engineering ideas - I've realized that I tend to follow a Run-Understand-Debug-Edit style of development. I find it is easier to keep a mental model of the program when I do incremental development of this sort. The result is that the program generally works (with the exception of a few unconsidered boundry cases) but is also messy. For example, the main procedure for logging websites currently has many bits and pieces and messy subroutines, since over time I have slowly widen the scope of what it can do. It works, but it's messy. I plan on taking a couple days to flush it out - to read over all the pieces and try to organize it to flow logically. My hope is that this will increase efficiency, reduce line-count, and prevent future bugs.
    I've finished with the basics, so now I have a few options on what to tackle next. Not all options are needed, and it really depends on what would be the most useful feature to have:
    1) Thumbnails
        I've had some problems early on with attempting to use thumbnails, but now that I have more experience with Javascript and Firefox I may be able to figure out some way to make the nodes thumbnails. 
    2) Relevant ordering of nodes
        Right now the layout of the graph has no significantly, it just uses magnetic and spring forces to spread the nodes out as evenly as possible. I could add a new layout type to list everything in chronological order, or a way to display only one specific website and all sites that came from it or linked to it. The graph could be filtered by site, so that a scientist could only see papers from one specific domain, for example.  
    3) Graph manipulation
        More features for users to edit the graph: New Edge, Delete Edge, Collapse Node, anything. These functions would be designed to present a clearer and more fluid graph for not only personal reference but sharing with others as well. 
    4) Significance of a node
        It was suggested two weeks ago, when I did my demo to the grad students, that I add a feature to show how significant or important a website is. I could do this by logging the length of time that was spent on each site, and then alter the size of the node to relate to how long was spent viewing. This would be more useful for the casual user than for a scientist, because the length of time spent on a website for a scientist could, most likely, correlate to the length of the article or paper being read. However, it is still an interesting idea and provides more information about the browsing session to the user at a single glance.

Thursday, July 9, 2009

Release Notes

DOWNLOAD:

   Download here from rapidshare. It can only be downloaded 10 times so if the link is down please send me and e-mail so I can reupload it.

INSTALL:

    1) I suggest setting up a new profile - just to be safe - as well as keeping an eye on the Firefox Error Console. To set up a new firefox profile see this short document: http://support.mozilla.com/en-US/kb/Managing+Profiles

    2) Drag BreadCrumbs.xpi into your Firefox browser ( >= 3.0)

    3) Click Install

    4) Done!

TO USE:

    On the bottom right hand corner of the Firefox status bar will be a red icon with URL on it, that is the main controlling icon for BreadCrumbs.

    To begin logging websites, simply left click the icon. It will turn green to signify that it is running. Browse away! It can be clicked again to turn off - essentially pausing the logging - and clicking once more will resume where you left off.

    Right clicking the icon will present a context menu.

        Save Session: This will save your current session to a file.

        Load Session: This will allow you to load a saved session file.

        New Session: This will erase any logged websites and start you over with a fresh graph.

        View Session: This will allow you to view the graph.

    Logged Browsing History (the graph)

        Show Session Trail: This shows a list of the links from site to site your session, ordered from earliest (top entry) to most recent (bottom entry). Each entry shows the destination site and the exact date and time. Hovering your mouse over an entry causes it to be highlighted, along with the edge that it describes. The entry can be clicked to bring up a window to enter a new annotation for that link [Please do not include "|||" in the annotation; three pipes].

        Reload: [Self explanatory]

        Pause Animation: When the page is loaded the nodes of the graph will continually spread out so that all are visible. Once they are visible enough for you you can click the Pause Animation button to stop them from spreading out more.  Note: Any clicking on the graph will cause the animation to resume.

The Graph itself:

    Nodes: Each node of the graph is a website. Most nodes can be dragged around the screen, but if is is outlined in red then it is a root node and thus cannot be dragged. Hovering over a node causes a tooltip to appear with the title of the site. Right click on a node to open a context menu.

        Edit Title: This opens a window to allow you to rename the node to anything you want - please, without the character sequence "|||" [three pipes].

        Visit Site: This opens the website in a background tab in your browser.

        Collapse Node: [NOT IMPLEMENTED YET].

        New Edge: [NOT IMPLEMENTED YET].

        Delete Node: This will delete the node and all edges that connect to it. This may result in multiple disconnected graphs, which is fine. [Deleting a node causes an automatic reload to reset the magnetic/spring forces - I am working on a better solution].

        Close: This simply closes the context menu.

    Edges: There are two types of edges, solid or dotted. Solid edges are formed when a link is a link is clicked (or selected to "open in new tab"), so it corresponds to direct links. Dotted edges are any other type of link: a bookmark, clicking "Home", manually entering a URL, opening a new tab (ctrl+t) then entering a URL, etc. If logging is paused during a session then turned back on in the future, the resulting edge will be a dotted edge since there may have been many sites in between.

    Hovering over an edge causes it to be highlighted, along with the corresponding entry in the Session Trail panel (if it is not hidden), and also displays the link annotation. Right clicking on the edge will bring up a context menu.

    Edit Annotation: This will open a small window to enter any annotation you wish for the edge [Again, avoid "|||"]. Clicking on the corresponding entry in the Session Trail will also open the window.

    Delete Edge: This will delete the edge and reload the graph automatically.

    Close: This closes the menu.

KNOWN BUGS/ISSUES:

  • "open in new tab" sometimes causes the resulting webpage to not become logged, or when it is logged it is set as a root node.
  • New Edge and Collapse functions are not implemented.
  • Selecting an improper file in the Load Session option causes it to break.
  • Saving/Loading a very long session is slow.
  • Sometimes the forces between edges and nodes in the graph cause it to spread out widely, the temporary fix for this is the "Pause Animation" button.
  • Not allowing websites to load sometimes results in two copies of the same link appearing in the Session Trail panel.
  • Favicon extraction isn't the best, but it should work for most sites.  
  • Browsing with multiple windows has not been significantly tested.
  • The colours don't match in any sense.

CURIOUS?:

    To view the source code rename BreadCrumbs.xpi to BreadCrumbs.zip, and unzip. If you want to see where everything is being stored go to your Firefox profile directory and look for breadCrumbs.sqlite. I use the Firefox extension SQLite Manager to check the contents of breadCrumbs.sqlite and the edgeLog table to see what's what.

Wednesday, July 8, 2009

Abstraction and Separation of Concern

    Tuesday's lunchtime meeting was about Abstraction and Separation of Concern, with a lot of emphasis on open-collaborative science. I thought the discussion was very compelling, so I'd like to discuss (very informally) some thoughts on the issues.

    Issues for "Open Collaborative Science":

        1) Miss use and Misinterpretation

        Science is generally a messy process. The steps that have been taken to reach a conclusion and deduce an explanation or otherwise may not always be completely rigorous, in the full sense of the word. This is just the way science works. However, the average person does not need to know the stumbles that scientists went through - and they shouldn't. Allowing this information to be visible to the public, all of the steps and tests and experiments that scientists did to reach their conclusion, adds the possibility for people to poke holes in the work based on, as they set it, "bad science". This goes beyond just trying to do "good science" and give collaborative support or constructive criticism. This could come from people just possibly being afraid of wary of the results or even a result of ideological principles. Anyone who does not like a new scientific development because it contradicts their preset ideology that they may have could go to the online notebook and see all the potential flaws, without fully understanding the heuristics and assumptions that scientists work under, the constraints they have to work around.

        To combat this flaw I feel that the system should have multiple levels of openness - you can only dig ass deep as your qualifications will permit. I call this Limitation by Qualification. In order to effectively maintain a level of qualification that each user has the system would have to be maintained by one or more governing authorities from multiple scientific fields. Users would have their scentific qualifications for their field (and other fields) through an application process. The process could be simple: Do they have articles in respected journals? Do they have advanced degree(s) in their claimed area of expertise? Question of this nature would have to be asked. Once this is done, when scientists make entries or updates to their online notebooks data files, results, conditions, etc can all be locked to only be viewable by someone with the proper qualifications. The idea would be to have the abstract and a general idea of what the scientist is doing available to the public, with a finer level of detail of their works available to those who understand it.

        2) Trust: How do I know this won't be used against me, or stolen?

        Ideologies and fear of change are not the only drives for potential misuse of this data. Sometimes there are rivalries between scientists, and theft of work does occur. Limitation by Qualification would not work here - both scientists are equally qualified. To solve this an idea similar to Limitation by Qualification would be need, possibly something along the lines of a Limitation by Intentions. While it is easy for a human to figure out what I mean by Limitation by Intentions, this is not a simple concept to represent in a computer system. A rating system could possibly be used, to "blacklist" unethical scientists by majority opinion. This has the potential for abuse though, as scientists who merely disagree with the majority view could be "kept quiet" through blacklisting. Also, this relies on catching the scientist in the act, and thus does not prevent the initial theft. Another option would be to allow the owners of each online lab book to control who can view the bottom layer of their work. This would prevent theft, but defeats the very purpose of the system.

    If the above issues can be overcome, then the implementation of tools to facilitate open collaborative science need a couple things:

        1) Standardization for Accessibility by all Disciplines

        The main reason (that I see) for persuing the idea of open collaborative science is to have scientists easily share information, to remove all the "red tape" that slows down advancements in technologies and sciences. Scientists should be able to share, but how do we help them find the information they need in an area that has many foreign concepts and details? My idea would be to use an Interfacer. The Interfacer would be, at a basic level, an expert system. It would take the users request along with the data objects of the users current experiment to extract a list of relevant information and data sources for them. It would have to be highly modular, with two (possibly more?) input modules required - one consisting of an expert system for the input, tailored to understand concepts and terminology of the user's area of expertise, and one consisting of an expert system for the output, tailored to understand concepts and terminology of the discipline of the desired information. How these two modules would interact is a difficult question to answer. One problem is that the issue being looked at is not well defined; there is no clear mapping of terms from one discipline into coherent terms of another discipline, for example. The Interfacer would have to be able to inferr based on what information the user thinks they need to what information they actually need. This issue was highlighted in the discussion with the example Steve gave about a scientist interested in plant growth ( I think?) wanted information from climate simulations about the state of the climate at a specific location, 50 years from now. The scientist got the results and then would draw conclusions from them. Steve pointed out that the conclusions drawn this way would not be scientifically sound since the scientist does not fully understand the assumptions that the climate model was built upon. For something of this nature to work clear and concise assumptions would need to be stated for an experiment or observation - like disclaimers for use by others. To have an Interfacer efficiently do its job each piece of data through the whole network of open notebooks would have to be extractable and have relevant information for the extraction process, be it tags or otherwise. This could be maintained by storing semantic information for each data object in the system, to come up with an "intelligent" description.

        2) Collaboration Tools

        I can't say much for this part, but in my view Google Wave is the right idea. Real time alteration by multiple users is exactly the sort of collaboration that people need. To fully utilize this standards would have to be adopted, so that tables of data, multiple file types, and any other experimental data could be easily maintained. Also, Google Wave presents a solution for how each piece of data can be properly tagged on the fly. The Google Wave demo showed a real time spellchecked which analyzes the content of the sentence in real time to fix typos and grammatical errors. This could be applied to the content of the data or the abstract, to come up with a relevant "blurb" describing the piece of information in question (as mentioned in the point #1). However, this turns the issue of referencing into developing an network of semantically linked objects and thus it falls into the niche of a natural language processing problem.

    Now that I've gotten those thoughts out of the way I have to get back to writing release notes for my extension.

Thursday, July 2, 2009

Tuesday's Meeting and Event Listeners

    When I demoed my application during the Tuesday meeting the grad students seemed interested; I was glad for all the feedback and suggestions. I've already finished altering it to as Jon Pipitone and others suggested, to represent link-following as solid lines in the graph and other visits (typing in a URL, clicking Home, a Bookmark, etc) be dotted lines. I thought this was going to be hard but Johnathon's suggestion of using document.referrer allowed me to make this change in less than 20 minutes. One thing I really want to do that was suggested is make the size of the node correlate to the length of time spent on that website, this would be a great way to better display meaningful sites to the user. One of my immediate concerns would be if someone visited one site many times, say 6-7 times in one session, and each time was only a minute or so, the overall node size may be a false representation that does not agree with their view of the "importantness" of the site.

    Having the sidebar of the trail of sites visited was not overly helped, since each site is only listed once it really just states the time of the fist visit. So I instead opted to list the edges, to give a complete overview of the order of links followed, which conveys more relevant information. But I'm finding it very difficult to keep a coherent graph while keeping the number of edges to a minimum and adding as little clutter to the screen as possible. For example, hitting the Back button does not log an edge, so if a user has a large graph, then hits the Back button a few times and then clicks a new link the resulting graph gives no indication that the user backtracked. The only way to tell is to scroll through the sidebar of links followed in order and watch the links of the graph highlight. It works, but visually the graph looks a bit odd when two links directly after one another in the sidebar list are on opposite sides of the graph.

  My most recent accomplishment has been the move from a timer loop to an event listener for loads. It still loads multiple times, even after using the Mozilla suggestions to have the appropriate event only fire on documents loading. I just added a few if statements to prevent some boundary cases from getting through; it works but it looks sort of messy - "if (on && aEvent.originalTarget.nodeName == "#document" && !(curURL== pastURL) && !(curURL == 'about:blank'))" is what I currently have. The last part is for opening a new tab and then typing in a URL. One of the main reasons for wanting to switch to event listeners (aside from being the standard way of doing something like this) is that with a timer browsing with multiple tabs and tab switching caused an incoherent mess of a graph. Now looking back and forth between tabs does not cause an edge to be created in the graph. My next goal is to fix the issue of clicking a link and selecting "open in new tab". The problem is that the tab opens in the background (not in focus) and the load event doesn't seem to register with the listener I have added to the gBrowser object. My hope is that this is an issue Mozilla has dealt with and I just have to dig through their documentation to find the right event to listen for.

    Although I have tabbed browsing sort of working I'm still behind on my schedule - I guess giving my self less than a week to release my extension for live testing for a bit short sighted. I think by next Wednesday I should be ready. I'll stop adding new features and instead focus on making it as understandable as possible. 

Thursday, June 25, 2009

Added Features and getting ready for Beta (Alpha?) Testing

  1) Delete Node
  It is now possible to delete a node in the graph, along with all of its child edges. The main reason for why I was avoiding implementing this was because I still hadn't fixed the problem of multiple root nodes causing the javascript to crash. Once I figured out how to allow for more than one root the repulsive forces caused the disconnected components to push each other all the way to the end of the screen. Eventually I found a solution to the problem. 
  Allowing for the deletion of nodes took some reworking of how I built the graph, particularly extraction from the SQLite database. My method followed the precondition that each index 1 through the number of rows in the database corresponded to an edge, but with nodes being deleting this no longer held. Luckily, this problem didn't take me long to solve. Now I have delete working exceptionally.
   
  2) SVG Edges, HTML Nodes
  I really wasn't fond of the dotted line appearance that the edges had when made by HTML div tags, so I changed them to SVG. However, this took awhile because I was held back by desperately searching for how to use SVG's image objects or add background images to the SVG shapes. I ended up doing what I was initially trying to avoid - editing the JSViz module even more. I had to alter how the view object was created to use both HTML - for the nodes - and SVG - for the edges. Now the edges are one smooth object and viewing the graph is significantly quicker, since I now longer have to add multiple event listeners for each pixel of the edge.
  
  3) Direct Graph
  This took me two whole days - two days to put triangular markers on edges pointing in the direction of the relation. My first idea was to brute force the calculations and add a triangular SVG object on top of the edge, with the tip always pointing at the destination of the edge. This involved using the X,Y co ordinates of both the source node and destination node as my only two references to find the equation for the three points, no matter what the position was. So I busted out my geometry, and was I in for a surprise. I hadn't done geometry in a long time. I couldn't even remember how to get the equation of the perpendicular line to a point on a slope - yes, my method required knowing this. Needless to say, after a lot of scribbling formula's and drawing mock graphs what I ended up with was a dozen seizure inducing, constantly moving, polygons that weren't even remotely near where I hoped they would be. I abandoned that idea by mid-afternoon and set out to do research. It turned out that SVG polylines had marker objects which do exactly what I wanted to do, but they were in no way simple to figure out. I still can't figure out why a style property would be called marker-mid when it does not place a marker in the middle of the line, but instead places one at every odd vertex excluding the start and the end. I mean, marker-start and marker-end do what they sound like they do, so this baffled me. Eventually I discovered that an odd combination of editing marker properties to refY = "5" and refX = "-15", in combination with a path obect with d = "M 10 10 0 5 10 0 z" resulted in the arrow positioned where I want. Why does it work? I'm not really sure, except M 10 10 positions the center at (10, 10), 0 5 draws a line from (10, 10) to (0, 5), then 10 0 draws a line from (0, 5) to (10, 0), and z close the object. What these coordinates are relative to, and what refY = "5" and refX = "-15" are relative to is still a mystery to me. 
  
  4) Renaming 
  Renaming my extension from "url" to "BreadCrumbs" took a lot longer than I thought it would, since I had to edit files that I haven't touched in ages, like the manifest file or install.rdf. I used this time to separate my methods into more Javascript files to group related methods together better. This close inspection of my code allowed me to spot some lines that were unneeded or just generally sloppy. In addition to this I added more comments, gave variables clearer names, and did more work on the CSS.
   
  5) Brainstorming / Implementing a way to easily convey to the user the order of sites visited
  During my demo on Tuesday Jon Pipitone asked about a way to show the order that sites were visited in. Adding the directed edges was meant to address this, but I realize that if one node has multiple edges out of it the direction of the arrow conveys nothing concerning the order the edges were traversed in. So with some brainstorming I decided to add a sidebar menu which shows an ordered list of the websites, based one their entries into the database. The first one in the list is always the root, and then as you go down the list it gets closer to the most recently logged website. The information displayed is only the website title (with the "- Mozilla Firefox" no longer being recorded) and the date/time that the site was first visited on, while logging. To easier display this information I added an effect to the list so that when the user mouses over any node on the graph the corresponding entry on the sidebar lights up.

   

  I will continue with this idea by having the list in its own container with a scroll bar, so that you don't have to scroll all the way down and not see the graph. Also, I would like to add an event so that when a node on the graph is clicked the list automatically scrolls to center that entry in the list container.

  That's all in terms of added features, for now. Once I'm satisfied with point #5 I'll be writing up some release notes (I haven't done this before) to prepare for the first round of testing. My goal is to have everything ready for Wednesday, July 1st, then hopefully some grad students and/or students in the lab here, BA2270, would be willing to take some time to play around / find bugs / give suggestions / complain about it!

Thursday, June 18, 2009

Code Cleaning, Short Term Memory, and Bread Crumbs...What?

  Lately I haven't added many new features to my extension. Instead I've been refactoring the code, changing variable names to make it more understandable, and separating related methods into a few extra javascript files. This helps me get everything straight it my mind - just today I noticed there was a file called graph.js and could not remember what it was for. It turned out it was an old file from two weeks ago, from when I was trying to build an XML file parser to build the graph structure into an XML file. This "code cleaning" will hopefully help me be more productive.
  My latest addition has been a context menu, for right-clicking on nodes of the graph, to open up a few extra options - Delete, Collapse, New Edge (?). I'm really at the stage where I have to come up with ideas on how the user may want to manipulate this graph (Any suggestions will be much appreciated!). One feature that I'm interested in adding is one that Steve mentioned, when Anita Sarma came to visit on Tuesday, which I would term as giving the extension a short term memory. The situation is as follows: Sometimes you don't know when you begin that your browsing session will be important enough to record until you visit a number of sites and reach a certain point. In this situation my extension cannot help, the user has to know to begin with that their future browsing will be useful and relevant. Steve told a story about a group who were teaching students with mental handicaps who would continuously record the students, how they got along, and so forth. Even though they were constantly recording only a certain time frame was ever SAVED, say one hour. So if something happened that warranted actually recording, one of the supervisors could press record or save so that the film would actually be saved to a persistent storage, along with the one hour of film previously recorded that has not been wiped. I could add the same feature, not based on times but based on a user specified amount of URL clicks or site visits. The idea would be to continuously log sites - even if its off - but once a certain number of entries are in the database the first ones would get overwritten. Then, once the logging feature is activated, the history will also be there. Now, this would have to be an option that could be turned on and off as desired, since often times the past, say, 30 links before the logging feature were turned on may be completely irrelevant, and just cause more work for the user to delete all those nodes out. Also, I'd have to do some tests to find an optimal default number of sites to remember from the past. Any thoughts on the utility of this idea?
  I've been trying to find more information that Gina Venolia has done, with Microsoft Research, related to semantic searching and the most effective ways to display information about a web page. However, I've yet to find the specific or relating papers. The limitations of using only favicons are readily noticeable, so I would to find a solution that can convey more information at a glace than just the website - that is, assuming the user is familiar with the site's favicon. Come early next week, I will be taking a closer look at thumbnails - a visual representation of the page layout is the most immediately identifiable form to convey all needed information in.
  I'm relatively far into development now, yet I still have no name. I tried asking a few non-computer scientist friends, explaining the basic idea of it. The best suggestion I've received so far for a name is Bread Crumbs. Also, I remember from the initial discussion about this with Steve that he kept saying things along the lines of "It's like leaving a trail of bread crumbs for yourself." I'm still open for suggestions, but by the end of next week I'd like to have a name. 

Monday, June 15, 2009

I'm never satisfied with how people do things.

  I spent at least two days trying to get edge annotations, separate from node annotations. Its hard to modify the JSViz source code to do what I want; I've probably spent 5-6 hours reading through it, trying to understand exactly what the code does (aside from the Runge-Kutta Integration algorithm, that's too much for me). It probably would have been easier to have two tables - nodes and edges - but that would make saving/loading twice as slow. Not to mention it would take a lot more code. Trying to do it with only one table is more complex: if a node is the target of more than one edges it creates an ugly situation since I can't directly reference edges. I had to draw a few pages of diagrams to run through the code and get a feeling for what was going wrong. I eventually (after a full day of working) figured out a solution by using only the index of each edge in the database, and a dictionary to store the annotations in. Its not too pretty but it actually works, and the only modification I had to add to JSViz was change a half dozen methods to take an additional parameter.
  Although my approach works there is a limiting factor to it - it slows everything down. The edges that JSViz makes by default are dotted. This is because the edges are rendered in HTML using div tags, which can me translated but not rotated. By making the edge appear as a dotted line the effect of an edge rotating when one node is dragged and moved around can be achieved by translating each pixel of the edge appropriately. If the edge was one solid line this would be very hard to do in HTML. As a result, there is no one cohesive "edge" object between two nodes, the edge is actually an array of pixels - the individual HTML div's. This causes problems for me because I was adding event listens for both mouse over and right clicks. In order for the mouse over to work with the initial dotted edge of about 5 pixels the mouse had to be perfectly on one of those 5 pixels, so it didn't work well. To solve this I increased the pixels of each edge from 5 to 50. This made the edge more solid and thicker, allowing an easier and smoother mouse over. But, in order for it to work, each pixels had to have three event listeners - mouse over, mouse out, and right click. This meant that adding one edge to the graph added 150 event listeners. As you can assume, this slowed the once smooth graph building down to a choppy mess. My plan is to do a bit of testing, eventually, to achieve the optimal number of pixels so that a mouse over event isn't frustrating for the user while still having the graph building process a smooth one. I think this is something to do towards the end, maybe getting a few people to use it and see what they like. I'd most likely show them where in the source code the line for the number of pixels is, so they can edit it and play around to find their "optimal pixel count".  
  The latest accomplishment I've made is updating the favicon extraction process so that it will always work on any site that has a favicon - even Facebook, which stores it at the obscure address http://static.ak.fbcdn.net/favicon.ico?8:132011. It didn't go smoothly either though, for at least an hour and a half I was confused as to why my update cased one of my functions to suddenly no longer become a function. It turned out I had a local variable url and the function was url.getPossibleFavicon(), a different url object. That's the problem with working on the same piece of code for the whole day, I get focused on the small details and lose track of the bigger picture. 
  This afternoon was spent preparing for the demo, centered around building a PC. The challenging part was trying to come up with a situation where the Back button's limitations really show. Also, trying to get a few different sites so the favicons are distinguishable took awhile. But overall I think I have a decent little presentation. I'm just glad I have everything working!

Wednesday, June 10, 2009

Updates and Frustration

   First off, a great extension to generate or extract favicons:

    IdentFavIcon: The actual generation of the custom favicons involves the use of a 32 bit cyclic redundancy table and does a lot of random alterations to the pixel colours and rendering context to produce a visually unique icon. One idea that I have to utilize this addon is to suggest using it in parallel with my extension. IdentFavIcon stores the custom generated icon in the moz_favicon table so it can be easily accessed by my extension. The idea would be to cause graceful degradation when trying to extract the favicon URL - first do my simple regular expression query of the moz_favicon database, if that fails use a simple but efficient function IdentFavIcon has to get the explicit URL, and finally if that fails IdentFavIcon would have created and custom one. I can extract the custom one by getting the last entry of in the moz_favcon table. If I do this then I no longer need to use my "dummy icon" (a black box with a question mark) for sites that do not have favicons. However, I'd be requiring the user to install a second extension to work in tandem. I could possibly copy the IdentFavIcon Javascript (there is only one) and integrate that into my extension, but that seems like the lazy way to do it. Not to mention it would feel like I'm stealing someones hard work. I'm still undecided as to what I should do.

 Now for some status updates:

    I spent all Tuesday doing that Save and Load functions. I'm glad to say they both work. It's rather simple, the Save part works by dumping the database to a text file, with '|||' separating each entry. Yes, '|||' is an odd separating string to use, but since I'm saving the user entered annotation I couldn't use something as simple as a comma. '|||' is really just an arbitrary choice that I suspect no one would enter into an annotation (unless they feel the need to make my extension crash and burn). The Loading portion parses the text file and inserts each entry into the database. Also, I've set it up so that Loading opens a new tab with the page that was last visited in the loaded session, so it is exactly like picking up from where you left off. My only gripe is that it takes a long time to Load - loading a file that is a copy of a database with 30 entries, and opening the new tab, takes about 4 seconds from selecting Open to having the tab opened. I suppose my method for saving isn't the most efficient but are there any alternatives?

    Today was spent cleaning up my code more - getting rid of many global variables, renaming methods, doing a few things more efficiently/elegantly, and so on. I did however end up wasting about two hours trying to upgrade the logging system to use page loading events as opposed to a constant timer. This turned out to be completely futile, as the page load event always fires two or three times during the first load, for no apparent reason. I've looked at the Mozilla documentation plus three extensions that use the page load event but none of their methods worked for me - every time the event would fire multiple times. This frustrated me to no end, so I just gave up and reverted to use timers.

    My current dilemma:

    Imagine this situation: You're running this extension and it is turned on, so it is logging the sites you visit. You visit site A, so it gets logged (its source/parent is irrelevant here). You then decide you don't want the application running anymore, so you click the icon and it stops. You click a mildly interesting link and go to site B, then click another and end up at site C. Site C is interesting to you, you want to log it, so you click the icon and turn the site logging on. Now, you select "Open Graph" to see what your browsing graph looks like. Is there one graph with an edge Site A -> Site C or is there multiple graphs, one of which only has two nodes and one edge, Site B -> Site C? 

    Currently my extension will show Site A -> Site C in a situation like this. This makes the most sense to the user, that IS the path they followed, just without the not-so-important Site B. However, its not the TRUE path they took, so the graph is not an unbiased "history of clicks". I'll most likely stick to the current model, but giving the user the option to choose between the two interpretations might be a good idea. 

Monday, June 8, 2009

 On Thursday morning I had a chat with Steve about some use cases. The focus was on scientists - I have to make this into a tool tailored for the scientific community. I think that may be why so many previous applications similar to this have failed: they lacked a specific audience. It's an interesting tool to develop but people need more of an incentive to use it and more of an idea of how and why it is useful.

 

 The most difficult features to implemnet would most likely be the graph manipulation features, related to clustering nodes. Since this is only the initial version I'll leave those for the end, if I think I have enough time to include them. Since I have the "Searching the web for papers" section completed and working (minor bugs excepted) my next task is going to be working on Save/Restore. I can think of two different ways to do Save/Restore:
  1) Save the data to a formatted XML file
  Pros: Can be viewed by anywhere, it will just open in a browser and all the information will be displayed. 
  Cons: It would be unchangeable, since there is no database annotations could not be changed and the user wouldn't be able to continue on from where they left off. This option only really produces a nice graphic - say for a presentation. 
  
  2) Dump the database to a .csv file
  Pros: Fully editable and allows the user to continue where they left off.  
  Cons: Overwrites the old session. No immediate way to integrate into something such as a blog post. Can only be viewed in the extension itself.
 Which route I take is really determined by what is more important: portability or changeability. My personal opinion is that the latter is more desired and useful. However, now that I think about it there is nothing preventing me from eventually adding an option like "Export to XML", in addition to the general Save/Restore. 
 While I wanted to to start on the Save/Restore process today I instead chose to improve my code. I spent some time modifying the way that URLs are logged and added to the database, in order to overcome the problem of having multiple root nodes if the user logs URLs in multiple windows. Also, I changed around the way edges are created so that circuits are now possible and don't make the graphing procedure crash. I also modified the insertion procedure so that if there is an edge A -> B then an entry of the form B -> A does not get included.

Friday, June 5, 2009

Status Report

It's been a couple days since I've last posted, but I'm happy to say that I've finally made some real progress on my extension.

My extension now properly builds the graph of a browsing session, with favicons as the graph nodes for each page. However, they don't always work. Sometimes a websites favicon URL differs greatly from the URL visited. In these cases the node image defaults to a black box with a question mark. What annoys me most is that many sites don't have favicons, but I guess there isn't much I can do about that.


(Unfortunately the picture doesn't show very well.) I've also implemented a few features to the graph. Double clicking an node opens a new tab in the browser and loads that node's URL. Hovering the mouse over a node of the graph makes a nice little tooltip open, displaying the URL default, but it's main purpose is to hold an annotation.

A single click on a node opens up a little dialog box to edit the annotation.

What I'd eventually like to do is alter the JSViz source code so that the edges between nodes are actual objects, then I could place the annotation as a mouse over event for the edges since that's really where it belongs.

Overall I'm very pleased with how it's all going! Now that I have the basics done I can start cleaning up my code, adding features, and altering the interface.

My plan for Monday is to work on a better regular expression to extract favicon URLs. Currently I just parse each URL until I reach .com or .ca or .org, then search the moz_favicon table for any URL that has a matching initial section, up until the domain name. It's not the most effective query string. The first real feature I want to add is a saving/loading options, which I'll go into detail about on my next post, (hopefully) on Monday. Also on Monday I hope to make a short post about the meeting I have with Steve on Wednesday morning, concerning use cases and potential features.

Tuesday, June 2, 2009

What Doesn't Work

Today I've found out what doesn't work:
 
    Using a hidden iframe to open the URL and this get its windowContents property, transferring that onto a canvas, does not work. Even though Mozilla says it should . Odd, isn't it?

    Once the URL is loaded, prior to its information being inserted into my database, saving the windowContents to a canvas then converting that to a string - its base 64 representation - to be inserted into a database does not work. I think the problem is that inserting a string with nearly 10,000 characters may be pushing SQLite. I'm not 100% sure if this is the problem though, since the only notification I get is a catastrophic error message with the only relevant information being the line and NS_ERROR_COMPONENT_FAILURE which, lets face it, doesn't tell me much.
 
    So currently thumbnails are a bit beyond my grasp, but during the meeting today I was given a great suggestion to use for now; instead of a thumbnail - use the favicon icon (I'm sorry, I forget what the name of the girl who suggested it!). I predict that this will work well, simply because I can query the SQLite database that Mozilla has for storing history related information though the moz_favicon table. The table has a reference to the URLs of the favicon icons. There are two issues I need to solve:

    How do I tie each URL to the appropriate favicon URL?

    I think I'll try to link them by querying the moz_favicon table. When a URL is being added to my URL table, query the moz_favicon database table with a regular expression containing the websites URL then add that URL as another column in my url table. However, it can't be the exact URL that I query the database with - that won't be in the moz_favicon table - so I have to instead get the relevant portion. I could possibly search from the beginning of the string to .com OR .ca OR .org OR...every possible domain. This isn't pretty, but it should work, in theory. Then I have to ensure that the favicon is indeed stored there. Unfortunately I have already ran into a site which has it stored at some obscure location; the Google favicon for some site I've visited was stored at http://www.gstatic.com/news/img/favicon.ico (found through a SQLite database manager extension), clearly the domain does not begin with google.com or google.ca. I think I'll try this route anyway, since its the only reasonable idea I can come up with. My other option would be to parse each website's HTML code and find a link tag which has rel="icon" or rel="icon shortcut", the two most prevalent tags used for linking the favicon. Once I found the proper link> tag I just insert its href attribute. What I don't like about this approach is that I'm relying on the website's coder to be tidy; I'm relying on them giving the tag an appropriate rel attribute (according to Wikipedia). 
  I'll take it one step at a time:
  Step 1 - Use a placeholder/dummy image but just try to build the graph of a session.
  Step 2 - Replace placeholder image with the favicon image of that site; this shouldn't require too much alterations if I can do Step 1.
  Step 3 - Generate thumbnails for the nodes. This step I might not get to, unfortunately, but I'd really REALLY like to. If I'm not able to implement this during this summer it makes for a good starting point for future enhancements to my extension.
   
    Does every site have a favicon image?

    There is some standardization for file formats for favicon images and such, but I'm not sure if they're mandatory. I think I'll create a dummy image if my basic queries can't find the URL, until I research this question further and get a definite answer.
  
 Since I now know how I'll approach the thumbnail issue (instead of flailing around with canvas examples and stumbling through documentation) what I want to do is choose a library to do the graphing of the data. Once I have this I can start doing simple tests to make sure my code works, and then really start making progress. The use of favicons makes it easier to choose, since they're direct URLs to the images I can use JSViz or, as Ainsley showed me to, Graph Gear  - both offers nice graphs and use URLs for the images. But this got me thinking. If I go in this direction the whole meat of this extension will be tied to using URLs for the graph nodes. This is good for my use, favicons, but once it gets upgraded to use thumbnail images the code would have to be reworked quite a bit. Most extensions that have anything to do with thumbnails render them with canvas'. This is doable but it takes a lot of time to wrap my head around how exactly they're going about it - how they're saving the data without actually saving an image. Should I be concerned with this? Tomorrow I'll ask Steve about this. I know it would be possible to save them all to the users local hard drive - but would people be comfortable with that? Once I have a working prototype I'll have to find some testers willing to critique it.

Friday, May 29, 2009

Thumbnails, Thumbnails, Thumbnails

    Today I decided to work from home, since all I'm really doing is research. I've been looking at a few FireFox extensions that involve web page thumbnails in one way or another, trying to determine how they generate the thumbnail.

    After jumping back and forth between each method that involves any "thumbnail" reference I think I've found the source, the following SQLite statement 

  "SELECT moz_historyvisits.id, title, domain, visit_count, screenshot, dayvisits, moz_places.url, visit_date " +
  "FROM moz_historyvisits " +
  "JOIN moz_places ON moz_historyvisits.place_id = moz_places.id " +
  "LEFT JOIN wr_places ON wr_places.url = moz_places.url " +
  "WHERE moz_historyvisits.id = ?1 " +
  "AND visit_type NOT IN (4,7);";

    The origin of all thumbnail references come from this SQLite statement and, in particular, there is,

  var thumbnail = createRootNodeStatement.getUTF8String(4);

     which gets the 4th variable returned from executing this statement - whatever is in the "screenshot" column. I was hoping that screenshot would be a picture, but according to the database initialization the screenshot column is just text. What really confused me the first few times I read over all the source code is that not once, in any of the Javascript files in WebReview, does any data ever get Inserted into the database. I know most of the tables dealt with here are built in to FireFox and it's history management system, but there is still the table made by WebReview

webreviewDBConn.executeSimpleSQL("CREATE TABLE IF NOT EXISTS wr.wr_places ( url TEXT PRIMARY KEY, frequency REAL, dayvisits INTEGER, domain TEXT, subdomain TEXT, screenshot TEXT, daysession INTEGER );");

    which never receives any data directly from a call in the JavaScripts. The only conclusion I can reach is that in the long SQLite statement, where the screenshot is extracted, the line "LEFT JOIN wr_places ON wr_places.url = moz_places.url " synchronizes the data on the moz_places table with wr_places to somehow generate useful data for the screenshot column. I found this site  which gives a rough idea of what each of the tables in the places.sqlite database contain, but none show any obvious screenshot related information. What I really need is an SQL expert to decode that statement for me, in hopes of getting more clues as to where the screenshot data comes from.

    Screengrab uses some Java methods to generate it's screenshots, the key ones being

var image = java.ava.awt.Robot().createScreenCapture(new java.awt.Rectangle(box.x, box.y, box.width, box.height));
  Packages.javax.imageio.ImageIO.write(image, "png", b64os);
  b64os.close();
  return "data:image/png;base64," + baos.toString();

    where box has a references to the screen dimensions. Screengrab uses its own custom class, Base64$OutputStreem, which I may have to decompile and read. The program basically gets the screen capture through the Robot's createScreenCapture method, and saves it to their custom Base64 OutputStreem. What's returned is the Base64 string representation of the image, which can be applied to any html img object through img.src = the returned string. I like this way of getting the screenshot - nothing is actually being saved, only the raw data of the screenshot is stored in the image src attribute. But would it be okay to decompile this custom made Base64$OutputStream file, understand it, then use it for my own extension?

    One extra piece of information I found while reading the source code is that there appears to be a method to save files built in to Mozilla. nsIFilePicker  creates an open/save dialog box, so this would be very useful for implementing a future save/load function to the graph. Also, nsIFile XPCOM should help in creating temporary files if needed.

    Showcase produces a nice thumbnail view of all the pages in tabs you currently have open. The source was pretty daunting to rummage through - one file had nearly 7,000 lines - but when I found how they actually make the thumbnails I was surprised, since it was so easy. It uses a drawWindow method for canvas's which renders the entire web content, given the dimensions, into a canvas object. I don't know why the previous two extensions didn't use this method, as opposed to their elaborate work around. I suppose there is the possibility that, for the Screengrab extension, saving it to a canvas does not help with actually saving the image. Using a canvas seems to be the most reasonable route to create the thumbnails. However, JSViz uses a CSS style property backgroundImage to set the image background, and it takes a URL as the parameter. This means I won't be able to use the JSViz library to create the graphs if I use canvas' to draw the thumbnails on. 
    On closer inspection it appears that WebReview uses a canvas to draw it's thumbnails as well, except it uses the drawImage method instead of the drawWindow method. This could be useful if I have problems with the drawWIndow method. However, I would still need to know what information is contained in the screenshot column of the database table. Based on what I learned from the Screengrab extension, I think that since it is a string it is most likely the base 64 encoding of the image that gets store in that column. How the information gets there with no table inserts is a mystery to me, though.
  
    As it stands I've made one step forward - I think I could do thumbnails! - but one step back - no more JSViz. I'll have an interesting time on Monday trying to find a graphing application/library that is compatible with Javascript canvas object (Steve told me not to design my own algorithm - thank god!). There is one more option I found though, which would work with JSViz. PageGlimpse offers a service to that offers developers access to thumbnails of any web page. Sounds perfect, right? Well, almost. It is indeed free, but only if I use under 300gb/month. This is a lot of bandwidth for just some small thumbnails, but to access the site through an application I write I would have to send my developer key (obtained through a free sign up) so the amount transferred would be linked to me. Also, since this is a FireFox extension, my hopes is that many people would be using it but the more people use it the more potential there is to go over the limit. Plus the site is still only in the beta stages; tying my extension to a site that I have no control over and could possibly shut down or decide to charge a fee does not sit well with me. Still, it is an interesting service to offer.
 
    The last thing I'd like to make a note about is this Python script using Mozilla to create a thumbnail of any URL. It's deceptively simple and only a hundred lines or less, but if it works correctly (haven't bothered testing) it would be a good option to look into if I have to cut my application away from being a FireFox extension and default to be a stand alone browser application. 
 
    I'll spend the rest of the day now cleaning up my test extension, which is now almost doesn't require clicking to log every website.

Wednesday, May 27, 2009

Making progress

    Throughout the morning I convinced myself that I'd finally choose which route to go - learning Flash and Flare or JSViz. Well, JSViz won. I know that in my last post I said I really wanted to use Flash, but that seems unreasonable at the moment. Maybe once I have the guts of the extension finished I could experiment with different visualization options. Plus, I figure that a lot people aren't very fond of Flash, and I'd have to spend quite a long time learning essentially two new sets of syntax - Flash probably isn't very similar to any languages I already know. JSViz has some nice examples that I should be able to cut and paste from, to get the general structure of how it does things. Also, it can create pretty nice looking graphs. But JSViz requires an XML file of the node data to build the graph from - Javascript cannot write to local files so I cant create this file on the fly. I'm hoping that I could have a standard XML file to load, with a built in Javascript that will automatically run and edit the DOM properties of the file to create the node structure that JAViz's XMLTreeGraphBuilder requires. After looking through the XMLTreeGraphBuilder source code I'm still unsure if it reads in the document with or without loading it, just scanning the lines. If it loads the document then my approach should work, however if it just scans the file I may be out of luck.

    I've finally worked most the kinks out of my test extension. It properly loads and displays the logged URLs, clears the display field when Clear Display is clicked, removes an entry from the database by selecting one in the list box and clicking Remove, and goes to the currently selected URL when Go To... is clicked. I'm having a bit of trouble re-displaying the list of URLs after one is Removed - I think the window.location.reload() function stops any function executing after it, since reload would reload the Javascript. But that's only a usability annoyance which I'll solve once the main project is near completion.

Tuesday, May 26, 2009

Beginning Version 2 - Integrating SQLite

    I've decided to bite the bullet and learn to use SQLite; I figure it's easier to do this than dig through Mozilla's documentation for solutions to my data management problem. Suprisngly, SQLite isn't that complex to learn. The Mozilla wrapper functions to deal with SQLite, on the other hand, need a bit of cleaning up, in my opinion. Current references:

    My most valuable reference though is the source code for WebReview. Without this I would be completely stuck, as Mozilla documentation covers only the very basics. The biggest problem I'm running in to is how every error I receive in the Firefox Error Console is unlisted anywhere, except for a few sparse posts by people working on various Mozilla projects. At least I've been able to solve most of the problems by copying the WebReview source code and editing it line by line to suit my needs. 

    While looking through the WebReview source code I discovered a whole separate class dealing specifically with the thumbnails - over 5,000 lines of code. This just reinforces my belief that adding thumbnails to the graph is just too far beyond my skill level. Since a final version would lack some visual flare to it I'm really going to focus on integrating SQLite and then learning some Flash to use the Flare API, creating really nice and interactive graphs.

What I accomplished today:

  • Learned the basics of SQL, enough to create and manipulate tables.
  • Learned some of the Mozilla API to access databases, execute commands, and save data.
  • Managed to finally get Components.utils.import to work, after altering my chrome.manifest file.
  • Started version two of my basic Firefox extension: now URLs are logged into a database to solve the problem of sharing data between two windows.

To do tomorrow:

  • Test to make sure everything works appropriately.
  • Implement "Go to..." and "Remove" - Remove is now possible since I can manipulate the database.
  • Start thinking about how to implement "Start" and "Stop" so that the Firefox status bar icon doesn't have to be clicked to log a URL.
  • Figure out why this SQL trigger causes an error in the exectuteSimpleSQL method:

 CREATE TRIGGER insert_url_time AFTER INSERT ON url_log
 BEGIN
 UPDATE url_log SET timeEnter = DATETIME('NOW')
  WHERE rowid = new.rowid;  
 END;

That's all for today.


Monday, May 25, 2009

Visualization

Today I put my test Firefox extension on hold and instead decided to dive in to the problem of creating the graph - and it is indeed a problem. From what I've found so far embedding thumbnails is going to be the most challenging aspect which I may have to simply not do. Even so, creating the graph myself is going to be difficult. 

Option 1: JViz library

This is a Javascript library to create graphs through DOM objects. It seems easy to use in the sense that coordinates of nodes do no have to be specified. However, this option is not very visually pleasing. In this tutorial I've found that the images for the nodes have to be supplied through a URL - I would either need a generic node image packaged with the extension and hope JSViz accepts relative URLs or find a way to save thumbnails of webpage. The later option does not seem possible, as Javascript does not have access to the local harddisk.

Option  2: Flare  

I really like the visualization options of Flare, it seems like it would be perfect to make up for the loss of thumbnails. The downside to using Flare is that I have to learn ActionScript and Flash, two things I've never used before. Also, so only data that can be passed from a Javascript to an ActionScript (to pass the logged URLs and associated data to the visualization) is a string. Now I could find a way to express the array of URL objects in a specially formatted string and parse it within the Flash file to generate the data, or I can learn something like SQlite and maintain a database of the information. Both options don't seem to pleasing to me, but I will most likely have to go the database route. 

I would really love to use Flare - if I end up doing this browsing history visualizer - but the sheer amount of learning I would have to do would slow the programming down a lot. What I have to learn completely is: Javascript, XUL, Mozilla API, Flash, ActionScript, Flare API, and SQlite. That is a lot of topics to learn for one relatively straight forward - in principle - Firefox extension. What's really holding me back is the actual graph part - dynamically creating a graph of data is considerably hard; dynamically creating a graph of data stored in a Javascript object which cannot be exported to, say, a temporary file is frustratingly harder.

If I use Flare I think my first step would be to decide whether I want to pass the data from the Javascript through a specially formatted string or just bite the bullet and learn how to use SQlite

Friday, May 22, 2009

I dislike Mozilla

    I'd just like to say that Mozilla has terrible documentation/API. They provide basic examples of what you can do with their functions, but whats the point if they don't work correctly? None of the options the Mozilla development site listed for sharing data between windows actually worked, every one I tried gave me some sort of error that wasn't explained anywhere. The mechanic I'm currently using only shares a copy of the objects, so editing them is pointless. Javascript core modules kept giving me "Permission Denied to access Components.utils.import" errors and the only reason I can think of would be because two instances of the javascript are run at the same time attempting to open the same module - but that's the very problem that it says its supposed to solve: "JavaScript code modules New in Firefox 3 is a simple method for creating shared global singleton objects that can be imported into any other JavaScript scope. The importing scope will have access to the objects and data in the code module. Since the code module is cached, all scopes get the same instance of the code module and can share the data in the module." 
    Mozilla has something called an XPCOM Component  which can be defined to contain any information and accessible from anywhere. However, the simplest example, the standard HelloWorld.js, is 106 lines - 106 lines for the simplest example. Learning how to make my own XPCOM components may simply be not worth it; I might as well learn something like sqlite and go the database route, since it could be helpful in future courses I take. 
 Now I'll begin trying to make some sort of a presentation for the beginning of next week.

Thursday, May 21, 2009

May 21: My First Firefox Extension

    I've spent today trying to develop a test extension, one that also involves saving visited URLs but doing nothing really fancy with them. My goal is to have a status bar icon that can be clicked and save the current URL. Then on a menu window will have an area to show the logged URLs and a few extra options. I'll just list a few problems/solutions I ran into while trying to develop it:

  • Some initial XUL research into the types of elements and objects it offers led me to use a listbox to display the URLs in, since it has an appendItem() method which I should be able to access through a Javascript to build up the list on screen.
  • It took me awhile to figure out how to properly edit XUL elements through Javascripts, since I initially had no understanding of the the DOM objects that XUL uses. I eventually learned the odd commands needed, such as the all important document.getElementById(id) method. 
  • After a frustrating hour of getElementById constantly returning null I found discovered that the document wasn't fully being loaded by the time my Javascript was called, so the object I was trying to reach wasn't in existence yet. I add to change the script to have a small function, init(), and then add onload="init();" to the window XUL object. Such frustrating details.
  • Once I finally figured that out I could begin designing a layout for the menu window. 
 This part wasn't too hard but required learning a lot more XUL to get a semi decent layout, mostly utilizing boxes to get things sort of lining up. I don't know if I can use the Start and Stop buttons as I wish yet - those were added under the assumption that the script would run constantly and logging URLs on its own. Right now that's just too advanced for me. The Go To... and Remove buttons are some things I thought of which I might add as a challenge to myself to understand more XUL and Javascript. I have an idea of how to implement them, but only once I get the basics of the extension actually working.

 

  • I wrote a relatively short Javascript to log the current URL and saving it to an array - rather simple. The problem I have is that the status bar icon can easily grab the URL of the browser, since they're embedded as the same window, but my options window (pictured above) has no access to that window. Unfortunately, to have access to the functions in my Javascript, and the array of URLs, I need to include a call in the option's XUL file. Doing this executes another instance of my Javascript, hence leaving me with no access to the built up array in the other instance of the script. This means I have no way to share data between the two windows- and I need access to the array in the options window to display the URLs when the Display button is clicked. I have the feeling that I'll have to maintain everything to a database, but I would much rather find a way that only involves Javascript, so let the Googling commence.
  • It seems like I'm finally getting a bit of luck. It turns out that the function to open a window from the main browser window allows for Javascript objects to be passed - albeit in a slightly odd manner. So as it stands my extension is working as intended. I'm unsure if my "Remove" button will be able to properly function though, because the solution I found passes a copy of the array of URLs, so I could remove a specific URL from being displayed, but that would not remove it from the actual structure; there is no way to pass pointers or references from a Javascript to a new XUL window. There are a few options here for working with objects between multiple windows, which would be a better solution to my problem.

Wednesday, May 20, 2009

Interesting Article

I was reading this article about provenance and browser history which I found really interesting. A couple thoughts:

  • They presented an interesting solution to the problem cycles: versioning and time stamping. The authors suggest that a graph of the browser history may contain multiple versions of the same site, depending on when it was visited following which link.
  • Current browsers do not register any meta-data between two pages in their history files when those pages are opened together - their is not timing data saved.
  • An interesting concept for history management would be to integrate a prolific search engine such as Google when searching browser history. If you can't remember an important site that you visited earlier but you know generally what it was about coming up with the search query to find the page again may be very difficult. If a search engine is integrated into the searching, when a query is entered it could go through the search engine but filter the results to display all relevant pages that also appear in the browser's history.
  • While this article discusses many points about using the actual stored browser history as the meta-data, the same ideas can be applied to a real time relational capturing tool.
  • Surprisingly, to date no browsers have integrated graph support for displaying user history.

May 20: A Review

Initial Idea: Obviously, use a graph structure where each node is a URL and the edges represent a click from that URL to another or the other way around (directed could be optional). The edge could easily be remembered: when the user clicks a link make the edge between that link and the last URL. This implementation seemed fairly intuitive to me, however I quickly ran into the problem of how the graph itself would be presented to the user. Using Javascript's rudimentary graphics tools to draw a dynamic and coherent web graph would be a painstaking task. Nodes would have to be evenly spread so that there is room for each branch, and even then I would have to add some complex geometric computations to ensure that each branch has enough space (relative to pixel real estate) to display all of its own edges and nodes without inadvertently overlapping other branches, reducing the graph to an unreadable mess of lines and thumbnails. However nice this option may sound I just cannot justify devoting a large portion of my work period to the development of algorithms to generate aesthetically pleasing graphs. 


Second Idea: A more reasonable approach to visualizing the users web browsing session would be through a tree. A tree structure allows for a clear representation similar to web graph, but the initial computation to generate children of a node seems far more straight forward. As long as each node keeps track of how many children it has, centering these children in a straight row below their parent node requires multiplying the set size of each node (relative to pixels) by the number of nodes, with a few pixels keeping the nodes from touching.
 My idea for the structure: 
  Each Node object would have five attributes: Its URL, Date/Time of initial visit (this session), an Array of other Node objects, a count of how many children this node has, and a Depth attribute to show its level in the tree.
  URL: Straight forward, this is the most important bit of information to keep track of.
  Date/Time: By giving a Date/Time attribute to each node I can give the ordering of a Node's children some significance, for example the ordering of children from left to right could represent the earliest visit to the most recent visit.
  Array: For the recursive building of the tree.
  Number of Children: This number is required to make computing the pixel real estate needed an easier processes, as briefly outlined above.
  Depth: The root would be considered depth 0. This property is a late edition that I thought of last night to try to solve the problem of overlapping. Even though I can easily compute where children will be located I still had no way to prevent children from overlapping. However, if I can search for each node at a certain depth I can list them side by side such that none overlap - and spread them out in an even manner.

 Doing this implementation I would have to have a set number of pixels to consider the maximum width. This wouldn't be all that hard, but I would then need to consider what would be a good bound on the maximum expected number of Nodes at each level, and make sure that my max pixel-to-Node-size-ratio is able to accommodate this.
 
 Some Potential Problems:
  1) Currently I have no way to deal with a cycle in the tree. A possible solution would be while building the graph flag a Node if that Node has already been entered into the tree but is a child of another Node - instead of adding it to the tree again or creating a path back to that Node which would most likely overlap other Nodes the next Node in the tree could be a Node with no children and arbitrary properties that says "See Node _____". But this creates a loss of relevance in what the tree is trying to convey (see next problem).

  2) How can I distinguish Site A being reached from Site B from, say, being at Site A then typing a completely new website into the search bar - Google, for example. In my structure there would be a path from Site A to Google even though there was no direct link following. This could potentially be a problem, but it could also be the right thing to do - the Google query could be provoked by the content of Site A and therefore there really should be a link between them. Would this be a good design choice? The flaw is that in most cases Google (or the users preferred search engine) would be visited multiple times per session; if I use the possible solution to problem 1 then all relevance between Site A and the result of the Google query would be lost.

 
Closer look at WebReview and thoughts of it:
 Since WebReview is a Firefox extension that has a history graphing utility similar to what I am researching I decided to take a closer look at it, well more accurately a closer look at its source code. After digging through the collection of Javascript files to find the ones related to the graph function I was greeted to nearly 2,500 lines of code with 80% of the documentation written in German - and that's only for the visualization of the graph, not the data structure of it. Unfortunately, even though it took this company 2,500 lines of code to display their graph the were doing it what I thought was the EASY way, in a tree structure. To be fair, their tree has nice graphics such as children of a parent not being displayed until the parent is clicked, causing the window to slide into focus of that node. 
 What WebReview is missing is an annotation feature. I feel I could implement this reasonably well, except to maximize its usefulness a Session and its resulting graph would have to have the ability to be saved. Saving into a html document as WebReview does is a complicated process, so instead an option I could pursue would be to save just the Nodes and their properties into a CSV file. Loading would only require parsing the file and rebuilding.
 One useful bit of information I discovered is that WebReview uses an sqlite database to store all of the extracted history from the Firefox history manager. I would personally rather use an abstract data type to store all of the Nodes in, something sortable like a Heap, but I'm unsure if this is even possible to do in a Javascript. I know I can create the data structure, but is maintaining it with a potentially large amount of data too intensive for Javascript? I may have to use a database like sqlite but that would require spending another week or so learning the ins and outs of it. One problem I foresee is that the building of the graph would be very slow with a Heap structure since it would have to be sorted with respect to the Depth property, so that computing the layout. (Note that using a tree structure does not require the Heap since everything is referenced to each other through each Node's Array of children). Also, my idea for the structure would be a complete waste if I had to end up using sqlite to store everything. 
 

Similar Projects:
 I found this great site that lists some projects that were being developed about a decade ago, similar to what I am attempting. What I envision is something similar to the SurfSerf application listed there (but less 90's Geocities-ish, I hope). Unfortunately the site is no longer maintained and none of the links work - but the pictures are representative enough.

 
To Accomplish Today:
 - Research the limitations of Javascript in terms of how large data structures can be managed.
 - Create a test extension that adds URLs to an array, with a button to open a window listing each of the stored URLs.
 - Maybe learn some sqlite.