Jun 03, 2005

Greasemonkey

So lately I've discovered the power of the Greasemonkey extension.  I'm not sure it offers much to the normal user, but if you're a tech savvy internet denizen not afraid to hack a little, (you are using Firefox, right?) then Greasemonkey can improve your browsing experience.  Essentially greasemonkey allows you to add your own custom javascript to a site.  Why would you want to do this?  Glad you asked.

I have a big monitor.  In fact I have two monitors attached to my primary computer, one 19" and one 17".  I rationalize that this lets me keep both my editor and a console window open and visible at the same time, but you and I both know that its really so I can browse the web and watch movies at the same time.  Unfortunately many websites are still using table based layouts with fixed widths.  This means on a big monitor like mine a website might look like this:

Notice all the whitespace on the right?  Only about 1/2 of my browser window actually contains the text that I want to read.  What I'd really prefer (since I'm using tabbed browsing) is to see the "printable" version.  No navigation or ads, and my whole screen is filled with the text I actually want to read.

So it's only one extra click to get to the printable version, but it breaks my flow for reading "article" style sites: go to the index page and open in tabs all the stories I wat to read.  So this script automatically parses the url and navigates to the printable version of the same page when installed as a greasemonkey script.  You have to read the greasemonkyey site to see how to do that, but short story is install greasemonkey in firefox, navigate to the script you want to install, and select "Install user script..." from the tools menu.  Type "http://frontpagemag.com/*" to apply the script to pages at frontpagemag.com.

Now my script is a crude little toy.  That's because my javascript skills range from nonexistent to laughable.  People (and by "people" I mean people who grok javascript and the dom a lot better than I do) are doing amazing things with greasemonkey scripts.  Among other gems are an ajax logging console and a collapsible tree interface to slashdot comments.  There is even an extension to generate a greasemonkey script from your in-the-browser edits of a web page.


May 14, 2005

PHP and XML editing

So I realise that PHP5 and simpleXML are the promised land for PHP devs in dealing with XMl.  The problem, for me and probably for you, is that I'm still stuck in the PHP 4.3.x series.  This is for a variety of reasons; I'm writing a relatively small and simple CMS that is targeting the shared hosting market segment (ie you don't have root, you can't install reallycoollibversion4.23.14b to make my cms work, and you don't have control over your PHP version.)

Now even PHP 4.3 boasts some nice integrated XML libraries. XSLT and DOM support etc. All not turned on by default of course. I am open to better solutions, but for PHP 4.3, it looks like the PEAR XML_Transformer class is the best bet for editing existing XML. This of course brings up the hideous lack of documentation online. Via PHP Kitchen's list of PEAR tutorials I found this tutorial wherein the author touts the ease of use of XML_Transform, demonstrates the settings for case folding, and never actually transforms any XML. So, the following brief snippet demonstrates a simple transformation.

What I want to do is have the equivalent of the domNode.innerHTMl = string way of editing html.  Not theoretically pure I konw, bu quick n easy!  My CMS has little snippets of XML that describe a page's configuration and in editing them I need to find a named BLOCK tag (which corresponds to a Template_Sigma variable) and replace it's current content (which may contain TEMPLATE, BLOCK, and OBJECT tages) with a new snippet of XML (which may contain new TEMPLATE, BLOCK, and OBJECT tags).

Basically, I write a class (replaceBlockContent) that extends the XML_Transformer_Namespace class. The base class handles all the opening and closing tag events, but calls methods in the child class if the tag type matches a defined function. For my class I have defined start_block and end_block methods, so they get called when a block tag (opening and closing) is found. I copied the default return statements from the base class. The block_start functions watches for a block tag to come along with the right name. It then sets the state of the class ($this->_capture=true) or increases the depth count if it is already in "capture mode". The block_end method usually just returns the closing tag and any cdata but if it returns an array of ($string, true) than $string overwrites all the returns back up to the opening tag. This is the behaviour we are looking for. The depth indicator is checked to account for the nested recursive nature of my XML schema. The following code would change this fragment of XML

<template name="index.html">
   <block name="menu">
       <data_object name="menu"></data_object>
   </block>
   <block name="middle_content">
   <template name="blog.html">
       <block name="blog">
               <data_object name="blog" factory="1" />
       </block>
       <block name="tags">
           <data_object name="blog" factory="1" tags="1" />
       </block>            
   </template>
   </block>
</template>

to this

<template name="index.html">
   <block name="menu">
       <data_object name="menu"></data_object>
   </block>
   <block name="middle_content">
       <data_object name="text"></data_object>
   </block>
</template>

Here is the code:
<?php

require_once 'XML/Transformer.php';
require_once 'XML/Transformer/Namespace.php';
class replaceBlockContent extends XML_Transformer_Namespace {
    var $_blockname ='';
    var $_data = '';
    var $_capture = false;
    var $_depth = 0;
    function replaceBlockContent($blockname, $xml)
    {
        $this->blockname = strtoupper($blockname);
        $this->xml = $xml;
    }
    function start_block($attributes)
    {
        if($this->_capture==true)
            $this->_depth++;
        if(strtoupper($attributes['name']) == $this->blockname)
        {
            $this->_blockname = $attributes['name'];
            $this->_capture=true;
        }
        return sprintf("<block %s>", XML_Util::attributesToString($attributes));
    }

    function end_block($cdata)
    {
        if($this->_capture==true and $this->_depth==0)
        {
            $this->_capture = false;
            return array('<block name="' . $this->_blockname . '">' . $this->xml . '</block>', true);
        }
        elseif($this->_capture==true)
        {
            $this->_depth--;
        }
        return array( sprintf('%s</block>', $cdata), FALSE);
    }
}

$name="middle_content"; $t=new XML_Transformer();
$t->overloadNamespace('', new replaceBlockContent($name, "<data_object name='text' />"));
$newschema = $t->transform($schema);

?>


Archive: [1]      «      16   |   17   |   18