Index: moodle/blocks/css_theme_tool/README.txt
--- moodle/blocks/css_theme_tool/README.txt No Base Revision
+++ moodle/blocks/css_theme_tool/README.txt Locally New
@@ -0,0 +1,71 @@
+CSS THEME TOOL README
+
+Description:
+    The CSS theme tool block is a block that is designed to make CSS creation and
+editing for Moodle both easy and accessible.
+It aims to provide a point and click interface for adding CSS rules and provides
+a user friendly interface for creating rules and is suitable for everything from
+creating simple modification to creating entire themes.
+As well as giving the user an easy means to create CSS it also allows the admin
+or a Moodle site to make immediate modifications to the installations CSS that
+will be visible by everyone using the site (not just those who can create CSS)
+and does this all without the need for editing any files within Moodle or even
+having to create or modify themes.
+
+
+About the CSS theme tool block:
+    This block was created during development of Moodle 2.0 before it was even in
+beta. My apologies if it no longer works and is out of date obviously I have not
+found time enough to maintain it.
+The block itself is 100% reliant on JavaScript trying to use it without JavaScript
+will simply mean you don't see the block at all, however the rules you made using
+it when you had JavaScript enabled will still be visible.
+Cross browser compatible.... if you're lucky. This block was developed using
+Firefox which I would recommend using it with initially. It wouldn't surprise me
+to hear it worked with Chrome, Safari, and Opera. But I do not expect it to
+work with Internet Explorer...Microsoft could sponsor me to do that ;)
+If you find bugs with this block please check first if they are listed in this
+file in the following section and if not please report them to me.
+Whilst I cannot guarantee that I will have time to maintain this block I will
+certainly aim to put in a gallant effort initially.
+
+
+Installation:
+    Simply put the block into your Moodle installations blocks directory (/blocks/)
+and then log into your site as an administrator and visit /admin/ if you are not
+prompted to upgrade immediately.
+This block creates two tables within your Moodle database which are both used to
+store CSS rules and styles. If you choose to remove this block make sure you
+remove the two tables as well:
+    * block_css_theme_tool
+    * block_css_theme_tool_styles
+
+
+Still coming, To-Do's, and bugs:
+# todo:             Finish PHP docs
+# todo:             Test
+# functionality:    Implement capabilities for the block so that more than just
+                    Admins can use it if desired.
+# functionality:    Export a theme (how cool will this be)
+# functionality:    CSS optimisation and refactoring
+# functionality:    Cache CSS files for serving rather than rebuilding every time
+# functionality:    Better caching for colour picker image
+# bug :             Cross browser support... will test... maybe fix
+
+About the creator:
+    Hi my name is Sam Hemelryk, I am a senior developer at Moodle HQ and have
+been with the project since June 2009. When I first started at HQ Moodle 2.0 was
+already well under construction but still well of being release and I was lucky
+enough to experience the growth that it went through.
+I started writing this block in March 2010 in the little bit of free time that I
+something find myself with.
+I started writing this block as I wanted to explore the knowledge that I had
+gained and put into use some of the cool stuff I had seen, used, and heard about.
+The most recent task I had undertaken at HQ was writing the base theme and
+standard themes for Moodle 2.0. This is why I created a theme creation block.
+This block attempts to use as much of Moodle 2.0's new API as is practical and I
+hope that it provides both useful functionality for themers and a good code
+reference for other plugin developers.
+One last thing before I wrap up if you have any requests for this block or would
+like to know anything more about it please feel free to post in the Moodle forums
+or email me at sam.hemelryk@gmail.com
Index: moodle/blocks/css_theme_tool/ajax.php
--- moodle/blocks/css_theme_tool/ajax.php No Base Revision
+++ moodle/blocks/css_theme_tool/ajax.php Locally New
@@ -0,0 +1,46 @@
+<?php
+
+/**
+ * This file processes all of the AJAX actions for the CSS theme tool block
+ *
+ * @package   blocks
+ * @subpackage css_theme_tool
+ * @copyright 2010 Sam Hemelryk <sam.hemelryk@gmail.com>
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+require_once('../../config.php');
+require_once($CFG->dirroot.'/blocks/css_theme_tool/lib.php');
+
+// Action determines what we do
+$action = required_param('action', PARAM_ALPHA);
+
+// Must have the sesskey
+require_sesskey();
+
+// Must be logged in and be a site admin
+if (!isloggedin() || !is_siteadmin()) {
+    error('You must be logged in as a site admin.');
+}
+
+switch ($action) {
+    case 'save':
+        // Save the rules that have been created
+        $rules = required_param('rules', PARAM_NOTAGS);
+        $styles = required_param('styles', PARAM_NOTAGS);
+        css_theme_tool::update_via_ajax($rules, $styles);
+        break;
+    case 'purge':
+        // Purge the rules in the database
+        css_theme_tool::purge_rules();
+        break;
+    case 'exportcss':
+        // Export the CSS
+        $blockid = required_param('id', PARAM_INT);
+        $blockinstance = $DB->get_record('block_instances', array('id'=>$blockid), '*', MUST_EXIST);
+        $blockcontext = get_context_instance(CONTEXT_BLOCK, $blockinstance->id);
+        $file = css_theme_tool::export_css($blockcontext);
+        $filepath = $CFG->wwwroot.'/pluginfile.php/'.$file->get_contextid().'/'.$file->get_filearea().'/'.$file->get_itemid().'/'.$file->get_filepath().'/'.$file->get_filename();
+        echo $filepath;
+        break;
+}
Index: moodle/blocks/css_theme_tool/block_css_theme_tool.php
--- moodle/blocks/css_theme_tool/block_css_theme_tool.php No Base Revision
+++ moodle/blocks/css_theme_tool/block_css_theme_tool.php Locally New
@@ -0,0 +1,105 @@
+<?php
+
+/**
+ * Contains the core class for the CSS theme tool
+ *
+ * @package   blocks
+ * @subpackage css_theme_tool
+ * @copyright 2010 Sam Hemelryk <sam.hemelryk@gmail.com>
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+require_once($CFG->dirroot.'/blocks/css_theme_tool/lib.php');
+
+/**
+ * Description of block_css_theme_tool
+ *
+ * @package   blocks
+ * @subpackage css_theme_tool
+ * @copyright 2010 Sam Hemelryk <sam.hemelryk@gmail.com>
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class block_css_theme_tool extends block_base {
+
+    function init() {
+        $this->title = get_string('pluginname', 'block_css_theme_tool');
+        $this->version = 2010032600;
+    }
+
+    function applicable_formats() {
+        return array('all' => true);
+    }
+
+    function instance_allow_multiple() {
+        return false;
+    }
+
+    function get_content() {
+
+        if ($this->content !== NULL) {
+            return $this->content;
+        }
+
+        $this->content = new stdClass;
+        $this->content->footer = '';
+        $this->content->text = '';
+
+        $csssupported = false;
+        if ($this->page->state == moodle_page::STATE_BEFORE_HEADER) {
+            $csssupported = true;
+            $this->page->requires->css('/blocks/css_theme_tool/styles.php');
+        }
+
+        if (isloggedin() && is_siteadmin()) {   // Show the block
+            $this->content->text .= html_writer::start_tag('div', array('class'=>'options'));
+            $this->content->text .= html_writer::empty_tag('input', array('type'=>'button', 'value'=>get_string('addrule', 'block_css_theme_tool'), 'class'=>'addrulebutton', 'disabled'=>'disabled'));
+            $this->content->text .= html_writer::empty_tag('input', array('type'=>'button', 'value'=>get_string('viewcss', 'block_css_theme_tool'), 'class'=>'viewcssbutton', 'disabled'=>'disabled'));
+            $this->content->text .= html_writer::empty_tag('input', array('type'=>'button', 'value'=>get_string('savechanges', 'block_css_theme_tool'), 'class'=>'savechangesbutton', 'disabled'=>'disabled'));
+            $this->content->text .= html_writer::empty_tag('input', array('type'=>'button', 'value'=>get_string('settings'), 'class'=>'settingsbutton', 'disabled'=>'disabled'));
+            $this->content->text .= html_writer::end_tag('div');
+
+            if (!$csssupported) {
+                $this->content->text .= html_writer::tag('div', get_string('cssnotsupported','block_css_theme_tool'), array('class'=>'cssnotsupported'));
+            }
+
+            $this->page->requires->strings_for_js(array(
+                'savenotification',
+                'cannotfindnode',
+                'errorcapturingclick',
+                'highlight',
+                'save',
+                'cancel',
+                'addnewstyle',
+                'purgerules',
+                'purgerulesdesc',
+                'purgerulesconfirm',
+                'purgerulescomplete',
+                'close',
+                'exportcss',
+                'exportcssdesc',
+                'exporttheme',
+                'exportthemedesc',
+                'showadvancedbodytags',
+                'showadvancedbodytagsdesc'
+            ), 'block_css_theme_tool');
+            $settings = array(
+                'fullbodytags' => (get_user_preferences('css_theme_tool_full_body_tags', false))
+            );
+            $this->page->requires->js_init_call('M.block_css_theme_tool.init', array($this->instance->id, css_theme_tool::get_rules_for_json(), $settings));
+            user_preference_allow_ajax_update('css_theme_tool_full_body_tags', PARAM_BOOL);
+        }
+
+        return $this->content;
+    }
+
+    public function send_file($context, $filearea, $itemid, $filepath, $filename) {
+        $fs = get_file_storage();
+        $file = $fs->get_file($context->id, $filearea, $itemid, $filepath, $filename);
+        if ($file) {
+            session_get_instance()->write_close(); // unlock session during fileserving
+            send_stored_file($file, 0, 0, true); // must force download - security!
+        } else {
+            send_file_not_found();
+        }
+    }
+}
Index: moodle/blocks/css_theme_tool/colourpicker_image.php
--- moodle/blocks/css_theme_tool/colourpicker_image.php No Base Revision
+++ moodle/blocks/css_theme_tool/colourpicker_image.php Locally New
@@ -0,0 +1,75 @@
+<?php
+
+/**
+ * Generates the colour picker image in for use with the colour picker
+ * component in module.js
+ *
+ * This is the coolest file in this block, it mathematically generates a PNG image
+ * in such a way that given XY coordinates JavaScript can work out what colour was
+ * clicked.
+ *
+ * @package   blocks
+ * @subpackage css_theme_tool
+ * @copyright 2010 Sam Hemelryk <sam.hemelryk@gmail.com>
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+/**
+ * Generates and sends the colour picker image
+ */
+function produce_colorpicker() {
+
+    $width = 766;
+    $height = 24;
+    $factor = 2;
+    $colour = array(255, 0, 0);
+
+    $matrices = array();//R,  G,  B
+    $matrices[] = array(  0,  1,  0);
+    $matrices[] = array( -1,  0,  0);
+    $matrices[] = array(  0,  0,  1);
+    $matrices[] = array(  0, -1,  0);
+    $matrices[] = array(  1,  0,  0);
+    $matrices[] = array(  0,  0, -1);
+    
+    $matrixcount = count($matrices);
+    $limit = floor($width/$matrixcount);
+    $heightbreak = floor($height/2);
+    
+    header("Content-type: image/png");
+    $image = imagecreatetruecolor($width, $height);
+    imagecolorallocate($image, 0, 0, 0);
+
+    for ($x = 0; $x < $width; $x++) {
+
+        $divisor = floor($x / $limit);
+        $matrix = $matrices[$divisor];
+
+        
+        $colour[0] += $matrix[0]*$factor;
+        $colour[1] += $matrix[1]*$factor;
+        $colour[2] += $matrix[2]*$factor;
+
+        for ($y=0;$y<$height;$y++) {
+            $pixel = $colour;
+
+            if ($y < $heightbreak) {
+                $pixel[0] += ((255-$pixel[0])/$heightbreak)*($heightbreak-$y);
+                $pixel[1] += ((255-$pixel[1])/$heightbreak)*($heightbreak-$y);
+                $pixel[2] += ((255-$pixel[2])/$heightbreak)*($heightbreak-$y);
+            } else if ($y > $heightbreak) {
+                $pixel[0] = ($height-$y)*($pixel[0]/$heightbreak);
+                $pixel[1] = ($height-$y)*($pixel[1]/$heightbreak);
+                $pixel[2] = ($height-$y)*($pixel[2]/$heightbreak);
+            }
+
+            imagesetpixel($image, $x, $y, imagecolorallocate($image, abs($pixel[0])%256, abs($pixel[1])%256, abs($pixel[2])%256));
+        }
+
+    }
+
+    imagepng($image);
+    imagedestroy($image);
+}
+
+produce_colorpicker();
Index: moodle/blocks/css_theme_tool/db/install.xml
--- moodle/blocks/css_theme_tool/db/install.xml No Base Revision
+++ moodle/blocks/css_theme_tool/db/install.xml Locally New
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<XMLDB PATH="blocks/css_theme_tool/db" VERSION="20100329" COMMENT="XMLDB file for Moodle blocks/css_theme_tool"
+    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+    xsi:noNamespaceSchemaLocation="../../../lib/xmldb/xmldb.xsd"
+>
+  <TABLES>
+    <TABLE NAME="block_css_theme_tool" COMMENT="Stores CSS rules created with the CSS theme tool" NEXT="block_css_theme_tool_styles">
+      <FIELDS>
+        <FIELD NAME="id" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" SEQUENCE="true" NEXT="selector"/>
+        <FIELD NAME="selector" TYPE="text" LENGTH="small" NOTNULL="true" SEQUENCE="false" COMMENT="The CSS selector for this rule" PREVIOUS="id" NEXT="timemodified"/>
+        <FIELD NAME="timemodified" TYPE="int" LENGTH="10" NOTNULL="false" UNSIGNED="true" SEQUENCE="false" PREVIOUS="selector" NEXT="userid"/>
+        <FIELD NAME="userid" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" SEQUENCE="false" PREVIOUS="timemodified"/>
+      </FIELDS>
+      <KEYS>
+        <KEY NAME="primary" TYPE="primary" FIELDS="id"/>
+      </KEYS>
+    </TABLE>
+    <TABLE NAME="block_css_theme_tool_styles" COMMENT="Styles for rules in block_css_theme_tool" PREVIOUS="block_css_theme_tool">
+      <FIELDS>
+        <FIELD NAME="id" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" SEQUENCE="true" NEXT="ruleid"/>
+        <FIELD NAME="ruleid" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" SEQUENCE="false" COMMENT="Relates to the id of the rule this style is for" PREVIOUS="id" NEXT="name"/>
+        <FIELD NAME="name" TYPE="char" LENGTH="255" NOTNULL="true" SEQUENCE="false" COMMENT="The name of the CSS style" PREVIOUS="ruleid" NEXT="value"/>
+        <FIELD NAME="value" TYPE="text" LENGTH="small" NOTNULL="true" SEQUENCE="false" COMMENT="The value for this style" PREVIOUS="name"/>
+      </FIELDS>
+      <KEYS>
+        <KEY NAME="primary" TYPE="primary" FIELDS="id"/>
+      </KEYS>
+    </TABLE>
+  </TABLES>
+</XMLDB>
Index: moodle/blocks/css_theme_tool/db/upgrade.php
--- moodle/blocks/css_theme_tool/db/upgrade.php No Base Revision
+++ moodle/blocks/css_theme_tool/db/upgrade.php Locally New
@@ -0,0 +1,29 @@
+<?php
+
+// This file keeps track of upgrades to
+// the admin block
+//
+// Sometimes, changes between versions involve
+// alterations to database structures and other
+// major things that may break installations.
+//
+// The upgrade function in this file will attempt
+// to perform all the necessary actions to upgrade
+// your older installtion to the current version.
+//
+// If there's something it cannot do itself, it
+// will tell you what you need to do.
+//
+// The commands in here will all be database-neutral,
+// using the methods of database_manager class
+//
+// Please do not forget to use upgrade_set_timeout()
+// before any action that may take longer time to finish.
+
+function xmldb_block_css_theme_tool_upgrade($oldversion) {
+    global $CFG, $DB;
+
+    return $result;
+}
+
+
Index: moodle/blocks/css_theme_tool/lang/en/block_css_theme_tool.php
--- moodle/blocks/css_theme_tool/lang/en/block_css_theme_tool.php No Base Revision
+++ moodle/blocks/css_theme_tool/lang/en/block_css_theme_tool.php Locally New
@@ -0,0 +1,35 @@
+<?php
+
+/**
+ * This file contains language strings used in the CSS theme tool block
+ *
+ * @since 2.0
+ * @package blocks
+ * @subpackage css_theme_tool
+ * @copyright 2010 Sam Hemelryk <sam.hemelryk@gmail.com>
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+$string['addnewstyle'] = 'Click to add a new style';
+$string['addrule'] = 'Add rule';
+$string['pluginname'] = 'CSS theme tool';
+$string['cancel'] = 'Cancel';
+$string['cannotfindnode'] = 'Unable to find the element of the rule you wish to edit.';
+$string['close'] = 'Close';
+$string['cssnotsupported'] = 'Your theme does not support the use of external CSS from this tool.<hr />Only Admin users will be able to see the rules you make.';
+$string['errorcapturingclick'] = 'Error capturing click: ';
+$string['exportcss'] = 'Export CSS file';
+$string['exportcssdesc'] = 'This will export the CSS rules you have created as a CSS file that you can add into any theme you choose.';
+$string['exporttheme'] = 'Export as theme';
+$string['exportthemedesc'] = 'This will export the CSS rules you have created as a complete theme based on the currently active theme you are using.';
+$string['highlight'] = 'Highlight';
+$string['purgerules'] = 'Purge rules';
+$string['purgerulesconfirm'] = 'Are you sure you want to purge all of the rules you have created?';
+$string['purgerulescomplete'] = 'All rules have been successfully deleted';
+$string['purgerulesdesc'] = 'Purging will delete all of your rules. This should only be done after you save the CSS to a theme or have exported it from this tool.';
+$string['save'] = 'Save';
+$string['savechanges'] = 'Save changes';
+$string['savenotification'] = 'Changes will not be saved until you click `Save Changes` from the CSS toolbox block';
+$string['showadvancedbodytags'] = 'Show advanced body tag information';
+$string['showadvancedbodytagsdesc'] = 'Checking this box will show absolutely all of the classes that are applied to the body tag rather than the simplified set you see by default';
+$string['viewcss'] = 'View CSS';
Index: moodle/blocks/css_theme_tool/lib.php
--- moodle/blocks/css_theme_tool/lib.php No Base Revision
+++ moodle/blocks/css_theme_tool/lib.php Locally New
@@ -0,0 +1,367 @@
+<?php
+
+/**
+ * Contains all of the classes used by the CSS theme tool
+ *
+ * @package   blocks
+ * @subpackage css_theme_tool
+ * @copyright 2010 Sam Hemelryk <sam.hemelryk@gmail.com>
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+/**
+ * Main CSS theme tool class
+ *
+ * This class is made up mostly of static methods that instantiae itself and do
+ * all of the work. This saves on writing code for those who wish to use it.
+ *
+ * @package   blocks
+ * @subpackage css_theme_tool
+ * @copyright 2010 Sam Hemelryk <sam.hemelryk@gmail.com>
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class css_theme_tool {
+
+    /**
+     * An array of rules that are stored in the database
+     * 
+     * @var array
+     */
+    protected $rules = null;
+
+    /**
+     * Gets all of the rules associated with the CSS theme tool
+     * 
+     * @global moodle_database $DB
+     * @return array
+     */
+    public function get_rules() {
+        global $DB;
+        if ($this->rules === null) {
+            $this->rules = array();
+            $rules = $DB->get_records('block_css_theme_tool');
+            if (count($rules)) {
+                $styles = $DB->get_records('block_css_theme_tool_styles');
+                foreach ($rules as $key=>$rule) {
+                    $this->rules[$key] = new css_theme_tool_rule();
+                    $this->rules[$key]->load($rule->id, $rule, $styles);
+                }
+            }
+        }
+        return $this->rules;
+    }
+
+    /**
+     * Gets all of the rules for this css theme tool and returns them in
+     * an array suitable for return as JSON
+     * @return array
+     */
+    public static function get_rules_for_json() {
+        $tool = new self();
+        $rules = $tool->get_rules();
+        foreach ($rules as &$rule) {
+            foreach ($rule->styles as &$style) {
+                $style->rule = null;
+            }
+        }
+        return $rules;
+    }
+
+    /**
+     * Output the rules as a CSS file
+     */
+    public static function output_css() {
+        header('Content-Type: text/css');
+        $tool = new self();
+        $rules = $tool->get_rules();
+        foreach ($rules as $rule) {
+            echo $rule->selector.' {';
+            foreach ($rule->styles as $style) {
+                echo $style->name.':'.$style->value.';';
+            }
+            echo "}\n";
+        }
+        die();
+    }
+
+    /**
+     * Caches the CSS and returns the file so it can be exported
+     * @return stored_file
+     */
+    public static function export_css($blockcontext) {
+        $tool = new self();
+        $rules = $tool->get_rules();
+        $css = "/** Created by CSS theme tool **/\n";
+        foreach ($rules as $rule) {
+            $css .= $rule->selector.' {';
+            foreach ($rule->styles as $style) {
+                $css .= $style->name.':'.$style->value.';';
+            }
+            $css .= "}\n";
+        }
+
+        $cssfile = array(
+            'contextid'=>$blockcontext->id,
+            'filearea'=>'block_css_theme_tool',
+            'itemid'=>$blockcontext->instanceid,
+            'filepath'=>'/',
+            'filename'=>'css_theme_tool_export_'.date('Ymd').'.css',
+            'timecreated'=>time(),
+            'timemodified'=>time());
+        $fs = get_file_storage();
+        if ($file = $fs->get_file($cssfile['contextid'], $cssfile['filearea'], $cssfile['itemid'], $cssfile['filepath'], $cssfile['filename'])) {
+            $file->delete();
+        }
+        return $fs->create_file_from_string($cssfile, $css);
+    }
+
+    /**
+     * Updates the rules stored in the database with those you have created
+     *
+     * @global moodle_database $DB
+     * @param array $rules
+     */
+    public function update_rules($rules) {
+        global $DB;
+        
+        $oldrules = $this->get_rules();
+        $rulestodelete = array();
+
+        foreach ($oldrules as $oldrule) {
+            foreach ($rules as $rule) {
+                if ($rule->selector == $oldrule->selector) {
+                    $rule->id = $oldrule->id;
+                    continue 2;
+                }
+            }
+            $rulestodelete[] = $oldrule->id;
+        }
+
+        if (count($rulestodelete) > 0) {
+            $DB->delete_records_list('block_css_theme_tool', 'id', $rulestodelete);
+            $DB->delete_records_list('block_css_theme_tool_styles', 'ruleid', $rulestodelete);
+        }
+
+        if (count($rules) > 0) {
+            foreach ($rules as $rule) {
+                $rule->save();
+            }
+        }
+        $this->rules = $rules;
+    }
+
+    /**
+     * Updates the rules you have created via JavaScript
+     *
+     * @param array $ajaxrules
+     * @param array $ajaxstyles
+     */
+    public static function update_via_ajax($ajaxrules, $ajaxstyles) {
+        $rules = array();
+        foreach ($ajaxrules as $key=>$selector) {
+            $rule = new css_theme_tool_rule();
+            $rule->create($selector, explode('@@@', $ajaxstyles[$key]));
+            $rules[] = $rule;
+        }
+        $tool = new self();
+        $tool->update_rules($rules);
+    }
+
+    /**
+     * Deletes all rules that have been stored in the database
+     * @global moodle_database $DB
+     */
+    public static function purge_rules() {
+        global $DB;
+        $DB->delete_records('block_css_theme_tool');
+        $DB->delete_records('block_css_theme_tool_styles');
+    }
+}
+
+/**
+ * Class representing a CSS rule
+ *
+ * @package   blocks
+ * @subpackage css_theme_tool
+ * @copyright 2010 Sam Hemelryk <sam.hemelryk@gmail.com>
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class css_theme_tool_rule {
+
+    /**
+     * The ID of the rule in the database (null if not stored yet)
+     * @var int
+     */
+    public $id = null;
+    /**
+     * The selector as a string
+     * @var string
+     */
+    public $selector;
+    /**
+     * The time this rule was last modified
+     * @var int
+     */
+    public $timemodified;
+    /**
+     * The ID of the user who created this rule
+     * @var int
+     */
+    public $userid;
+    /**
+     * All of the styles associated with this object {@see css_theme_tool_style}
+     * @var array
+     */
+    public $styles = array();
+
+    /**
+     * Loads the rule from the database
+     * 
+     * @global moodle_database $DB
+     * @param int $id
+     * @param stdClass $rule
+     * @param array $styles
+     * @return bool
+     */
+    public function load($id, stdClass $rule=null, array $styles = null) {
+        global $DB;
+        if ($rule == null) {
+            $rule = $DB->get_record('block_css_theme_tool', array('id'=>$id), '*', MUST_EXIST);
+        }
+        $this->id = $id;
+        $this->selector = $rule->selector;
+        $this->timemodified = $rule->timemodified;
+        $this->userid = $rule->userid;
+        $this->styles = css_theme_tool_style::load_for_rule($this, $styles);
+        return true;
+    }
+    /**
+     * Creates this object from the given selector and rules
+     * @param string $selector
+     * @param array $styles
+     */
+    public function create($selector, array $styles) {
+        global $USER;
+        $this->id = null;
+        $this->selector = $selector;
+        $this->timemodified = time();
+        $this->userid = $USER->id;
+        $this->styles = css_theme_tool_style::create_for_rule($this, $styles);
+    }
+    /**
+     * Inserts or Updates the rule in the database
+     * @global moodle_database $DB
+     */
+    public function save() {
+        global $DB;
+        if ($this->id !== null) {
+            $DB->update_record('block_css_theme_tool', $this);
+        } else {
+            // Creating a new DB record
+            $this->id = $DB->insert_record('block_css_theme_tool', $this);
+        }
+
+        $DB->delete_records('block_css_theme_tool_styles', array('ruleid'=>$this->id));
+        foreach ($this->styles as $style) {
+            $style->ruleid = $this->id;
+            $style->save();
+        }
+    }
+}
+
+/**
+ * Class representing a style attribute
+ *
+ * @package   blocks
+ * @subpackage css_theme_tool
+ * @copyright 2010 Sam Hemelryk <sam.hemelryk@gmail.com>
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class css_theme_tool_style {
+    /**
+     * The ID in the database (null if not saved)
+     * @var int
+     */
+    public $id;
+    /**
+     * The ID of the rule this style belongs too
+     * @var int
+     */
+    public $ruleid;
+    /**
+     * The rule this style belongs too
+     * @var css_theme_tool_rule
+     */
+    public $rule;
+    /**
+     * The name of this style
+     * @var string
+     */
+    public $name;
+    /**
+     * The value for this style
+     * @var string
+     */
+    public $value;
+
+    /**
+     * Loads all of the styles for the given rule
+     *
+     * @global moodle_database $DB
+     * @param css_theme_tool_rule $rule
+     * @param array $dbstyles
+     * @return array
+     */
+    public static function load_for_rule(css_theme_tool_rule $rule, array $dbstyles=null) {
+        global $DB;
+        $styles = Array();
+        if ($dbstyles == null) {
+            $dbstyles = $DB->get_records('block_css_theme_tool_styles', array('ruleid'=>$rule->id));
+        }
+        foreach ($dbstyles as $dbstyle) {
+            if ($dbstyle->ruleid !== $rule->id) {
+                continue;
+            }
+            $style = new self();
+            $style->id = $dbstyle->id;
+            $style->ruleid = $rule->id;
+            $style->rule = $rule;
+            $style->name = $dbstyle->name;
+            $style->value = $dbstyle->value;
+            $styles[] = $style;
+        }
+        return $styles;
+    }
+    /**
+     * Creates new styles for the given rule
+     *
+     * @param css_theme_tool_rule $rule
+     * @param array $newstyles
+     * @return array
+     */
+    public static function create_for_rule(css_theme_tool_rule $rule, array $newstyles) {
+        $styles = Array();
+        foreach ($newstyles as $newstyle) {
+            if (preg_match('/^([^:]+)\s*:\s*([^;]+);?$/', $newstyle, $bits)) {
+                $style = new self();
+                $style->id = null;
+                $style->ruleid = $rule->id;
+                $style->rule = $rule;
+                $style->name = $bits[1];
+                $style->value = $bits[2];
+                $styles[] = $style;
+            }
+        }
+        return $styles;
+    }
+    /**
+     * Saves this style to the database
+     *
+     * @global moodle_database $DB
+     */
+    public function save() {
+        global $DB;
+        // Creating a new DB record
+        $this->id = $DB->insert_record('block_css_theme_tool_styles', $this);
+    }
+}
Index: moodle/blocks/css_theme_tool/module.js
--- moodle/blocks/css_theme_tool/module.js No Base Revision
+++ moodle/blocks/css_theme_tool/module.js Locally New
@@ -0,0 +1,1364 @@
+/**
+ * CSS theme tool JavaScript module
+ *
+ * This JavaScript file contains all of the client side logic for the CSS theme
+ * tool. Of course as this block relies 100% on JavaScript is it relativiliy advanced.
+ * It requires YUI 3.1.0 which is the default being shipped with Moodle 2.0 as of
+ * its beta period.
+ * 
+ * @package   blocks
+ * @subpackage css_theme_tool
+ * @copyright 2010 Sam Hemelryk <sam.hemelryk@gmail.com>
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+/**
+ * Namespace for the CSS theme tool
+ */
+M.block_css_theme_tool = {
+    /**
+     * The currently active instance of the CSS theme tool
+     * @var {css_theme_tool}
+     */
+    instance : null,
+    /**
+     * @param {YUI} Y A yui instance to use
+     * @param {int} instanceid Instance ID
+     * @param {array} existingrules An array of existing rule objects
+     * @param {object} settings A settings object
+     */
+    init : function(Y, instanceid, existingrules, settings) {
+        // Load the required YUI modules into the Y object for use here
+        Y.use('node','overlay', 'event', 'event-key', 'event-mouseenter', 'io', function() {
+            // The CSS theme tool object
+            // Associates itself with the CSS theme tool block
+            var css_theme_tool = {
+                /**
+                 * Used by all components to display their content nicely
+                 * @var {YUI.Overlay}
+                 */
+                overlay : null,
+                /**
+                 * A <style> tag that gets inserted into the head. This is how
+                 * we can show our live changes
+                 * @var {Y.Node}
+                 */
+                styletag : null,
+                /**
+                 * An array containing rule objects
+                 * @var {array}
+                 */
+                rules : [],
+                /**
+                 * Tracks whether the user has been informed about the how deleting
+                 * rules work, and that you need to save
+                 * @var {bool}
+                 */
+                explaineddelete : false,
+                /**
+                 * The block instance ID
+                 * @var {int}
+                 */
+                instanceid : instanceid,
+                /**
+                 * Stores the configuration settings for this instance
+                 * @var {object}
+                 */
+                cfg : {
+                    /**
+                     * Set to true if you want the selector to display all options
+                     * for the body tag. Defaults to false
+                     * @var {bool}
+                     */
+                    showadvancedbodytags : settings.fullbodytags | false
+                },
+                /**
+                 * Stores useful nodes to the tool
+                 * @var {object}
+                 */
+                nodes : {
+                    /**
+                     * The save button in the block
+                     * @var {Y.Node}
+                     */
+                    savechangesbutton : null
+                },
+                /**
+                 * Stores component instances
+                 * @var {object}
+                 */
+                components : {
+                    /**
+                     * An instance of the CSS builder
+                     * @var {M.block_css_theme_tool.css_builder}
+                     */
+                    cssbuilder : null,
+                    /**
+                     * An instance of the CSS viewer
+                     * @var {M.block_css_theme_tool.css_viewer}
+                     */
+                    cssviewer : null,
+                    /**
+                     * An instance of the settings manager
+                     * @var {M.block_css_theme_tool.settings_manager}
+                     */
+                    settingsmanager : null
+                },
+                /**
+                 * Initliases the CSS theme tool
+                 */
+                init : function() {
+                    // Publish the changed event
+                    this.publish('cssthemetool:changed');
+
+                    // Make sure all existing rules are moved into this instance
+                    for (var i in existingrules) {
+                        var rule = existingrules[i];
+                        var rulestyles = [];
+                        for (var j in rule.styles) {
+                            rulestyles.push(rule.styles[j].name+': '+rule.styles[j].value+';');
+                        }
+                        this.rules.push({selector:rule.selector,styles:rulestyles});
+                    }
+                    // Instantiate the CSS builder
+                    this.components.cssbuilder = new M.block_css_theme_tool.css_builder(Y, this);
+                    // Instantiate the CSS viewer
+                    this.components.cssviewer = new M.block_css_theme_tool.css_viewer(Y, this);
+                    // Instantiate the settings manager
+                    this.components.settingsmanager = new M.block_css_theme_tool.settings_manager(Y, this);
+
+                    // Instantiate the overlay
+                    this.overlay = new Y.Overlay({
+                        bodyContent : 'temp',
+                        width: '80%',
+                        height : '80%',
+                        visible : false,
+                        zIndex : 500
+                    });
+                    // Render it on the body (less chance of clipping)
+                    this.overlay.render(Y.one(document.body));
+                    // Make it fixed so it centers nicely
+                    this.overlay.bodyNode.ancestor('.yui3-overlay').setStyle('position', 'fixed');
+                    // Center it
+                    this.overlay.bodyNode.ancestor('.yui3-overlay').setStyle('margin', '100px 10%');
+                    // Add our custom class
+                    this.overlay.bodyNode.addClass('css_builder_overlay');
+
+                    // Create the style tage and add it to the head of the document
+                    this.styletag = Y.Node.create('<style type="text/css"></style>');
+                    Y.one('head').append(this.styletag);
+
+                    // Process all rules... adds them to the stylesheet we just created
+                    this.process_rules_to_stylesheet();
+
+                    // Remove the link to the tools styles.php page otherwise we
+                    // won't see rule deletions of modifications!
+                    Y.all('link').each(function(link){
+                        if (link.getAttribute('rel')=='stylesheet' && link.getAttribute('href').match(/\/blocks\/css_theme_tool\/styles\.php$/)) {
+                            link.remove();
+                        }
+                    }, this);
+
+                    // Fetch and store the save button from the block
+                    this.nodes.savechangesbutton = Y.one('.block_css_theme_tool input.savechangesbutton');
+                    // Attach the save_rules event to the buttons click
+                    this.nodes.savechangesbutton.on('click', this.save_rules, this);
+                    // On a change event for the tool re-enable the button
+                    this.on('cssthemetool:changed', function(){
+                        this.removeAttribute('disabled');
+                    }, this.nodes.savechangesbutton);
+                },
+                /**
+                 * Adds a CSS rule
+                 * @param {string} selector
+                 * @param {array} styles
+                 * @return {bool}
+                 */
+                add_rule : function(selector, styles) {
+                    var added = false;
+                    for (var i in this.rules) {
+                        if (this.rules[i].selector == selector) {
+                            this.rules[i] = {selector:selector,styles:styles};
+                            added = true;
+                            break;
+                        }
+                    }
+                    if (!added) {
+                        this.rules.push({selector:selector,styles:styles});
+                    }
+                    this.fire('cssthemetool:changed');
+                    return this.process_rules_to_stylesheet();
+                },
+                /**
+                 * Gets a rule object given a selector
+                 * @param {string} selector
+                 * @return {bool}
+                 */
+                get_rule : function(selector) {
+                    // Trim the selector first up
+                    selector = selector.replace(/^\s+|\s+$/g,'');
+                    for (var i in this.rules) {
+                        // Compare each rule
+                        var ruleselector = this.rules[i].selector.replace(/^\s+|\s+$/g,'');
+                        if (ruleselector == selector) {
+                            // Return the located rule
+                            return this.rules[i];
+                        }
+                    }
+                    // Return false by default
+                    return false;
+                },
+                /**
+                 * Deletes the rule with the given selector
+                 * @param {string} selector
+                 * @return {bool}
+                 */
+                delete_rule : function(selector) {
+                    selector = selector.replace(/^\s+|\s+$/g,'');
+                    for (var i in this.rules) {
+                        var ruleselector = this.rules[i].selector.replace(/^\s+|\s+$/g,'');
+                        if (ruleselector == selector) {
+                            delete this.rules[i];
+                            this.process_rules_to_stylesheet();
+                            if (!this.explaineddelete) {
+                                alert(M.str.block_css_theme_tool.savenotification);
+                                this.explaineddelete = true;
+                                this.fire('cssthemetool:changed');
+                            }
+                            return true;
+                        }
+                    }
+                    return false;
+                },
+                /**
+                 * Edits the rule with the given selector
+                 * @param {string} selector
+                 */
+                edit_rule : function(selector) {
+                    selector = selector.replace(/^\s+|\s+$/g,'');
+                    var target = Y.one(selector);
+                    if (target) {
+                        this.components.cssbuilder.show(target);
+                    } else {
+                        alert(M.str.block_css_theme_tool.cannotfindnode);
+                    }
+                },
+                /**
+                 * Saves all of the rules to the database
+                 */
+                save_rules : function() {
+                    var data = [];
+                    data.push('action=save');
+                    data.push('sesskey='+M.cfg.sesskey);
+                    for (var i in this.rules) {
+                        var rule = this.rules[i];
+                        data.push('rules['+i+']='+encodeURIComponent(rule.selector));
+                        data.push('styles['+i+']='+encodeURIComponent(rule.styles.join('@@@')));
+                    }
+                    this.nodes.savechangesbutton.setAttribute('disabled', true);
+                    Y.io(M.cfg.wwwroot+'/blocks/css_theme_tool/ajax.php', {
+                        method : 'POST',
+                        data : data.join('&'),
+                        on: {
+                            complete:this.save_rules_callback
+                        },
+                        context:this
+                    });
+                },
+                /**
+                 * Callback for the save_rules method could be made to do something
+                 * usefull.
+                 * @param {int} transactionid
+                 * @param {object} outcome
+                 * @param {mixed} args
+                 */
+                save_rules_callback : function(transactionid, outcome, args) {
+                    //alert(transactionid+"\n"+outcome.responseText);
+                },
+                /**
+                 * Purges all rules, deleting them from the database
+                 */
+                purge_rules : function() {
+                    // Confirm the action before taking it
+                    if (confirm(M.str.block_css_theme_tool.purgerulesconfirm)) {
+                        // Fire an ajax request to purge the rules from the database
+                        Y.io(M.cfg.wwwroot+'/blocks/css_theme_tool/ajax.php', {
+                            method : 'POST',
+                            data : 'action=purge&sesskey='+M.cfg.sesskey,
+                            on: {
+                                complete:function(){
+                                    this.rules = [];
+                                    this.process_rules_to_stylesheet();
+                                    alert(M.str.block_css_theme_tool.purgerulescomplete);
+                                }
+                            },
+                            context:this
+                        });
+                    }
+                },
+                /**
+                 * Adds each rule to the stylesheet for a visable result
+                 * @return {bool}
+                 */
+                process_rules_to_stylesheet : function() {
+                    var rules = [];
+                    for (var i in this.rules) {
+                        rules.push(this.rules[i].selector+' {'+this.rules[i].styles.join('')+'}');
+                    }
+                    this.styletag.set('innerHTML', rules.join("\n"));
+                    return true;
+                },
+                /**
+                 * Hides all of the compontents
+                 */
+                hide_components : function() {
+                    for (var i in this.components) {
+                        this.components[i].hide();
+                    }
+                },
+                /**
+                 * Exports a CSS file
+                 */
+                export_css : function() {
+                    Y.io(M.cfg.wwwroot+'/blocks/css_theme_tool/ajax.php', {
+                        method : 'POST',
+                        data : 'id='+this.instanceid+'&action=exportcss&sesskey='+M.cfg.sesskey,
+                        on: {
+                            complete:function(transactionid, outcome, args){
+                                var popup = window.open(outcome.responseText, 'csstoolexport');
+                                if (!popup) {
+                                    alert('Popups must be allowed for this site in order for export to work');
+                                }
+                            }
+                        },
+                        context:this
+                    });
+                }
+            }
+            // Give the css_theme_tool the power of the Y.EventTarget
+            Y.augment(css_theme_tool, Y.EventTarget);
+            // Initliase the tool!
+            css_theme_tool.init();
+            // and store it just incase
+            M.block_css_theme_tool.instance = css_theme_tool;
+        });
+    }
+}
+/**
+ * CSS theme tool component that allows you to view the CSS you have created.
+ *
+ * This component also provides hooks to edit and delete CSS rules you have created.
+ *
+ * @class
+ * @constructor
+ * @param {YUI} Y
+ * @param {css_theme_tool} themetool
+ */
+M.block_css_theme_tool.css_viewer = function(Y, themetool) {
+    this.Y = Y;
+    this.themetool = themetool;
+    // Attach the event to display the CSS
+    Y.one('.block_css_theme_tool input.viewcssbutton').removeAttribute('disabled').on('click', this.show, this);
+}
+/**
+ * Properties of css_viewer
+ */
+M.block_css_theme_tool.css_viewer.prototype = {
+    /**
+     * The YUI instance to use with this block
+     * @var {YUI}
+     */
+    Y : null,
+    /**
+     * The themetool instance this component belongs to.
+     * @var {css_theme_tool}
+     */
+    themetool : null,
+    /**
+     * True if the component is being shown
+     * @var {bool}
+     */
+    shown : false,
+    /**
+     * The event for closing the overlay
+     * @var {Y.Event}
+     */
+    eventcloseoverlay : null,
+    /**
+     * Shows the CSS viewer component
+     */
+    show : function () {
+        // First hide all other components (they may be using the dialogue)
+        this.themetool.hide_components();
+        // Create the content div for the CSS viewer
+        var content = this.Y.Node.create('<div class="viewcssbox"></div>');
+        for (var i in this.themetool.rules) {
+            // Create the HTML for each rule so we can show it
+            var r = this.themetool.rules[i];
+            var selector = this.Y.Node.create('<div class="cssrule"></div>');
+            var editicon = this.Y.Node.create('<img src="'+M.util.image_url('t/edit', 'moodle')+'" alt="edit" style="width:11px;height:11px;" class="icon" />');
+            var deleteicon = this.Y.Node.create('<img src="'+M.util.image_url('t/delete', 'moodle')+'" alt="delete" style="width:11px;height:11px;" class="icon" />');
+            selector.append(editicon);
+            selector.append(deleteicon);
+            selector.append(this.Y.Node.create('<span class="selector">'+r.selector+'</span> {'+r.styles.join('')+'}'));
+            // Add it to the content div
+            content.append(selector);
+            // Attach the edit event
+            editicon.on('click', this.edit_rule, this, r);
+            // Attach the delete event
+            deleteicon.on('click', this.delete_rule, this, r);
+        }
+        // Setup the theme tools overlays as we require
+        this.themetool.overlay.set('bodyContent', content);
+        this.themetool.overlay.show();
+        // Attach the close event to a click anywhere on the overlay
+        this.eventcloseoverlay = this.themetool.overlay.bodyNode.on('click', this.hide, this);
+        this.shown = true;
+    },
+    /**
+     * Hides the CSS viewer component if it is being shown
+     */
+    hide : function () {
+        if (this.shown) {
+            this.eventcloseoverlay.detach();
+            this.themetool.overlay.hide();
+            this.shown = false;
+        }
+    },
+    /**
+     * Sets up to edit the selected rule
+     *
+     * @param {Y.Event} e
+     * @param {object} rule
+     */
+    edit_rule : function(e, rule) {
+        this.themetool.edit_rule(rule.selector);
+    },
+    /**
+     * Sets up to delete the selected rule
+     * 
+     * @param {Y.Event} e
+     * @param {object} rule
+     */
+    delete_rule : function(e, rule) {
+        this.themetool.delete_rule(rule.selector);
+    }
+}
+/**
+ * CSS builder component
+ *
+ * This component is the core component in the CSS theme tool.
+ * It allows the user to add rules by clicking on an element, and provides a nice
+ * interface for creating rules.
+ *
+ * @class
+ * @constructor
+ * @param {YUI} Y
+ * @param {css_theme_tool} themetool
+ */
+M.block_css_theme_tool.css_builder = function(Y, themetool) {
+    // Store the arguments
+    this.Y = Y;
+    this.themetool = themetool;
+    // Create the mask. This will be used to capture click events
+    this.mask = new this.Y.Overlay({
+        bodyContent : ' ',
+        width: '100%',
+        height: '100%',
+        visible : false,
+        xy : [0,0]
+    });
+    // Render it to the body, it needs to be here to avoid clipping rules
+    this.mask.render(this.Y.one(document.body));
+    // Prepare the overlay div as a modal
+    var modal = this.mask.bodyNode.ancestor('.yui3-overlay');
+    if (modal) {
+        // Has to be completely inivisble
+        modal.setStyle('opacity', '0.0');
+        // Crosshair makes it obvious whats going on
+        modal.setStyle('cursor', 'crosshair');
+        // Has to be fixed we don't want it to move or grow
+        modal.setStyle('position', 'fixed');
+    }
+    // Generate the core content to increase reponse time during mouse events and
+    // display calls
+    this.init_generate_controls();
+    // Enable the addrule button and attach the click event
+    this.Y.one('.block_css_theme_tool input.addrulebutton').removeAttribute('disabled').on('click', this.capture_next_click, this)
+}
+/**
+ * Properties for the css builder
+ */
+M.block_css_theme_tool.css_builder.prototype = {
+    /**
+     * The YUI instance for this tool
+     * @var {YUI}
+     */
+    Y : null,
+    /**
+     * True is the component is being shown
+     * @var {bool}
+     */
+    shown : false,
+    /**
+     * The CSS theme tool this component belong too
+     * @var css_theme_tool
+     */
+    themetool : null,
+    /**
+     * The mask overlay to capture mouse events
+     * @var {Y.Overlay}
+     */
+    mask : null,
+    /**
+     * An array of classes not to select by default.... this list will grow
+     * @var {array}
+     */
+    nondefaultclasses : ['region-content','yui3-js-enabled'],
+    /**
+     * The number of selectors currently being shown
+     * @var {int}
+     */
+    selectorcount : 0,
+    /**
+     * The mouse move event reference when in use
+     * @var {Y.Event}
+     */
+    eventmousemove : null,
+    /**
+     * The mouse capture click reference when in use
+     * @var {Y.Event}
+     */
+    eventcaptureclick : null,
+    /**
+     * The node that contains all of the controls for this component... a buffer really.
+     * @var {Y.Node}
+     */
+    controlsnode : null,
+    /**
+     * When called this sets the component up to capture the next click to add a rule
+     */
+    capture_next_click : function() {
+        // Hide all components, I don't want people to style them ;)
+        this.themetool.hide_components();
+        // Show the mask
+        this.mask.show();
+        // Capture all body clicks
+        this.eventcaptureclick = this.mask.bodyNode.on('click', this.capture_click, this);
+        // Create a set of divs to highlight the element under the mouse
+        var div = this.Y.one('#mousemovetrackdiv') || (function(Y){
+            var div = Y.Node.create('<div id="mousemovetrackdiv"><div class="top"></div><div class="bottom"></div><div class="left"></div><div class="right"></div></div>');
+            div.setStyle('opacity', 0.4);
+            Y.one(document.body).append(div);
+            return div;
+        })(this.Y);
+        div.setStyle('visibility', 'visible');
+        // Attach the highlight event
+        this.eventmousemove = this.Y.one(document.body).on('mousemove', this.highlight_cell_below_event, this, div);
+    },
+    /**
+     * Captures a click and allows the user to add a rule for the element they clicked on
+     * @param {Y.Event} e
+     */
+    capture_click : function(e) {
+        // Stop the event immediatly!
+        e.halt();
+        // Detach the mouse move event its no longer needed
+        this.eventmousemove.detach();
+        // Detach the capture click event we're doing that now
+        this.eventcaptureclick.detach();
+        // Hide the highlight divs
+        this.Y.one('#mousemovetrackdiv').setStyle('visibility', 'hidden');
+        // Hide the mask
+        this.mask.hide();
+        try {
+            // Attempt to get the element that exists below the click event
+            var target = this.Y.one(document.elementFromPoint(e.clientX, e.clientY));
+        } catch (ex) {
+            // Can't capture the event... please upgrade to a real browser!
+            alert(M.str.block_css_theme_tool.errorcapturingclick+ex.message);
+            return;
+        }
+        this.show(target);
+    },
+    /**
+     * Highlights the cell below the mouse
+     * @param {Y.Event} e
+     * @param {Y.Node} div
+     */
+    highlight_cell_below_event : function(e, div) {
+        // Hide the mask so we don't get this node back
+        this.mask.hide();
+        // Hide the divs so we don't get this node back
+        div.setStyle('visibility', 'hidden');
+        // Get the event below the click
+        var target = this.Y.one(document.elementFromPoint(e.clientX, e.clientY));
+        // Show the divs again
+        div.setStyle('visibility', 'visible');
+        // Show the mask again
+        this.mask.show();
+        // Get the position of the target node
+        var pos = target.getXY();
+        // Get the scroll position .... grr
+        var scrollY = this.Y.get('window').get('scrollY');
+        // Get the window width and hieght
+        var winpos = [this.Y.get(document.body).get('winWidth'), this.Y.get(document.body).get('winHeight')];
+        // Change the highlight div dimensions to highlight the node being hovered over
+        div.one('.top').setStyle('height', (pos[1]-scrollY)+'px');
+        div.one('.left').setStyle('width', pos[0]+'px');
+        div.one('.bottom').setStyle('height', (winpos[1]-pos[1]-target.get('offsetHeight')+scrollY)+'px');
+        div.one('.right').setStyle('width', (winpos[0]-pos[0]-target.get('offsetWidth'))+'px');
+    },
+    /**
+     * Highlights all of the cells that the current rule selector is going to be
+     * applied to.
+     * @param {Y.Event} e
+     */
+    highlight_affected_cells : function(e) {
+        // Make the overlay opace so we can see what is being highlighted
+        this.themetool.overlay.bodyNode.setStyle('opacity', 0.2);
+        // Create a div to contain all highlight divs
+        var container = this.Y.Node.create('<div class="cssbuilder_highlight_container"></div>');
+        // Attach it to the body to avoid clipping
+        this.Y.one(document.body).append(container);
+        // Add a body watch to make the highlighting obvious
+        container.append(this.Y.Node.create('<div class="cssbuilder_bodywash"></div>'));
+
+        // For each node found by the selector create a div that has the same position
+        // and dimensions as the located node. Add it to the highlight container.
+        // This new div is the highlight!
+        this.Y.all(this.get_selected_selector()).each(function(node){
+            var highlight = this.Y.Node.create('<div class="cssbuilder_cellhighlight"></div>');
+            highlight.setXY(node.getXY());
+            highlight.setStyle('position', 'absolute');
+            highlight.setStyle('width', node.get('offsetWidth')+'px');
+            highlight.setStyle('height', node.get('offsetHeight')+'px');
+            highlight.setStyle('opacity', 0.9);
+            container.append(highlight);
+        }, this);
+        // Add a mouse up event to the node so that we we raise the mouse button
+        // the highlight goes away.
+        e.target.on('mouseup', function(){
+            this.themetool.overlay.bodyNode.setStyle('opacity', 1.0);
+            this.Y.all('.cssbuilder_highlight_container').remove();
+        }, this);
+    },
+    /**
+     * Builds the controls div for this component and stores it. This is done to increase
+     * percieved performance when using the tool!
+     */
+    init_generate_controls : function() {
+        if (this.controlsnode == null) {
+            var controls = this.Y.Node.create('<div class="css_selector_controls"></div>');
+            controls.append(this.Y.Node.create('<div class="colorpickerdialogue"></div>'));
+            controls.append(this.Y.Node.create('<div class="control colourpicker"> </div>'));
+            controls.append(this.Y.Node.create('<div class="control backgroundcolourpicker"> </div>'));
+            controls.append(this.Y.Node.create('<div class="control bold"> </div>'));
+            controls.append(this.Y.Node.create('<div class="control italic"> </div>'));
+            controls.append(this.Y.Node.create('<div class="control underline"> </div>'));
+            controls.append(this.Y.Node.create('<div class="control alignleft"> </div>'));
+            controls.append(this.Y.Node.create('<div class="control aligncenter"> </div>'));
+            controls.append(this.Y.Node.create('<div class="control alignright"> </div>'));
+            controls.append(this.Y.Node.create('<div class="paramcenter"><input type="button" id="highlightaffectedcells" value="'+M.str.block_css_theme_tool.highlight+'" /><input id="commitnewstyle" type="button" value="'+M.str.block_css_theme_tool.save+'" /><input id="cancelnewstyle" type="button" value="'+M.str.block_css_theme_tool.cancel+'" /></div>'));
+            controls.one('#commitnewstyle').on('click', this.save, this);
+            controls.one('#cancelnewstyle').on('click', this.hide, this);
+            controls.one('#highlightaffectedcells').on('mousedown', this.highlight_affected_cells, this);
+
+            new M.block_css_theme_tool.colour_picker(this, controls.one('.colorpickerdialogue'), controls.one('.control.colourpicker'), 'color');
+            new M.block_css_theme_tool.colour_picker(this, controls.one('.colorpickerdialogue'), controls.one('.control.backgroundcolourpicker'), 'background-color');
+            new M.block_css_theme_tool.generic_add_style_button(this, controls.one('.control.bold'), 'font-weight: bold;');
+            new M.block_css_theme_tool.generic_add_style_button(this, controls.one('.control.italic'), 'font-style: italic;');
+            new M.block_css_theme_tool.generic_add_style_button(this, controls.one('.control.underline'), 'text-decoration: underline;');
+            new M.block_css_theme_tool.generic_add_style_button(this, controls.one('.control.alignleft'), 'text-align: left;');
+            new M.block_css_theme_tool.generic_add_style_button(this, controls.one('.control.aligncenter'), 'text-align: center;');
+            new M.block_css_theme_tool.generic_add_style_button(this, controls.one('.control.alignright'), 'text-align: right;');
+
+            this.controlsnode = controls;
+        }
+        return this.controlsnode
+    },
+    /**
+     * Show the CSS builder component
+     *
+     * @param {Y.Node} target The target node to create/edit a rule for.
+     */
+    show : function(target) {
+        // Hide all other components
+        this.themetool.hide_components();
+        // Create the HTML for the comontent
+        var content = this.Y.Node.create('<div class="css_builder"></div>');
+        var selectorcontent = this.Y.Node.create('<div class="css_selectors"></div>');
+        var selectors = [];
+        this.selectorcount = 0;
+        while (target) {
+            // Create a selector for each node back to the html tag
+            selectors.push(this.generate_selectors_for_node(target));
+            target = target.ancestor()
+        }
+        // Reverse it otherwise its all backwards !
+        selectors.reverse();
+        for (var i in selectors) {
+            // Add each selector to the selector content node
+            selectorcontent.append(selectors[i]);
+        }
+
+        content.append(selectorcontent);
+        // Add the style defintion node
+        content.append(this.Y.Node.create('<div class="css_current_selector"><div class="selectorcontainer"></div>{<ul class="style_definitions"></ul>}</div>'));
+        // Add the controls
+        content.append(this.controlsnode);
+
+        // Sets up the themetool overlay
+        this.themetool.overlay.set('bodyContent', content);
+        this.themetool.overlay.show();
+        // Attaches an update event to EVERY seletor shown in the compontent
+        this.themetool.overlay.bodyNode.all('.tagselectorbox input.tagselector').on('click', this.update, this);
+        // Update it node anyway
+        this.update();
+        this.shown = true;
+    },
+    /**
+     * Hides this component if it is being shown
+     */
+    hide : function() {
+        if (this.shown) {
+            this.themetool.overlay.set('bodyContent', '');
+            this.themetool.overlay.hide();
+            this.shown = false;
+        }
+    },
+    /**
+     * Given a node this generates a set of selectors for it. Tag name, classes and id.
+     *
+     * @param {Y.Node} node
+     * @return {Y.Node} The selectors node
+     */
+    generate_selectors_for_node : function(node) {
+        // Get the tag name
+        var tag = node.get('nodeName').toLowerCase();
+        // Get the tag id
+        var tagid = node.get('id');
+        // Get an array of classes
+        var tagclasses = node.get('className').split(' ');
+
+        // Create a container
+        var content = this.Y.Node.create('<div class="tagselectorbox"></div>');
+        // Add the tag selector
+        content.append(this.Y.Node.create('<div class="tagname"><input class="tagselector" type="checkbox" id="tagselector_'+this.selectorcount+'" value="'+tag+'" /><label id="tagselector_'+this.selectorcount+'_label">'+tag+'<label></div>'));
+        // If there is an id for the node add an ID selector
+        if (tagid && tagid != '' && tagid.match && !tagid.match(/^yui_\d/)) {
+            // Increase the selector count VERY IMPORTANT
+            this.selectorcount++;
+            content.append('<div class="tagid"> <input class="tagselector" type="checkbox" id="tagselector_'+this.selectorcount+'" value="#'+tagid+'" /><label id="tagselector_'+this.selectorcount+'_label">#'+tagid+'<label></div>');
+        }
+        // Add each class in the classes array to the tag selectors
+        if (tagclasses && tagclasses.length > 0) {
+            var classes = this.Y.Node.create('<div class="tagclasses"></div>');
+            for (var i in tagclasses) {
+                var c = tagclasses[i];
+                // Don't process if its empty or based on settings for the tool.
+                if (!c || c=='' || (!this.themetool.cfg.showadvancedbodytags && tag=='body' && !c.match(/^(page|path)\-/))) {
+                    continue;
+                }
+                // Increase the selector count VERY IMPORTANT
+                this.selectorcount++;
+                // Add the class as a selector
+                var tagclass = this.Y.Node.create('<div class="tagclass"></div>');
+                var tagclassinput = this.Y.Node.create('<input class="tagselector" type="checkbox" id="tagselector_'+this.selectorcount+'" value=".'+c+'" />');
+                var tagclasslabel = this.Y.Node.create('<label id="tagselector_'+this.selectorcount+'_label">.'+c+'<label>');
+                if (i==0 && !M.util.in_array(c, this.nondefaultclasses)) {
+                    // Check it by default IF it is the first class and not one of the nondefaultclasses
+                    tagclass.addClass('isselected');
+                    tagclassinput.set('checked', true);
+                }
+                tagclass.append(tagclassinput);
+                tagclass.append(tagclasslabel);
+                classes.append(tagclass);
+            }
+            // Add the classes to the content
+            content.append(classes);
+        }
+        // Increase the selector count VERY IMPORTANT
+        this.selectorcount++;
+        // Return the selector content node
+        return content;
+    },
+    /**
+     * Adds controls to add a style to the selector creating a rule
+     * @param {Y.Event} e
+     */
+    add_style : function(e) {
+        // Get the definitions UL
+        var ul = this.themetool.overlay.bodyNode.one('.style_definitions');
+        // Gets the new style li
+        var addnewstyle = ul.one('.addnewstyle').ancestor('li');
+        // Hide it :)
+        addnewstyle.addClass('hideme');
+        // Create a new li
+        var newstyleli = this.Y.Node.create('<li></li>');
+        // Create an input to type the style into
+        var newstyleinput = this.Y.Node.create('<input type="text" value="" id="nextstyleinput" />');
+        // Combine
+        newstyleli.append(newstyleinput);
+        // Add the new LI to the UL collection
+        ul.insert(newstyleli, addnewstyle);
+        // Focus on the input
+        newstyleinput.focus();
+        // Attach an event to complete the process if Tab or Enter are pressent
+        this.Y.on('key', this.process_style, newstyleinput, 'press:9,13', this, newstyleinput);
+    },
+    /**
+     * Manually adds a given style to the current selector structure
+     * @param {string} newstyle
+     */
+    manual_add_style : function(newstyle) {
+        if (!newstyle.match(/;$/)) {
+            newstyle += ';';
+        }
+        var li = this.Y.one('.css_current_selector ul.style_definitions').one('.addnewstyle').ancestor('li');
+        li.ancestor().insert(this.create_style_li(newstyle, 'styleattribute'), this.Y.Node.getDOMNode(li));
+    },
+    /**
+     * Processes the contents of the add style input and adds it properly to the rule
+     * @param {Y.Event} e
+     * @param {Y.Node} i The input node
+     * @param {bool} finish If true then we are done adding styles and should revert to
+     *               basic layout
+     */
+    process_style : function(e, i, finish) {
+        var newstyle = i.get('value');
+        var li = i.ancestor('ul').one('.addnewstyle').ancestor('li');
+        var liswitch = (finish===true);
+        if (newstyle.match(/^[^:]+:.*$/)) {
+            if (!newstyle.match(/;$/)) {
+                newstyle += ';';
+            }
+            li.ancestor().insert(this.create_style_li(newstyle, 'styleattribute'), this.Y.Node.getDOMNode(li));
+            i.set('value', '');
+        } else if (newstyle.match(/^\s*$/)) {
+            liswitch = true;
+        }
+        if (liswitch) {
+            li.removeClass('hideme');
+            i.remove();
+        }
+    },
+    /**
+     * Saves the rule as a selector and styles collection in the database using an
+     * AJAX call.
+     */
+    save : function() {
+        if (this.Y.one('#nextstyleinput')) {
+            this.process_style(null, this.Y.one('#nextstyleinput'), true);
+        }
+
+        var selector = this.get_selected_selector();
+        var styles = [];
+        this.themetool.overlay.bodyNode.all('.style_definitions .styleattribute').each(function(){
+            styles.push(this.get('innerHTML'));
+        });
+        if (this.themetool.add_rule(selector, styles)) {
+            this.hide();
+        } else {
+            alert('Unable to save the rule');
+        }
+    },
+    /**
+     * Updates the selector and rules shown to the user to reflect changes in what
+     * the user has selected
+     */
+    update : function() {
+        if (this.themetool.overlay.bodyNode.all('.addnewstyle').size()<1) {
+            var li = this.Y.Node.create('<li><span class="addnewstyle">'+M.str.block_css_theme_tool.addnewstyle+'</span></li>');
+            this.themetool.overlay.bodyNode.one('.css_current_selector .style_definitions').append(li);
+            li.on('click', this.add_style, this);
+        }
+
+        var selected = this.get_selected_selectors();
+        var selectors = [];
+        var rawselectors = [];
+        for (var i in selected) {
+            var selectorsinbox = [];
+            var rawselectorsinbox = [];
+            for (var j in selected[i]) {
+                rawselectorsinbox.push(selected[i][j].get('value'));
+                selectorsinbox.push('<span class="'+selected[i][j].ancestor().get('className')+'">'+selected[i][j].get('value')+'</span>');
+            }
+            selectors.push(selectorsinbox.join(''));
+            rawselectors.push(rawselectorsinbox.join(''))
+        }
+        this.themetool.overlay.bodyNode.one('.css_current_selector .selectorcontainer').set('innerHTML', selectors.join(' '));
+
+        var l = this.themetool.overlay.bodyNode.one('.style_definitions .addnewstyle').ancestor('li');
+        var ul = l.ancestor('ul');
+        ul.all('.styleattribute.predefined').each(function(){
+            this.ancestor('li').remove();
+        });
+        var currentrule = this.themetool.get_rule(rawselectors.join(' '));
+        if (currentrule) {
+            for (j in currentrule.styles) {
+                ul.insert(this.create_style_li(currentrule.styles[j], 'styleattribute predefined'), this.Y.Node.getDOMNode(l));
+            }
+        }
+    },
+    /**
+     * Convenience function to create an LI node with editing controls
+     * @param {string} style
+     * @param {string} classstr
+     * @return {Y.Node}
+     */
+    create_style_li : function(style, classstr) {
+        var li = this.Y.Node.create('<li></li>');
+        var editicon = this.Y.Node.create('<img src="'+M.util.image_url('t/edit', 'moodle')+'" alt="edit" style="width:8px;height:8px;" class="icon" />');
+        editicon.on('click', this.edit_style, this, li, style);
+        var deleteicon = this.Y.Node.create('<img src="'+M.util.image_url('t/delete', 'moodle')+'" alt="delete" style="width:8px;height:8px;" class="icon" />');
+        deleteicon.on('click', this.delete_style, this, li);
+        li.append(editicon).append(deleteicon).append(this.Y.Node.create('<span class="'+classstr+'">'+style+'</span>'));
+        return li;
+    },
+    /**
+     * Deletes the selected style
+     * @param {Y.Event} e
+     * @param {Y.Node} li
+     */
+    delete_style : function(e, li) {
+        li.remove();
+    },
+    /**
+     * Opens a set style for editing
+     * @param {Y.Event} e
+     * @param {Y.Node} li
+     * @param {string} style
+     */
+    edit_style : function(e, li, style) {
+        if (this.Y.one('#nextstyleinput')) {
+            this.process_style(null, this.Y.one('#nextstyleinput'), true);
+        }
+        li.remove();
+        this.add_style(e);
+        this.Y.one('#nextstyleinput').set('value',style);
+    },
+    /**
+     * Gets an array of the selected selectors
+     *
+     * @return {array}
+     */
+    get_selected_selectors : function() {
+        var selectors = [];
+        this.themetool.overlay.bodyNode.all('.tagselectorbox').each(function(box) {
+            var selectorsinbox = [];
+            box.all('input.tagselector').each(function(checkbox){
+                checkbox.ancestor().removeClass('isselected');
+                if (checkbox.get('checked')) {
+                    checkbox.ancestor().addClass('isselected');
+                    selectorsinbox.push(checkbox);
+                }
+            }, this);
+            if (selectorsinbox.length > 0) {
+                selectors.push(selectorsinbox);
+            }
+        }, this);
+        return selectors;
+    },
+    /**
+     * Gets the selected selectors as a string that can be used in CSS or YUI
+     *
+     * @return {string}
+     */
+    get_selected_selector : function() {
+        var selectors = this.get_selected_selectors();
+        var selector = '';
+        for (var i in selectors) {
+            for (var j in selectors[i]) {
+                selector += selectors[i][j].get('value');
+            }
+            selector += ' ';
+        }
+        return selector;
+    }
+}
+/**
+ * Colour picker component
+ *
+ * This component I am particually proud of it displays a colour picker control
+ * that allows the user to point and click a colour to add it as a style.
+ * It is HEAVILY tied to colourpicker_image.php which mathmetically generates
+ * the colour picker image and for which this component reverses the process to
+ * work out the colour that was clicked on.
+ *
+ * @class
+ * @constructor
+ * @param {M.block_css_theme_tool.css_builder} cssbuilder
+ * @param {Y.Node} box A node to put the colour picker within
+ * @param {Y.Node} button A node to display the picker when clicked
+ * @param {string} cssstyle The style to add the colour for
+ */
+M.block_css_theme_tool.colour_picker = function(cssbuilder, box, button, cssstyle) {
+    this.cssbuilder = cssbuilder;
+    this.box = box;
+    this.currentcolour = '#FFFFFF';
+    this.cssstyle = cssstyle;
+
+    this.overlay = new this.cssbuilder.Y.Overlay({
+        bodyContent : ' ',
+        width: '50px',
+        height : '50px',
+        centered : false,
+        visible : false,
+        zIndex : 5000
+    });
+    this.overlay.render(this.cssbuilder.Y.one(document.body));
+    this.overlay.bodyNode.addClass('colourpicker_preview');
+    button.on('click', this.show, this);
+}
+/**
+ * Colour picker properties
+ */
+M.block_css_theme_tool.colour_picker.prototype = {
+    /**
+     * The YUI instance
+     * @var {YUI}
+     */
+    Y : null,
+    /**
+     * The box top display in
+     * @var {Y.Node}
+     */
+    box : null,
+    /**
+     * The overlay this component uses
+     * @var {Y.Overlay}
+     */
+    overlay : null,
+    /**
+     * The currently selected colour
+     * @var {string}
+     */
+    currentcolour : null,
+    /**
+     * @var {bool}
+     */
+    overlayshown : false,
+    /**
+     * The CSS builder this component is associated with
+     * @var {M.block_css_theme_tool.css_builder}
+     */
+    cssbuilder : null,
+    /**
+     * The style this picker is used for
+     * @var {string}
+     */
+    cssstyle : null,
+    /**
+     * An object containing component events
+     * @var {object}
+     */
+    events: {
+        /**
+         * Used to track when the mouse enters the colour picker
+         * @var {Y.Event}
+         */
+        mouseenter : null,
+        /**
+         * Used to track the click event of the colour picker
+         * @var {Y.Event}
+         */
+        click : null,
+        /**
+         * Used to track when the mouse moves over the colour picker
+         * @var {Y.Event}
+         */
+        mousemove : null,
+        /**
+         * Used to track when the mouse leaves the colour picker
+         * @var {Y.Event}
+         */
+        mouseleave : null
+    },
+    /**
+     * Sets the background colour to the floating overlay to that of the colour the mouse is over.
+     * @var {Y.Event}
+     */
+    display_color : function(e) {
+        this.overlay.move(e.pageX+10, e.pageY+10);
+        this.currentcolour = this.determine_colour(e);
+        this.overlay.bodyNode.setStyle('backgroundColor', this.currentcolour);
+    },
+    /**
+     * Starts the proceedings for the colour picker, following changes the overlay
+     * background colour and when clicked stops and adds the style to the CSS builder.
+     * @var {Y.Event}
+     */
+    start_follow : function(e) {
+        this.overlay.show();
+        this.overlayshown = true;
+        this.events.click = this.box.on('click', this.pick_colour, this);
+        this.events.mousemove = this.box.on('mousemove', this.display_color, this);
+        this.events.mouseleave = this.cssbuilder.Y.on('mouseleave', this.stop_follow, this.box, this);
+        this.display_color(e);
+    },
+    /**
+     * Stops following the mouse and does away with the colour picker
+     */
+    stop_follow : function() {
+        this.overlay.hide();
+        this.overlayshown = false;
+        this.events.mousemove.detach();
+        this.events.mouseleave.detach();
+        this.events.click.detach();
+        this.box.setStyle('visibility', 'hidden');
+    },
+    /**
+     * Determines the colour the mouse is currently over.
+     *
+     * This is the a reversal of the logic in ~/colourpicker_image.php and mathematically
+     * determines the colour below the mouse pointer
+     * @var {Y.Event}
+     * @return {string}
+     */
+    determine_colour : function(e) {
+
+        var eventx = Math.floor(e.pageX-e.target.getX());
+        var eventy = Math.floor(e.pageY-e.target.getY());
+
+        var imagewidth = 766;
+        var imageheight = 24;
+        var factor = 2;
+        var colour = [255,0,0];
+
+        var matrices = [
+            [  0,  1,  0],
+            [ -1,  0,  0],
+            [  0,  0,  1],
+            [  0, -1,  0],
+            [  1,  0,  0],
+            [  0,  0, -1]
+        ];
+
+        var matrixcount = matrices.length;
+        var limit = Math.round(imagewidth/matrixcount);
+        var heightbreak = Math.round(imageheight/2);
+
+        for (var x = 0; x < imagewidth; x++) {
+            var divisor = Math.floor(x / limit);
+            var matrix = matrices[divisor];
+
+            colour[0] += matrix[0]*factor;
+            colour[1] += matrix[1]*factor;
+            colour[2] += matrix[2]*factor;
+
+            if (eventx==x) {
+                break;
+            }
+        }
+
+        var pixel = [colour[0], colour[1], colour[2]];
+        if (eventy < heightbreak) {
+            pixel[0] += Math.floor(((255-pixel[0])/heightbreak) * (heightbreak - eventy));
+            pixel[1] += Math.floor(((255-pixel[1])/heightbreak) * (heightbreak - eventy));
+            pixel[2] += Math.floor(((255-pixel[2])/heightbreak) * (heightbreak - eventy));
+        } else if (eventy > heightbreak) {
+            pixel[0] = Math.floor((imageheight-eventy)*(pixel[0]/heightbreak));
+            pixel[1] = Math.floor((imageheight-eventy)*(pixel[1]/heightbreak));
+            pixel[2] = Math.floor((imageheight-eventy)*(pixel[2]/heightbreak));
+        }
+
+        return this.convert_rgb_to_hex(pixel);
+    },
+    /**
+     * Stops everything and adds the selected colour + style to the CSS builder
+     * @var {Y.Event}
+     */
+    pick_colour : function(e) {
+        this.stop_follow();
+        this.cssbuilder.manual_add_style(this.cssstyle+': '+this.currentcolour);
+    },
+    /**
+     * Shows the colour picker component
+     * @var {Y.Event}
+     */
+    show : function (e) {
+        this.box.setStyle('visibility', 'visible');
+        this.start_follow(e);
+    },
+    /**
+     * Converts the RGB value into a hex equivilant
+     * @param {array} rgb Array(r,g,b);
+     * @return {string} #ABCDEF
+     */
+    convert_rgb_to_hex : function(rgb) {
+        var hex = '#';
+        var hexchars = "0123456789ABCDEF";
+        for (var i=0; i<3; i++) {
+            var number = Math.abs(rgb[i]);
+            if (number == 0 || isNaN(number)) {
+                hex += '00';
+            } else {
+                hex += hexchars.charAt((number-number%16)/16)+hexchars.charAt(number%16);
+            }
+        }
+        return hex;
+    }
+}
+/**
+ * Generic add style button component
+ * @class
+ * @constructor
+ * @param {M.block_css_theme_tool.css_builder} cssbuilder
+ * @param {Y.Node} button
+ * @param {string} style
+ */
+M.block_css_theme_tool.generic_add_style_button = function(cssbuilder, button, style) {
+    this.cssbuilder = cssbuilder;
+    this.button = button;
+    this.style = style;
+    button.on('click', this.add_style, this);
+}
+/**
+ * Properties for generic_add_style_button
+ */
+M.block_css_theme_tool.generic_add_style_button.prototype = {
+    /**
+     * CSS builder instance this component is associated with
+     * @var {M.block_css_theme_tool.css_builder}
+     */
+    cssbuilder : null,
+    /**
+     * The button to attach this to
+     * @var {Y.Node}
+     */
+    button : null,
+    /**
+     * The style this should add
+     * @var {string}
+     */
+    style : null,
+    /**
+     * Adds the style to the CSS builder
+     */
+    add_style : function() {
+        this.cssbuilder.manual_add_style(this.style);
+    }
+}
+/**
+ * CSS theme tool settings manager
+ *
+ * This is a component that allows the user to take advanced actions and toggle the settings
+ * for the CSS theme tool.
+ *
+ * @class
+ * @constructor
+ * @param {YUI} Y
+ * @param {css_theme_tool} themetool
+ */
+M.block_css_theme_tool.settings_manager = function(Y, themetool) {
+    this.Y = Y;
+    this.themetool = themetool;
+    this.init_generate_content();
+    Y.one('.block_css_theme_tool input.settingsbutton').removeAttribute('disabled').on('click', this.show, this);
+}
+
+/**
+ * Properties for the settings manager
+ */
+M.block_css_theme_tool.settings_manager.prototype = {
+    /**
+     * The YUI instance
+     * @var {YUI}
+     */
+    Y : null,
+    /**
+     * Theme tool instance this component belongs to
+     * @var {css_theme_tool}
+     */
+    themetool : null,
+    /**
+     * The content for the settings manager
+     * @var {Y.Node}
+     */
+    content : null,
+    /**
+     * @var {bool}
+     */
+    shown : false,
+    /**
+     * Generates the content for this compontent and stores it in
+     */
+    init_generate_content : function() {
+        var Y = this.Y;
+        var div = Y.Node.create('<div class="settingscontainer"><h2>Settings</h2></div>');
+
+        var purge = Y.Node.create('<input type="button" value="'+M.str.block_css_theme_tool.purgerules+'" />');
+        var purgediv = Y.Node.create('<div class="settingbox"></div>');
+        purgediv.append(purge);
+        purgediv.append(Y.Node.create('<div class="settingdescription">'+M.str.block_css_theme_tool.purgerulesdesc+'</div>'));
+        div.append(purgediv);
+
+        var exportcss = Y.Node.create('<input type="button" value="'+M.str.block_css_theme_tool.exportcss+'" />');
+        var exportcssdiv = Y.Node.create('<div class="settingbox"></div>');
+        exportcssdiv.append(exportcss);
+        exportcssdiv.append(Y.Node.create('<div class="settingdescription">'+M.str.block_css_theme_tool.exportcssdesc+'</div>'));
+        div.append(exportcssdiv);
+
+        /**
+         * To be created at a later point
+         * 
+         * var exporttheme = Y.Node.create('<input type="button" value="'+M.str.block_css_theme_tool.exporttheme+'" />');
+         * var exportthemediv = Y.Node.create('<div class="settingbox"></div>');
+         * exportthemediv.append(exporttheme);
+         * exportthemediv.append(Y.Node.create('<div class="settingdescription">'+M.str.block_css_theme_tool.exportthemedesc+'</div>'));
+         * div.append(exportthemediv);
+         *
+         * exporttheme.on('click', this.themetool.export_theme, this.themetool);
+         */
+
+
+        var showadvancedbodytags = Y.Node.create('<input type="checkbox" value="'+M.str.block_css_theme_tool.showadvancedbodytags+'" />');
+        var showadvancedbodytagsdiv = Y.Node.create('<div class="settingbox"></div>');
+        showadvancedbodytagsdiv.append(showadvancedbodytags);
+        showadvancedbodytagsdiv.append(Y.Node.create('<div class="settingdescription">'+M.str.block_css_theme_tool.showadvancedbodytagsdesc+'</div>'));
+        div.append(showadvancedbodytagsdiv);
+        if (this.themetool.cfg.showadvancedbodytags) {
+            showadvancedbodytags.set('checked', true);
+        }
+
+        var controls = Y.Node.create('<div class="overlaycontrols"></div>');
+        var closebutton = Y.Node.create('<input type="button" value="'+M.str.block_css_theme_tool.close+'" />');
+        controls.append(closebutton);
+        div.append(controls);
+
+        purge.on('click', this.themetool.purge_rules, this.themetool);
+        exportcss.on('click', this.themetool.export_css, this.themetool);
+        showadvancedbodytags.on('click', this.toggle_showadvancedbodytags, this, showadvancedbodytags);
+        closebutton.on('click', this.hide, this);
+
+        this.content = div;
+    },
+    /**
+     * Shows the settings manager
+     */
+    show : function() {
+        this.themetool.hide_components();
+        this.themetool.overlay.set('bodyContent', this.content);
+        this.themetool.overlay.show();
+        this.shown = true;
+    },
+    /**
+     * Hides the settings manager if is is being shown
+     */
+    hide : function() {
+        if (this.shown) {
+            this.themetool.overlay.hide();
+            this.shown = false;
+        }
+    },
+    /**
+     * Toggles the showing of advanced body tags
+     * @param {Y.Event} e
+     * @param {Y.Node} checkbox
+     */
+    toggle_showadvancedbodytags : function(e, checkbox) {
+        this.themetool.cfg.showadvancedbodytags = (checkbox.get('checked'))?true:false;
+        M.util.set_user_preference('css_theme_tool_full_body_tags', this.themetool.cfg.showadvancedbodytags);
+    }
+}
Index: moodle/blocks/css_theme_tool/pix/alignCenter.png
MIME: application/octet-stream; encoding: Base64; length: 1239
iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c
6QAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0
SU1FB9oDHgYWJzUIs5sAAAAZdEVYdENvbW1lbnQAQ3JlYXRlZCB3aXRoIEdJ
TVBXgQ4XAAAEMklEQVRIx5WWu2+TZxTGf+/VMbZlG2gGHMgSJEC9okZcqiKV
hbkSVTt2YEBKu1Ap/0AZO3Wo+h/QDejCrSpDEUmQAJWoTVo1AxVj4iSO48/J
Z7+nw3fxR6jU8snH7/2c87zPefxZ8T+e+YU5ffDgG15rbSUEBRBEVK+33X/n
7Xd3eY3nYqVSWTDG9AHJTGstzWZTDh8+LK1WS8bHx+XAgQNSKpXyPdbanUaj
8cRae7HoUGWdcrn8bRRFX3rvmZ2d5cSJExw9OkWrNUGj0QBARBAECZL0RVhr
r7H0+xJzc3Nc++EaK3+tUK1Wv+t2uzN5FK31J4BcvnxZtrpbEkU9ifqR9Pv9
3KJ+JFHUk15vW7rbXdna2pJOpyMbmxuyvr4ua+01WV1dlZmZmQz1xzmCZrPx
5MiRyfcePnyI1gqlNEol4JJWEEkRSCCkCIKEFE0yl41PnjzJ5ubm406n874G
6Ha337x06VLiTClUalrrtK/zOfIWFIokj2ycdC9cuECv13sLQAPEceymp6dT
UpJDrwZRL6+P6CuQmcyeO/chg8HAA9hsw9TUVJpNsrHeqBPvxsRxjHMO7z3e
e5xzlEoe5zzeO7wv4bzjp3v3kigCx44dH/G7uPjMjY2NUS6XESGvFq00xhi8
9xhjUiQZJyNEo7zTggUmJiYy/VhbqVT21Wq1dFUhCApFu91+yUlWlnuJLRIM
ggDWGvbv30+1WqtZpZRtNBpp9oIShRBSeoQMd1ZFSKYB8nmQ7JO31lq0Vvus
iKgsQxQECWiSIEoS55OTk2ijMdpgncVai7OOhYX5RHiS5l6I0Ggm4rSD4SDu
9/tJgDTxACjJvsB7h9YJD8YarLEYY5LsU0RkKi/CAGwIEvIAkHpPnKfgWF7+
Y4/Y9lh696MgwsbGBiIou7PT30kGMiI05QIEpWC0lAUJrwYLRSQwGMSIhJ6N
oiiO45itrQ61Wi2pIVGISqrp7NkPEqFphVY64cIYrDEYY7hx48Yo85TLOI5p
t9fpdrsdffrUGQF4/vxvgggSQl6KQQK+5HHe4azDuYRcay3WOaxzRFE/OVdA
8eLFCwBOnzozyJW8tLTE8ePHEKVQQoJASBRKsUCyUi38fO/hZHlpeaRkAGPM
zqNHCymCTEypgFLLkeWOQmpp5kHy8YMHv2Ct3c0DlEqlX2/e/JEwHBauZ+Qk
ZKoNQgghsfxawsvBRLh79x6VSuU3AAMwHA5Xu93uZxubG5z/6HzhGiSvdTIa
cwWPSC0q/OurV7n/832Gw+EXIYQlk6p4uVqtjs/Pz09fv36d4XBIuVxmrDyG
845RjMKdp3Xf6XRYWVnh1q3bXPnqCndu36Fer38fRdE3r7zxvfefNpvNp865
3eJLv16vS71el+LcXiuVSoNWq/XnoUOHPn+dfxnML8zZxcVn9SdPH5f/Zc39
1/l/AKIOEnKgF249AAAAAElFTkSuQmCC
Index: moodle/blocks/css_theme_tool/pix/alignLeft.png
MIME: application/octet-stream; encoding: Base64; length: 1232
iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c
6QAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0
SU1FB9oDHgYVFdbysdgAAAAZdEVYdENvbW1lbnQAQ3JlYXRlZCB3aXRoIEdJ
TVBXgQ4XAAAEK0lEQVRIx5WWzW8TRxjGfzM7M46xLdtAc8CBfihIgPqJGgEH
qMqFcyWq9tgDB6S0FyrxD5RjTz1U/Q/oDVEhQaAqhyKSIAEqAYyq5kDFMXEc
2/E6WXveHvbDm6RSy8qv5mv3feaZ5312rfgf18LivN6//w2ntTbivQLwIqrf
3xh88P6HW7zGdb5UKi0GQTAAJA2ttdTrdTl48KA0Gg2ZnJyUffv2SaFQyO4x
xmzWarVHxpjz+YQq7RSLxR/CMPzGOcfly5c5duwYhw9P02hMUavVABARBEG8
xH0RVlurNJ83mZ+f5+rPV1n+a5lyufxjr9ebzVC01p8DcvHiRen2uhKGfQkH
oQwGgyzCQShh2Jd+f0N6Gz3pdrvS6XSkvd6WtbU1WW2tysrKiszOzqasP8sY
1Ou1R4cOvfnR/fv30VqhlEapmFzcCiIJA/H4hIEXn7CJ59Lx8ePHWV9ff9jp
dD7WAL3exrsXLlyIkymFSkJrnfR1NkfWgkIR7yMdx91z587R7/ffA9AAURTZ
mZmZRJT4od0gavv6WL6cmPHsmTOnGQ6HDsCkN0xPTye7iW+s1qpEWxFRFGGt
xTmHcw5rLYWCw1qHcxbnClhn+fXOnRhF4MiRo2N9l5ae2ImJCYrFIiJk1aKV
JggCnHMEQZAwSTUZMxrvOylYYGpqKvWPMaVSaU+lUklWFYKgULRarW1J0rLc
KWxeYBAEMCZg7969lMuVilFKmVqtluxeUKIQfCKPkPJOqwhJPUA2D5L+stYY
g9ZqjxERle4QBV48GsXb77yFH3lEJBY70AQ6wFjD06dPM0aSgu9AqNVjc5rh
aBgNBoMYINm4B5x1+GCUM2OsQ2CCnJPJGJEDG9MB4734DADi7AqeP3+Wf5Ps
MNuOSM5+DCK0221EUGZzc7AZDySfCiXx2SsF46UUxO8G83kmMBxGiPi+CcMw
iqKIbrdDpVKJa0gUpz85nT2klEJphVY61iIIMEHAjRs3th9PomUURbRaa/R6
vY4+eeKUALx8+TdeBPEeLx5nHdZZXCFurbFYa7DGYozBWIsXySLP4tWrVwCc
PHFqmDm52Wxy9OgRRCmUwNztuR0KpNqlpUqckN2avGi+GBcHQBAEmw8eLCYM
UjMlBkoiZTZO5JNIdu4lG9+79zvGmK0MoFAo/HH9+i/40Shzqs8l8alrveC9
jyM7Fr8dTITbt+9QKpWeAQQAo9Fopdfrfdleb3P207O5YxjXOqmMmYPHouYd
/t2VK9z97S6j0ehr730zSFz8olwuTy4sLMxcu3aN0WhEsVhkojiBdZYxRu7M
k7rvdDosLy9z8+YtLn17iblbc1Sr1Z/CMPx+1xffOfdFvV5/bK3dyn/0q9Wq
VKtVyc/tjEKhMGw0Gn8eOHDgq9f5l8HC4rxZWnpSffT4YfFf1ux/Pf8PNy8e
fpAq0OgAAAAASUVORK5CYII=
Index: moodle/blocks/css_theme_tool/pix/alignRight.png
MIME: application/octet-stream; encoding: Base64; length: 1244
iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c
6QAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0
SU1FB9oDHgYVJx4l4FgAAAAZdEVYdENvbW1lbnQAQ3JlYXRlZCB3aXRoIEdJ
TVBXgQ4XAAAEN0lEQVRIx5WWTW8TVxSGn3vn3nGMbdkGmgUOdJNIgPqJGgES
RSob1pWo2mUXLJDSbqjEHyjLrrqo+gcK3SEqUT6CyqKIJEiAitUaVc2CimXi
OLbjcTL2PV3cmfEEKrWMfOZ+zj3nPe95Z6z4H9fyypLev/+NUGttxDkF4ETU
YLA1fPed93Z4jetcqVRaCYJgCEhqWmup1+ty8OBBaTQaMj09Lfv27ZNCoZDt
McZs12q1x8aYc/kDVdopFovfRlH0ZRiGXLp0iaNHjzI3N0ujMUOtVgNARBAE
ceL7Iqy312n90WJpaYmrP15l9a9VyuXyd/1+fyHzorX+BJALFy5Ir9+TKBpI
NIxkOBxmFg0jiaKBDAZb0t/qS6/Xk263K53NjmxsbMh6e13W1tZkYWEhRf1x
hqBerz0+dOjN9x88eIDWCqU0SnlwvhVEEgTicAkCJy5B4+fS8bFjx9jc3HzU
7XY/0AD9/tZb58+f94cphUpMa530dTZH1oJC4eNIx7579uxZBoPB2wAaII5j
Oz8/n5DiH3rVidq9PqEvR6afPX36Q0ajUQhg0g2zs7NJNH5jtVYl3omJ4xhr
LWEYEoYh1loKhRBrQ8LQEoYFbGi5u7jovQgcPnxkwm+z+dROTU1RLBYRIasW
rTRBEBCGIUEQJEhSTiaIJnEnBQvMzMyk+jGmVCrtqVQqyapCEBSKdru965C0
LF8mNk8wCAIYE7B3717K5UrFKKVMrVZLoheUKASX0COkuNMqQlINkM2DpL+s
NcagtdpjRESlEaLAiUPjnShRGW5BmJudw1iDMQZrLGFoMdZijeXGzzd2eajV
vTjNaDyKh8Ohd5AE7gAl6S1NEVhrCUyACUzCS0CgPVekKs/DAIxz4jIHkJzu
D0/ApS5oNpsZFxNzuKQ/cSJ0Oh1EUGZ7e7jtB7loEy5AUAomSykXLqfsxFze
CYxGMSJuYKIoiuM4ptfrUqlUfA2JQpSvJhE4deoUSiu00ujAp8QEgW+N5+SH
K1f8a1WEOI5ptzfo9/tdfeL4SQF4/vxvD9W5rBSd+L4NPZHWenKNMRhrMwuM
2YXixYsXAJw4fnKUKbnVanHkyGFEKZTgESSpubu4mCuQtFRzr++XeHnWejZR
MkAQBNsPH64kCFIxJQJKLEOWI1fSsZNMfCLC/fu/YozZyRwUCoXfrl//CTce
59IzOcSlqnWCc85bRq7b7UyEO3cWKZVKvwMEAOPxeK3f73/W2exw5qMzuTQk
de1vyXxaTZKRmlf415cvc++Xe4zH4y+cc60gUfGzcrk8vby8PH/t2jXG4zHF
YpGp4hQ2tEx85HKeqK/b7bK6usrNm7e4+NVFbt+6TbVa/T6Kom9e+eKHYfhp
vV5/Yq3dyX/0q9WqVKtVyc+9bIVCYdRoNP48cODA56/zL4PllSXTbD6tPn7y
qPgva/a/nv8HlAMFgXm1Nb4AAAAASUVORK5CYII=
Index: moodle/blocks/css_theme_tool/pix/backgroundColor.png
MIME: application/octet-stream; encoding: Base64; length: 1171
iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c
6QAAAAZiS0dEAAAAAAAA+UO7fwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0
SU1FB9oDHgYCFdNxNU4AAAAZdEVYdENvbW1lbnQAQ3JlYXRlZCB3aXRoIEdJ
TVBXgQ4XAAAD7klEQVRIx72WTUxcVRTHf+/NzJsZpjBQSgqhilAmtMV0IVJN
MI1NUwtdSBp3riRuDFYbY9KNCzfuTAgabdzUpG5NEzG0pk38aozJ2AILbFII
TAqkMwxhGDudD3jvzb0u3vfIwlVv8s+8N3Pv+d9zzv+cM/As1+RRpod6KCbi
SPAQUhR5MBqVPQcOyOcSCdkZj8uOWEzGQiF3T4wmOcRLxfcZm/LbVJyHcZV7
M4KXo6rKJydPMphMkkokONLURJuqWpukBCGgXrdgmuwYBg9qNf4olbi+tsZS
qcQ4PfdnWBt2CSZh+ipc/mBggOnBQVRFgVDIgqJYkNIi8RkPwDDANLmSy/F5
JsOHNE19SfVjBWAIirK9vXVuZATCYQ+hEKiqRQDe7fczbhNgGPQuLtJe0f+Z
w2hTAR5C63t9fZYhVfVuHw5DJGLBT+oQO+T+Z0Xhzc5OHmK2AqgAFeCVZNIL
h5/IT9Jo3O+d7/NsMkkFK6RhJ8mpeDxAkCmXOXrjhquExxMTdEUinjG/4QaS
F0MhV0UqQDwUIu4k0VbL7MaG9wrMrK56iZbSwz7reUd1DkGLpnkStA86BOM9
PQD8kMlYCRYisG8/hIXgUDTqEbRqmndICMq7u/yezxNRFL46dYqoqvJrNkup
WrWUIgTLT55w5u5dmm7eJHr7NkPpNH+VSq7aIrYXni++ArqTy6ELweuHD3NE
0zjb2YkuBLfW1909b6fT/FYoUBMCXUrmy2XeWVpyL3pQ0zyCXad4hADTZDab
tcLT1QWmycXubitMGxuu3u+fPo28cAFx7hxzw8MArOzuepGwV9glsI1LKbm1
uQnApYUFLi0suJt/yuXQdR3Nruh0ocD3uRx3CgUADCldD3d03fOguLdnxbZe
597ODnn7x8ZVMk1+2dwEw2BqZYVX02kUKfkulSIQaiEwhPAIdCEoO+HJ5wH4
5vhx5PnzyNFR5NgYV0+csMKUy4Fp8unqKgATHR1kqlVP0vU6JrC9txdM8iM7
trPb2wD0aVqgz6Rs2f24tYU0DA6FrRodnJ/nreVlWmzV5Pf2WPflwCV4oOtk
q1UWKhUAeiMRr4kZBim7inO6TrpY5Hp/PwOxGJqi8EZzMz/39QHw2dYWfzfO
gwTId/v7+aKl5b99RlECFR4oSEccPpFQr3PZMLi2tk4FqbjtOhuPtz4+dgxF
yqDx/QgcEgd+IqA3m6W9anjt+jW4lqvV+KhQsA77QoOuB9+dvt8waDBNAK7U
ajyqVhkh8u2+I3OguZnJ7m7OxGK8ICXN9sGAF762UpKStUiEPw2Dr/N5Fp8+
DYzM/zX02zRNtmla4LtGOEN/ktHpZ/pP5V+cvFsFB+qGWQAAAABJRU5ErkJg
gg==
Index: moodle/blocks/css_theme_tool/pix/bold.png
MIME: application/octet-stream; encoding: Base64; length: 1240
iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c
6QAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0
SU1FB9oDHgYTGP4ZauMAAAAZdEVYdENvbW1lbnQAQ3JlYXRlZCB3aXRoIEdJ
TVBXgQ4XAAAEM0lEQVRIx5VWTU8bRxh+Zjz22rHp2jhwwCVcHCml36goyaGR
mgsnpERK1Z5QD0ghpb2kEkcuzbGnCpX+gSa9ofSSQKXm0AibSCFqUGtUFaFU
iENiY+Nd767Z9bw9zH5RaGhGHs3MO/PO8349s2b4H626VuGnTw+kOOeCpGQA
IImYZXWcd9957wCv0K5ls9m1RCLhAKCgc86pUCjQ8PAwlUolGhwcpGKxSJqm
hWeEEN18Pr8uhLgWv5AFk0wm861t21+mUinMzc1hdHQUZ8+WUSq9jnw+DwAg
IhAIJEnNidDYa6D2Rw2VSgV3fryDrb+2kMvlvjNNczZE4Zx/DIBmZmbIMA2y
bYtsxybHccJuOzbZtkWW1SGzY5JhGNRut6m136Jms0mNvQbV63WanZ0NvL4a
elAo5NfPnBl5f3V1FZwzMMbBmHJOjQQi3wOSkL4HkqTvjZIF67GxMezv7z9u
t9sfcAAwzc5b09PT6jLGwPzOOffnPJQhHAEGBmVHsFbTiYkJWJb1NgBwAHBd
Nzk+Pu4nRSkdBWGH96P0xZKppJcufQjP81IhAACUy2XfmsgiIQQSiQSEEEgm
k9A0Del0GgMDg5iamkKzuYfI9Khkzp17I0Le2HiaTKfT1Ol0yLIschyHut0u
ua5L8VI9rk9OTqpEt1rUbO5Ro9Ggev0F7e7uEgCqrlUEz2azp/r6+nwdgIKR
6ETSrKysxPQibSES6O/vRy7X18cZYyKfz4dVAgKI5BGAbrcL27axvLwcyorF
ItQxCn7hKIQA5+yUICIWWswASRIcDAR5CEDTtENrzjnm5+cV8ci3PYaQLyhy
Cq/nuY7jKACp0i4BsBMiNDIyoirLZzQClsfdAMClJBkAEPnPgE+el7Xt7W1c
v34dCwsL6mIVBh+E0Gq1QATGu12nqxbR+yJ9ZsabYRgwjDbq9Tpu3/4hlC8u
LkaGhZ4AnueCSFrctm3XdV0YRltR3qe7/JcHASiB4LpeKH/+/EVkuZ9L13Wx
t9eEaZptceH8RQKAZ8/+xuibo2BEIMbA6DBT9df0Y0NVLpch4+Elws7ODgDg
wvmLXsjkWq0GklIdPsaD/2o3Pr9xKLxEhM3aZrgvACCRSHQfPVrTrly9AkYA
MQIjoNFoxK4KKIiwcsiPd/R9UPx5+PBXCCEOPM9Tb5Gmab/dvfsTZK8Xy0Gg
IKP4S4KUUvUwLDI8F4CurPyMbDb7OwAkAKDX69VN0/y0td/C5Y8u+6QJrFIW
I0ijX+eh9XEeEOHrW7fw4JcH6PV6X0gpawmfxZu5XG6wWq2OLy0todfrIZPJ
IJ1JI5lKIsKI6jyo+3a7ja2tLdy7dx83v7qJ5fvL0HX9e9u2vzmSsFQq9Umh
UHiSTCYP4q+mruuk6/pLX1ZN07xSqfTn0NDQZ6/yLwPVtYrY2Hiqrz95nDlm
L3mS/j/rFgXtSI0uZAAAAABJRU5ErkJggg==
Index: moodle/blocks/css_theme_tool/pix/color.png
MIME: application/octet-stream; encoding: Base64; length: 1385
iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c
6QAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0
SU1FB9oDHgYDMm9gsWQAAAAZdEVYdENvbW1lbnQAQ3JlYXRlZCB3aXRoIEdJ
TVBXgQ4XAAAExElEQVRIx5VWSW8URxT+qrp6FmacmWExkscmQhiJoJAFxcdg
QEgIJSQgEWU5BYmDJScHHIkbpyBxSQIEgfIPyCHRYAdibCRsWZbHZjHBVjKI
xIGLg0Cz9rSne+juejlUd8+wSAklPVXVq7fUq/e9183wP8bsXJ6vXbsuwjkX
JCUDAEnEGo0V+8033nqClxiHEonEnKZpNgAKiHNOmUyGenp6KJvNUmdnJ61Z
s4ai0WgoI4RoptPpeSHEoXaDLFjE4/HvLcv6MhKJ4NixY9i6dSs2b+5FNtuN
dDoNACAiEAgkSa2JUCqXUPijgHw+jws/XsDSX0tIJpPnTdMcDL1wzj8CQAMD
A1Q362RZDbJsi2zbDsmyLbKsBjUaK2SumFSv18kwDKrWqlSpVKhULlGxWKTB
wcEg6oNhBJlMen7DhlffnpmZAecMjHEwpoJTM4HIj4AkpB+BJOlHo3jBfvv2
7ajVarcMw3iHA4Bprrx+5MgRZYwxMJ845/6ahzyEM8DAoO4R7NVy7969aDQa
2wCAA4DjOHpfX5+fFKX0vBP29HkrfW3JVNwdO96F67qR0AEA9Pb2+rfxxe//
DU0IcE0DFwJ4+DB0Ekq13b4dMlu2vNbK7+Ligh6LxRCPx0GEEC3s0uWnbsdG
RnxEIpQLBz09d3d3B/UjeCKRWNXR0dFS9md2WTmQ+/er/fCwSnSbNeWIgGe0
hdCwevVqJJMdHZwxJtLpdIgSEEB1A2xqCiQEvDNnQJEI2OQkqFYDfPzj3p+I
7duHZOd6vLJuHTr6d0K7Na/0AQghwDlbxYmItReRJAl29SqY44D6+yG7uiB3
7QJzHLDRUUhfLnr4MMT0NJhtgzkOxMICEoODCDykM6o4ueu5jm3byoGPb/7r
KADAe/89hfEPP1DCIyOhXGNqCkathlqlDGNiAgCg3b/fejb/JYWUJAMHyipB
GxsDAOhHh6AfHQpzqY2Pg+wmSNfVRW7cgMjlICauqTy5rmolRKhWqyAC482m
3VQbdcBu3gR7/PiFnZDV6+CTkyCS0M+eRXLPHoAB5vnzbYhSiXZdB0SywS3L
chzHQb1ugEhCu6Kexzp9CnXDCMn67lsVxaVfIIkQO3kSAGB/+hm0Bw9a9qWE
4zgolyswTdMI+dPT01SulMnZto0IIDOXo2qtGpJ58SIRQN769VQul8jt6VHA
88lLJokAenTnN5rJzwRtvFXJhUIBWF6GWFwEALgbekBShuRu3KgS/egR+PUb
MM+dg7tpE0jX0dzZj8rPPwEAEqdO427hbhiRAABN05rXr89FDxw8gFKx2Kp+
2Vay3VmUSsXWOxOhkp9RXxsffQ//WQYRYfr4cQghnriuqyKIRqN3hodHID0v
bLnSb81EEjJo0ZIgpVTkOyEpQ7kAKOPjV5FIJH4HAA0APM8rmqb5SbVWxe5d
u1XBBwYoaD7k84M2odASGA3kvz5xAhPXJuB53hdSyoLmV/HdZDLZOTs725fL
5eB5HuLxOGLxGPSIjpYPCnFO/lMZhoGlpSWMjl7B0FdDGLsyhlQq9YNlWd88
h/NIJPJxJpO5rev6k/aPfiqVolQqRe28ZykajbrZbPZeV1fX5y/zl4HZubxY
XFxIzd++FX/Bmf5f+v8Cb7gVB+ScH88AAAAASUVORK5CYII=
Index: moodle/blocks/css_theme_tool/pix/italic.png
MIME: application/octet-stream; encoding: Base64; length: 1276
iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c
6QAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0
SU1FB9oDHgYTObJwer0AAAAZdEVYdENvbW1lbnQAQ3JlYXRlZCB3aXRoIEdJ
TVBXgQ4XAAAEV0lEQVRIx5WWv29TVxTHP/f62c6LbdkGkgEnqaIECZL+SBEG
FalIYWGuRNWOHRiQQhcq8Q+UsVOHqiNioRuiC5BKZWhEYksB1VFr1CZKRori
xHZe/Bzsd0+H98MvBKnlSsf3l+/5nu/5ZSv+x1ipLOsTJ0ZSWmtLjFEARkR1
Ovvdjz6ce807jKuZTKaSSCS6gISitZZisSjj4+NSKpVkdHRUjh8/Lul0OvqO
ZVkHhULhmWVZV+MKVbiwbft713W/TqVS3Lp1i5mZGU6dmqZUGqNQKAAgIgiC
GPHXIjR2GtT/rLO8vMy9n+6xsb5BNpv9wXGchQhFa/05INevX5c9Z09ctyNu
15VutxuJ23XFdTvS6eyLs+/I3t6etNttabaasru7K42dhmxvb8vCwkLI+rOI
QbFYeDYx8d7HT58+RWuFUhqlfHL+LIgEDMRgAgZGTMDGPwv3Z8+epdVqrbbb
7XMawHH237927ZqvTClUIFrrYK2jM6IZFArfjnDvL69cuUKn0/kAQAP0er1k
uVwOguI/OgqiDt8PwhcLpn966dKn9Pv9VAQAMD09HVgTs0gpXr16RTabJZlM
kk6nGRoawraHyWQyTE5OMjB9kDKnT5+JgK21tVqyXD6PbduIgFK+r5UoBGFk
ZIRarYbnedy5c4e7d++yuLiIiJDNZn0twqF5bGwsrB/LymQyw7lcLrj1lSoU
IhJZMTk5iYiwubnJhQsXmJqaigXbxHQLAlhWgmPHjpHN5nJaKWUVCoUoSxAQ
MVHGxOdqtUq5XA7OiLLLfxSABLNlWWithi0RUWERocCIQaMQDEpUZNvLf16y
tbXFufI5v9iCQpMwhd9AKBT94rT6Xr/X7XZ9AOOH3QBKwg9/VFYqaK2Zm5uL
VTIQABEDG9AByxgxEQAE2n3lATkAqtUKs7OzDNvDA+vjLCAGIjSbTURQ1sFB
98DfDKwVJHKPn1VQqVQ5f74ciwmHQUycCfT7PURMx3Jdt9fr9djba5PL5fwc
EoUoobHdoNVqYYxhdXWV+fl51tfX0VozMTEeNUBiLESEXq/Hzs4ujuO0Qx/I
0tISM7MzQbkrFIqpqSkajcaRnm7bNrW1GsVi8S0sDJtbW1z85CKAssJH9Xqd
M2dOI0qhBEQJ6+t/x9wWxi6wWPAVIkdi8qL+YtCpARKJxEG1WsEEVvhdMeiQ
gYgx/nmkyAzqxAysFxGWln7DsqzXEUA6nf79wYOfMZ4XtVwTU2LCqjWCMcaX
yC3mMJgIi4u/kMlk/gBIAHiet+04zpfNVpPL85djbhjkOmEYowoeBDUKtAjf
3r7Nk1+f4HneDWNMPRFkwotsNju6srJSvn//Pp7nYds2Q/YQyVSSAUbM50He
t9ttNjY2ePjwETe/ucnjR4/J5/M/uq773ZHsSKVSXxSLxefJZPJ1/Ec/n89L
Pp+X+Nmbkk6n+6VS6a+TJ09+9S7/MlipLFtra7X8s+er9lvukv/1/l/2rTYX
rz9+AAAAAABJRU5ErkJggg==
Index: moodle/blocks/css_theme_tool/pix/transpixel.png
MIME: application/octet-stream; encoding: Base64; length: 101
iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAAXNSR0IArs4c
6QAAAAZiS0dEAP8A/wD/oL2nkwAAAA1JREFUCNdjSL/03wcABmEChRtjS5cA
AAAASUVORK5CYII=
Index: moodle/blocks/css_theme_tool/pix/underline.png
MIME: application/octet-stream; encoding: Base64; length: 1254
iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c
6QAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0
SU1FB9oDHgYaOWOywfQAAAAZdEVYdENvbW1lbnQAQ3JlYXRlZCB3aXRoIEdJ
TVBXgQ4XAAAEQUlEQVRIx5WWS2hUVxjHf+fOnUluZuK8koBOdCxE0LTpIzRF
hQp147pgaZcjTHEg7caCS7uooEJXXZQKxq3duVQ3ddFgEkEFA42UxrqV5jGP
m7kTZ+Z8XdxzH2OE1gPnnsd3z/l/3/c//3Ov4n+U5ZUla2xsPGVZli1aKwAt
otrtnc4H73/4ircoZ9Pp9EoikegAElTLsiSfz8vBgwelVCrJxMSEFItFGRoa
Ct+xbXs3l8s9tm37bHxDFXQcx/nJ87xvU6kUFy9eZHp6miNHpiiVJsnlcgCI
CIIgWvy+CJtbm6z9scbS0hK3fr3F+l/rZDKZn13XnQ9RLMv6ApBarSYttyWe
1xav40mn0wmr1/HE89rSbu+Iu+NKq9WSZrMp9UZdtre3ZXNrUzY2NmR+fj6I
+vMwgnw+9/jQofJHDx48wLIUSlko5Qfnt4KIiUA02kSgRZto/LlgPDs7S6PR
eNRsNj+2AFx3571qtepvphTKVMuyTN8K5whbUCh8P4Kx3z1z5gztdnsGwALo
drvJubk5Q4q/aC+IGrRH9MXI9GdPnfqUXq+XCgEApqamjDcxj5QikUhg23Ys
XX5Jp9OMjo4SuR4dmaNHj0X8rq4+TQ4PD+M4DiKEpwWT88Eixv7G6bCdnJwM
9GNb6XR6xPfELA43kQEAfxzZ94DGVtt2gkKhQCYzOmorpexcLme8EpQoBG2y
9zqAwB5gs72Yt01r2zaWpUZsEVFhWhRo0Vj4IEqinGvRIKCN2CL/Y5HFEHJ5
X5x2r9/rdjodH8A4rgElwcNsFKgXQfd1RHo8MgMWhQOW1qIDABFzDcTE4zgO
ANvbW6H95cuXAOzbty+6Pky+fBChXq8jgrJ2dzu7/iC6X3SgTK0pl8sAXLr0
PRsb/+C6LteuXQVgZmZm0LEwEuj1uojotuV5Xrfb7dJqNX2vjdy1aESESqUC
wMLCAocPv8P+/fu5cWMBgFrt/GB6DJfdbpetrW1c120GLMri4iLT704buStf
k8Z6c+Em169f5/nz5wCUy2WqX1epVqsDkQep/fvFC06eOAmg7IDEtbU1jh07
iiiFEhAlIcfnzlWonKsY7oKjaohHBkFEeLb2LFIyQCKR2H34cMU/giY9V69c
IZ8vhLWQL1AoFCgWihSLY4yNjTE+Ps7E+AS187XQexFhcfF3bNt+Fd4eIyMj
K5lM5pPV1acoywrTo+JalejcB0Qie70XEY4fP0Gr1XrSaDRmEwD9fn/Ddd2v
6o06pz87HUuDOdf+Y0BMIhGpcYX/cPky93+7T7/f/0ZrvZYwKn6WyWQmlpeX
527fvk2/38dxHIadYZKpJBFGLOcmgmazyfr6Onfu3OXCdxe4d/ce2Wz2F8/z
ftzzxU+lUl/m8/knyWTyVfyjn81mJZvNSnzu9To0NNQrlUp/HjhwoPI2fxks
ryzZq6tPs4+fPHLeYEv+1/p/AVC26pved/EiAAAAAElFTkSuQmCC
Index: moodle/blocks/css_theme_tool/styles.css
--- moodle/blocks/css_theme_tool/styles.css No Base Revision
+++ moodle/blocks/css_theme_tool/styles.css Locally New
@@ -0,0 +1,72 @@
+/*.css_builder {background-color:#DDD;border:3px solid blue;}*/
+.css_builder .css_selectors {background-color:#FFF;margin:10px;border:1px solid #BBB;}
+.css_builder .css_selectors .tagselectorbox {border-top:1px solid #DDD;overflow:hidden;font-size:8pt;clear:both;padding:2px;}
+.css_builder .css_selectors .tagselectorbox:first-child {border-top-width:0px;}
+.css_builder .css_selectors .tagselectorbox input[type=checkbox] {font-size:6pt;vertical-align: middle;margin-right:2px;}
+.css_builder .css_selectors .tagselectorbox label {color:#333;}
+.css_builder .css_selectors .tagselectorbox .isselected label {font-weight:bold;}
+.css_builder .css_selectors .tagselectorbox .tagname {width:50px;float:left;}
+.css_builder .css_selectors .tagselectorbox .tagid {width:120px;float:left;}
+.css_builder .css_selectors .tagselectorbox .tagclasses {margin-left:170px;overflow:hidden;}
+.css_builder .css_selectors .tagselectorbox .tagclasses .tagclass {float:left;min-width:33%;}
+
+.css_builder .css_current_selector .tagname,
+.css_builder .css_selectors .tagselectorbox .tagname.isselected label {color:#006BC4;}
+.css_builder .css_current_selector .tagid,
+.css_builder .css_selectors .tagselectorbox .tagid.isselected label {color:#760000;}
+.css_builder .css_current_selector .tagclass,
+.css_builder .css_selectors .tagselectorbox .tagclasses .tagclass.isselected label {color:#097800;}
+
+.css_builder .css_selector_controls {background-color:#EFEFEF;height:24px;margin:0 10px 10px 10px;}
+.css_builder .css_selector_controls .control {float:left;width:24px;height:24px;background-color:#DDD;}
+.css_builder .css_selector_controls .control.backgroundcolourpicker {background-image:url([[pix:block_css_theme_tool|backgroundColor]]);}
+.css_builder .css_selector_controls .control.colourpicker {background-image:url([[pix:block_css_theme_tool|color]]);}
+.css_builder .css_selector_controls .control.bold {background-image:url([[pix:block_css_theme_tool|bold]]);}
+.css_builder .css_selector_controls .control.italic {background-image:url([[pix:block_css_theme_tool|italic]]);}
+.css_builder .css_selector_controls .control.underline {background-image:url([[pix:block_css_theme_tool|underline]]);}
+.css_builder .css_selector_controls .control.alignleft {background-image:url([[pix:block_css_theme_tool|alignLeft]]);}
+.css_builder .css_selector_controls .control.aligncenter {background-image:url([[pix:block_css_theme_tool|alignCenter]]);}
+.css_builder .css_selector_controls .control.alignright {background-image:url([[pix:block_css_theme_tool|alignRight]]);}
+
+.css_builder .css_selector_controls .colorpickerdialogue {margin:0;height:24px;width:766px;background-color:#FFF;border:1px solid #000;background-image:url(/m20dev3/blocks/css_theme_tool/colourpicker_image.php);visibility:hidden;position:absolute;}
+
+.css_builder .css_current_selector {font-weight:bold;background-color:#FFF;margin:10px 10px 0 10px;border:1px solid #BBB;min-height:1em;font-size:8pt;color:#333;padding:5px;}
+.css_builder .css_current_selector ul {margin:2px 16px;}
+.css_builder .css_current_selector ul li {list-style: none;margin:2px;}
+.css_builder .css_current_selector ul li .addnewstyle {color:#AAA;padding:2px 0px;}
+.css_builder .css_current_selector ul li.hideme {visibility:hidden;display:none;height:1px;}
+.css_builder .css_current_selector ul li input[type=text] {width:100%;border:1px inset #bbb;padding:1px;}
+.css_builder .css_current_selector ul li .icon {margin-right:5px;vertical-align:middle;cursor:pointer;}
+
+#mousemovetrackdiv {background-color:#B2E8FF;z-index:50;}
+#mousemovetrackdiv div {position:fixed;border:0 solid #00A8EF;background-image:url([[pix:block_css_theme_tool|transpixel]])}
+#mousemovetrackdiv div.top {top:0;left:0;width:100%;height:0;border-bottom-width:1px;margin-bottom:1px;}
+#mousemovetrackdiv div.bottom {bottom:0;left:0;width:100%;height:0;border-top-width:1px;margin-top:1px;}
+#mousemovetrackdiv div.left {top:0;left:0;height:100%;width:0;border-right-width:1px;margin-right:1px;}
+#mousemovetrackdiv div.right {top:0;right:0;height:100%;width:0;border-left-width:1px;margin-left:1px;}
+
+.viewcssbox {margin:10px;border:1px solid #ddd;background-color:#fff;padding:7px 0;}
+.viewcssbox .cssrule {margin:3px 10px;}
+.viewcssbox .cssrule .selector {font-weight:bold;color:#097800;}
+.viewcssbox .cssrule .icon {margin-right:5px;vertical-align:middle;cursor:pointer;}
+
+.block_css_theme_tool {visibility: hidden;}
+.jsenabled .block_css_theme_tool {visibility: visible;}
+.block_css_theme_tool .content {text-align:center;}
+.block_css_theme_tool .content input {width:90%;margin:3px;border:1px outset #00b3ff; background-color:#D6F3FF;font-size:8pt;-moz-border-radius:5px;}
+.block_css_theme_tool .content .cssnotsupported {font-size:80%;color:#A00;}
+.block_css_theme_tool .content .cssnotsupported hr {border-width:1px 0 0 0;border-color:#A00;width:70%;margin:2px 15%;}
+
+.yui3-overlay .yui3-widget-bd.css_builder_overlay {background-color:#EEE;padding:10px;border:1px solid #333;}
+.yui3-overlay .yui3-widget-bd.colourpicker_preview {background-color:#FFF;border:2px solid #000;}
+
+.cssbuilder_bodywash {position:fixed;top:0px;left:0px;width:100%;height:100%;background-color:#FFF;opacity:0.7;}
+.cssbuilder_cellhighlight {border:1px solid #00b3ff;background-color:#D6F3FF;position:absolute;z-index:10000;}
+
+.settingscontainer {}
+.settingscontainer .settingbox {overflow:hidden;background-color:#FFF;border:1px solid #DDD;padding:8px;border-top-width:0;}
+.settingscontainer .settingbox .settingdescription {margin-left:150px;}
+.settingscontainer .settingbox input {float:left;}
+.settingscontainer .settingbox input[type=button] {width:140px;}
+.settingscontainer .settingbox input[type=checkbox] {margin-left:130px;}
+.settingscontainer .overlaycontrols {overflow:hidden;text-align:center;background-color:#E6E6E6;border:1px solid #DDD;padding:4px;border-top-width:0;}
Index: moodle/blocks/css_theme_tool/styles.php
--- moodle/blocks/css_theme_tool/styles.php No Base Revision
+++ moodle/blocks/css_theme_tool/styles.php Locally New
@@ -0,0 +1,15 @@
+<?php
+
+/**
+ * Outputs the rules created by the CSS theme tool as a CSS file
+ *
+ * @package   blocks
+ * @subpackage css_theme_tool
+ * @copyright 2010 Sam Hemelryk <sam.hemelryk@gmail.com>
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+require_once('../../config.php');
+require_once($CFG->dirroot.'/blocks/css_theme_tool/lib.php');
+
+css_theme_tool::output_css();
