Anonymous avatar Anonymous committed 831d439

using static instead of media

Comments (0)

Files changed (54)

  * django-cms2, tested on r172 or later
  * lxml, tested with 2.2.4
  * pyquery, tested with 0.3.1
- * Javascript dependencies are included in media/
+ * Javascript dependencies are included in static/
 
 Settings
 ========
  * Normal Django settings
  * Normal django-cms2 settings
  * INSTALLED_APPS - add "semanticeditor"
- * SEMANTICEDITOR_MEDIA_URL = os.path.join(MEDIA_URL, "semanticeditor/")
+ * SEMANTICEDITOR_MEDIA_URL = os.path.join(STATIC_URL, "semanticeditor/")
 
 Templates
 =========
 =====
 
 Media files: media/semanticeditor needs to be copied/linked/served so that it is
-under MEDIA_URL.  (i.e. side-by-side with the media/cms directory).
+under STATIC_URL.
 
 URLS
 ====
 include LICENSE
 include MANIFEST.in
 recursive-include semanticeditor/templates *
-recursive-include semanticeditor/media *
+recursive-include semanticeditor/static *

semanticeditor/media/semanticeditor/javascript/jquery.query-2.1.7.js

-/**
- * jQuery.query - Query String Modification and Creation for jQuery
- * Written by Blair Mitchelmore (blair DOT mitchelmore AT gmail DOT com)
- * Licensed under the WTFPL (http://sam.zoy.org/wtfpl/).
- * Date: 2009/8/13
- *
- * @author Blair Mitchelmore
- * @version 2.1.7
- *
- **/
-new function(settings) { 
-  // Various Settings
-  var $separator = settings.separator || '&';
-  var $spaces = settings.spaces === false ? false : true;
-  var $suffix = settings.suffix === false ? '' : '[]';
-  var $prefix = settings.prefix === false ? false : true;
-  var $hash = $prefix ? settings.hash === true ? "#" : "?" : "";
-  var $numbers = settings.numbers === false ? false : true;
-  
-  jQuery.query = new function() {
-    var is = function(o, t) {
-      return o != undefined && o !== null && (!!t ? o.constructor == t : true);
-    };
-    var parse = function(path) {
-      var m, rx = /\[([^[]*)\]/g, match = /^([^[]+)(\[.*\])?$/.exec(path), base = match[1], tokens = [];
-      while (m = rx.exec(match[2])) tokens.push(m[1]);
-      return [base, tokens];
-    };
-    var set = function(target, tokens, value) {
-      var o, token = tokens.shift();
-      if (typeof target != 'object') target = null;
-      if (token === "") {
-        if (!target) target = [];
-        if (is(target, Array)) {
-          target.push(tokens.length == 0 ? value : set(null, tokens.slice(0), value));
-        } else if (is(target, Object)) {
-          var i = 0;
-          while (target[i++] != null);
-          target[--i] = tokens.length == 0 ? value : set(target[i], tokens.slice(0), value);
-        } else {
-          target = [];
-          target.push(tokens.length == 0 ? value : set(null, tokens.slice(0), value));
-        }
-      } else if (token && token.match(/^\s*[0-9]+\s*$/)) {
-        var index = parseInt(token, 10);
-        if (!target) target = [];
-        target[index] = tokens.length == 0 ? value : set(target[index], tokens.slice(0), value);
-      } else if (token) {
-        var index = token.replace(/^\s*|\s*$/g, "");
-        if (!target) target = {};
-        if (is(target, Array)) {
-          var temp = {};
-          for (var i = 0; i < target.length; ++i) {
-            temp[i] = target[i];
-          }
-          target = temp;
-        }
-        target[index] = tokens.length == 0 ? value : set(target[index], tokens.slice(0), value);
-      } else {
-        return value;
-      }
-      return target;
-    };
-    
-    var queryObject = function(a) {
-      var self = this;
-      self.keys = {};
-      
-      if (a.queryObject) {
-        jQuery.each(a.get(), function(key, val) {
-          self.SET(key, val);
-        });
-      } else {
-        jQuery.each(arguments, function() {
-          var q = "" + this;
-          q = q.replace(/^[?#]/,''); // remove any leading ? || #
-          q = q.replace(/[;&]$/,''); // remove any trailing & || ;
-          if ($spaces) q = q.replace(/[+]/g,' '); // replace +'s with spaces
-          
-          jQuery.each(q.split(/[&;]/), function(){
-            var key = decodeURIComponent(this.split('=')[0] || "");
-            var val = decodeURIComponent(this.split('=')[1] || "");
-            
-            if (!key) return;
-            
-            if ($numbers) {
-              if (/^[+-]?[0-9]+\.[0-9]*$/.test(val)) // simple float regex
-                val = parseFloat(val);
-              else if (/^[+-]?[0-9]+$/.test(val)) // simple int regex
-                val = parseInt(val, 10);
-            }
-            
-            val = (!val && val !== 0) ? true : val;
-            
-            if (val !== false && val !== true && typeof val != 'number')
-              val = val;
-            
-            self.SET(key, val);
-          });
-        });
-      }
-      return self;
-    };
-    
-    queryObject.prototype = {
-      queryObject: true,
-      has: function(key, type) {
-        var value = this.get(key);
-        return is(value, type);
-      },
-      GET: function(key) {
-        if (!is(key)) return this.keys;
-        var parsed = parse(key), base = parsed[0], tokens = parsed[1];
-        var target = this.keys[base];
-        while (target != null && tokens.length != 0) {
-          target = target[tokens.shift()];
-        }
-        return typeof target == 'number' ? target : target || "";
-      },
-      get: function(key) {
-        var target = this.GET(key);
-        if (is(target, Object))
-          return jQuery.extend(true, {}, target);
-        else if (is(target, Array))
-          return target.slice(0);
-        return target;
-      },
-      SET: function(key, val) {
-        var value = !is(val) ? null : val;
-        var parsed = parse(key), base = parsed[0], tokens = parsed[1];
-        var target = this.keys[base];
-        this.keys[base] = set(target, tokens.slice(0), value);
-        return this;
-      },
-      set: function(key, val) {
-        return this.copy().SET(key, val);
-      },
-      REMOVE: function(key) {
-        return this.SET(key, null).COMPACT();
-      },
-      remove: function(key) {
-        return this.copy().REMOVE(key);
-      },
-      EMPTY: function() {
-        var self = this;
-        jQuery.each(self.keys, function(key, value) {
-          delete self.keys[key];
-        });
-        return self;
-      },
-      load: function(url) {
-        var hash = url.replace(/^.*?[#](.+?)(?:\?.+)?$/, "$1");
-        var search = url.replace(/^.*?[?](.+?)(?:#.+)?$/, "$1");
-        return new queryObject(url.length == search.length ? '' : search, url.length == hash.length ? '' : hash);
-      },
-      empty: function() {
-        return this.copy().EMPTY();
-      },
-      copy: function() {
-        return new queryObject(this);
-      },
-      COMPACT: function() {
-        function build(orig) {
-          var obj = typeof orig == "object" ? is(orig, Array) ? [] : {} : orig;
-          if (typeof orig == 'object') {
-            function add(o, key, value) {
-              if (is(o, Array))
-                o.push(value);
-              else
-                o[key] = value;
-            }
-            jQuery.each(orig, function(key, value) {
-              if (!is(value)) return true;
-              add(obj, key, build(value));
-            });
-          }
-          return obj;
-        }
-        this.keys = build(this.keys);
-        return this;
-      },
-      compact: function() {
-        return this.copy().COMPACT();
-      },
-      toString: function() {
-        var i = 0, queryString = [], chunks = [], self = this;
-        var encode = function(str) {
-          str = str + "";
-          if ($spaces) str = str.replace(/ /g, "+");
-          return encodeURIComponent(str);
-        };
-        var addFields = function(arr, key, value) {
-          if (!is(value) || value === false) return;
-          var o = [encode(key)];
-          if (value !== true) {
-            o.push("=");
-            o.push(encode(value));
-          }
-          arr.push(o.join(""));
-        };
-        var build = function(obj, base) {
-          var newKey = function(key) {
-            return !base || base == "" ? [key].join("") : [base, "[", key, "]"].join("");
-          };
-          jQuery.each(obj, function(key, value) {
-            if (typeof value == 'object') 
-              build(value, newKey(key));
-            else
-              addFields(chunks, newKey(key), value);
-          });
-        };
-        
-        build(this.keys);
-        
-        if (chunks.length > 0) queryString.push($hash);
-        queryString.push(chunks.join($separator));
-        
-        return queryString.join("");
-      }
-    };
-    
-    return new queryObject(location.search, location.hash);
-  };
-}(jQuery.query || {}); // Pass in jQuery.query as settings object

semanticeditor/media/semanticeditor/javascript/json2.js

-/*
-    http://www.JSON.org/json2.js
-    2008-11-19
-
-    Public Domain.
-
-    NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK.
-
-    See http://www.JSON.org/js.html
-
-    This file creates a global JSON object containing two methods: stringify
-    and parse.
-
-        JSON.stringify(value, replacer, space)
-            value       any JavaScript value, usually an object or array.
-
-            replacer    an optional parameter that determines how object
-                        values are stringified for objects. It can be a
-                        function or an array of strings.
-
-            space       an optional parameter that specifies the indentation
-                        of nested structures. If it is omitted, the text will
-                        be packed without extra whitespace. If it is a number,
-                        it will specify the number of spaces to indent at each
-                        level. If it is a string (such as '\t' or '&nbsp;'),
-                        it contains the characters used to indent at each level.
-
-            This method produces a JSON text from a JavaScript value.
-
-            When an object value is found, if the object contains a toJSON
-            method, its toJSON method will be called and the result will be
-            stringified. A toJSON method does not serialize: it returns the
-            value represented by the name/value pair that should be serialized,
-            or undefined if nothing should be serialized. The toJSON method
-            will be passed the key associated with the value, and this will be
-            bound to the object holding the key.
-
-            For example, this would serialize Dates as ISO strings.
-
-                Date.prototype.toJSON = function (key) {
-                    function f(n) {
-                        // Format integers to have at least two digits.
-                        return n < 10 ? '0' + n : n;
-                    }
-
-                    return this.getUTCFullYear()   + '-' +
-                         f(this.getUTCMonth() + 1) + '-' +
-                         f(this.getUTCDate())      + 'T' +
-                         f(this.getUTCHours())     + ':' +
-                         f(this.getUTCMinutes())   + ':' +
-                         f(this.getUTCSeconds())   + 'Z';
-                };
-
-            You can provide an optional replacer method. It will be passed the
-            key and value of each member, with this bound to the containing
-            object. The value that is returned from your method will be
-            serialized. If your method returns undefined, then the member will
-            be excluded from the serialization.
-
-            If the replacer parameter is an array of strings, then it will be
-            used to select the members to be serialized. It filters the results
-            such that only members with keys listed in the replacer array are
-            stringified.
-
-            Values that do not have JSON representations, such as undefined or
-            functions, will not be serialized. Such values in objects will be
-            dropped; in arrays they will be replaced with null. You can use
-            a replacer function to replace those with JSON values.
-            JSON.stringify(undefined) returns undefined.
-
-            The optional space parameter produces a stringification of the
-            value that is filled with line breaks and indentation to make it
-            easier to read.
-
-            If the space parameter is a non-empty string, then that string will
-            be used for indentation. If the space parameter is a number, then
-            the indentation will be that many spaces.
-
-            Example:
-
-            text = JSON.stringify(['e', {pluribus: 'unum'}]);
-            // text is '["e",{"pluribus":"unum"}]'
-
-
-            text = JSON.stringify(['e', {pluribus: 'unum'}], null, '\t');
-            // text is '[\n\t"e",\n\t{\n\t\t"pluribus": "unum"\n\t}\n]'
-
-            text = JSON.stringify([new Date()], function (key, value) {
-                return this[key] instanceof Date ?
-                    'Date(' + this[key] + ')' : value;
-            });
-            // text is '["Date(---current time---)"]'
-
-
-        JSON.parse(text, reviver)
-            This method parses a JSON text to produce an object or array.
-            It can throw a SyntaxError exception.
-
-            The optional reviver parameter is a function that can filter and
-            transform the results. It receives each of the keys and values,
-            and its return value is used instead of the original value.
-            If it returns what it received, then the structure is not modified.
-            If it returns undefined then the member is deleted.
-
-            Example:
-
-            // Parse the text. Values that look like ISO date strings will
-            // be converted to Date objects.
-
-            myData = JSON.parse(text, function (key, value) {
-                var a;
-                if (typeof value === 'string') {
-                    a =
-/^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*)?)Z$/.exec(value);
-                    if (a) {
-                        return new Date(Date.UTC(+a[1], +a[2] - 1, +a[3], +a[4],
-                            +a[5], +a[6]));
-                    }
-                }
-                return value;
-            });
-
-            myData = JSON.parse('["Date(09/09/2001)"]', function (key, value) {
-                var d;
-                if (typeof value === 'string' &&
-                        value.slice(0, 5) === 'Date(' &&
-                        value.slice(-1) === ')') {
-                    d = new Date(value.slice(5, -1));
-                    if (d) {
-                        return d;
-                    }
-                }
-                return value;
-            });
-
-
-    This is a reference implementation. You are free to copy, modify, or
-    redistribute.
-
-    This code should be minified before deployment.
-    See http://javascript.crockford.com/jsmin.html
-
-    USE YOUR OWN COPY. IT IS EXTREMELY UNWISE TO LOAD CODE FROM SERVERS YOU DO
-    NOT CONTROL.
-*/
-
-/*jslint evil: true */
-
-/*global JSON */
-
-/*members "", "\b", "\t", "\n", "\f", "\r", "\"", JSON, "\\", apply,
-    call, charCodeAt, getUTCDate, getUTCFullYear, getUTCHours,
-    getUTCMinutes, getUTCMonth, getUTCSeconds, hasOwnProperty, join,
-    lastIndex, length, parse, prototype, push, replace, slice, stringify,
-    test, toJSON, toString, valueOf
-*/
-
-// Create a JSON object only if one does not already exist. We create the
-// methods in a closure to avoid creating global variables.
-
-if (!this.JSON) {
-    JSON = {};
-}
-(function () {
-
-    function f(n) {
-        // Format integers to have at least two digits.
-        return n < 10 ? '0' + n : n;
-    }
-
-    if (typeof Date.prototype.toJSON !== 'function') {
-
-        Date.prototype.toJSON = function (key) {
-
-            return this.getUTCFullYear()   + '-' +
-                 f(this.getUTCMonth() + 1) + '-' +
-                 f(this.getUTCDate())      + 'T' +
-                 f(this.getUTCHours())     + ':' +
-                 f(this.getUTCMinutes())   + ':' +
-                 f(this.getUTCSeconds())   + 'Z';
-        };
-
-        String.prototype.toJSON =
-        Number.prototype.toJSON =
-        Boolean.prototype.toJSON = function (key) {
-            return this.valueOf();
-        };
-    }
-
-    var cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
-        escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
-        gap,
-        indent,
-        meta = {    // table of character substitutions
-            '\b': '\\b',
-            '\t': '\\t',
-            '\n': '\\n',
-            '\f': '\\f',
-            '\r': '\\r',
-            '"' : '\\"',
-            '\\': '\\\\'
-        },
-        rep;
-
-
-    function quote(string) {
-
-// If the string contains no control characters, no quote characters, and no
-// backslash characters, then we can safely slap some quotes around it.
-// Otherwise we must also replace the offending characters with safe escape
-// sequences.
-
-        escapable.lastIndex = 0;
-        return escapable.test(string) ?
-            '"' + string.replace(escapable, function (a) {
-                var c = meta[a];
-                return typeof c === 'string' ? c :
-                    '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
-            }) + '"' :
-            '"' + string + '"';
-    }
-
-
-    function str(key, holder) {
-
-// Produce a string from holder[key].
-
-        var i,          // The loop counter.
-            k,          // The member key.
-            v,          // The member value.
-            length,
-            mind = gap,
-            partial,
-            value = holder[key];
-
-// If the value has a toJSON method, call it to obtain a replacement value.
-
-        if (value && typeof value === 'object' &&
-                typeof value.toJSON === 'function') {
-            value = value.toJSON(key);
-        }
-
-// If we were called with a replacer function, then call the replacer to
-// obtain a replacement value.
-
-        if (typeof rep === 'function') {
-            value = rep.call(holder, key, value);
-        }
-
-// What happens next depends on the value's type.
-
-        switch (typeof value) {
-        case 'string':
-            return quote(value);
-
-        case 'number':
-
-// JSON numbers must be finite. Encode non-finite numbers as null.
-
-            return isFinite(value) ? String(value) : 'null';
-
-        case 'boolean':
-        case 'null':
-
-// If the value is a boolean or null, convert it to a string. Note:
-// typeof null does not produce 'null'. The case is included here in
-// the remote chance that this gets fixed someday.
-
-            return String(value);
-
-// If the type is 'object', we might be dealing with an object or an array or
-// null.
-
-        case 'object':
-
-// Due to a specification blunder in ECMAScript, typeof null is 'object',
-// so watch out for that case.
-
-            if (!value) {
-                return 'null';
-            }
-
-// Make an array to hold the partial results of stringifying this object value.
-
-            gap += indent;
-            partial = [];
-
-// Is the value an array?
-
-            if (Object.prototype.toString.apply(value) === '[object Array]') {
-
-// The value is an array. Stringify every element. Use null as a placeholder
-// for non-JSON values.
-
-                length = value.length;
-                for (i = 0; i < length; i += 1) {
-                    partial[i] = str(i, value) || 'null';
-                }
-
-// Join all of the elements together, separated with commas, and wrap them in
-// brackets.
-
-                v = partial.length === 0 ? '[]' :
-                    gap ? '[\n' + gap +
-                            partial.join(',\n' + gap) + '\n' +
-                                mind + ']' :
-                          '[' + partial.join(',') + ']';
-                gap = mind;
-                return v;
-            }
-
-// If the replacer is an array, use it to select the members to be stringified.
-
-            if (rep && typeof rep === 'object') {
-                length = rep.length;
-                for (i = 0; i < length; i += 1) {
-                    k = rep[i];
-                    if (typeof k === 'string') {
-                        v = str(k, value);
-                        if (v) {
-                            partial.push(quote(k) + (gap ? ': ' : ':') + v);
-                        }
-                    }
-                }
-            } else {
-
-// Otherwise, iterate through all of the keys in the object.
-
-                for (k in value) {
-                    if (Object.hasOwnProperty.call(value, k)) {
-                        v = str(k, value);
-                        if (v) {
-                            partial.push(quote(k) + (gap ? ': ' : ':') + v);
-                        }
-                    }
-                }
-            }
-
-// Join all of the member texts together, separated with commas,
-// and wrap them in braces.
-
-            v = partial.length === 0 ? '{}' :
-                gap ? '{\n' + gap + partial.join(',\n' + gap) + '\n' +
-                        mind + '}' : '{' + partial.join(',') + '}';
-            gap = mind;
-            return v;
-        }
-    }
-
-// If the JSON object does not yet have a stringify method, give it one.
-
-    if (typeof JSON.stringify !== 'function') {
-        JSON.stringify = function (value, replacer, space) {
-
-// The stringify method takes a value and an optional replacer, and an optional
-// space parameter, and returns a JSON text. The replacer can be a function
-// that can replace values, or an array of strings that will select the keys.
-// A default replacer method can be provided. Use of the space parameter can
-// produce text that is more easily readable.
-
-            var i;
-            gap = '';
-            indent = '';
-
-// If the space parameter is a number, make an indent string containing that
-// many spaces.
-
-            if (typeof space === 'number') {
-                for (i = 0; i < space; i += 1) {
-                    indent += ' ';
-                }
-
-// If the space parameter is a string, it will be used as the indent string.
-
-            } else if (typeof space === 'string') {
-                indent = space;
-            }
-
-// If there is a replacer, it must be a function or an array.
-// Otherwise, throw an error.
-
-            rep = replacer;
-            if (replacer && typeof replacer !== 'function' &&
-                    (typeof replacer !== 'object' ||
-                     typeof replacer.length !== 'number')) {
-                throw new Error('JSON.stringify');
-            }
-
-// Make a fake root object containing our value under the key of ''.
-// Return the result of stringifying the value.
-
-            return str('', {'': value});
-        };
-    }
-
-
-// If the JSON object does not yet have a parse method, give it one.
-
-    if (typeof JSON.parse !== 'function') {
-        JSON.parse = function (text, reviver) {
-
-// The parse method takes a text and an optional reviver function, and returns
-// a JavaScript value if the text is a valid JSON text.
-
-            var j;
-
-            function walk(holder, key) {
-
-// The walk method is used to recursively walk the resulting structure so
-// that modifications can be made.
-
-                var k, v, value = holder[key];
-                if (value && typeof value === 'object') {
-                    for (k in value) {
-                        if (Object.hasOwnProperty.call(value, k)) {
-                            v = walk(value, k);
-                            if (v !== undefined) {
-                                value[k] = v;
-                            } else {
-                                delete value[k];
-                            }
-                        }
-                    }
-                }
-                return reviver.call(holder, key, value);
-            }
-
-
-// Parsing happens in four stages. In the first stage, we replace certain
-// Unicode characters with escape sequences. JavaScript handles many characters
-// incorrectly, either silently deleting them, or treating them as line endings.
-
-            cx.lastIndex = 0;
-            if (cx.test(text)) {
-                text = text.replace(cx, function (a) {
-                    return '\\u' +
-                        ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
-                });
-            }
-
-// In the second stage, we run the text against regular expressions that look
-// for non-JSON patterns. We are especially concerned with '()' and 'new'
-// because they can cause invocation, and '=' because it can cause mutation.
-// But just to be safe, we want to reject all unexpected forms.
-
-// We split the second stage into 4 regexp operations in order to work around
-// crippling inefficiencies in IE's and Safari's regexp engines. First we
-// replace the JSON backslash pairs with '@' (a non-JSON character). Second, we
-// replace all simple value tokens with ']' characters. Third, we delete all
-// open brackets that follow a colon or comma or that begin the text. Finally,
-// we look to see that the remaining characters are only whitespace or ']' or
-// ',' or ':' or '{' or '}'. If that is so, then the text is safe for eval.
-
-            if (/^[\],:{}\s]*$/.
-test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@').
-replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']').
-replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) {
-
-// In the third stage we use the eval function to compile the text into a
-// JavaScript structure. The '{' operator is subject to a syntactic ambiguity
-// in JavaScript: it can begin a block or an object literal. We wrap the text
-// in parens to eliminate the ambiguity.
-
-                j = eval('(' + text + ')');
-
-// In the optional fourth stage, we recursively walk the new structure, passing
-// each name/value pair to a reviver function for possible transformation.
-
-                return typeof reviver === 'function' ?
-                    walk({'': j}, '') : j;
-            }
-
-// If the text is not JSON parseable, then a SyntaxError is thrown.
-
-            throw new SyntaxError('JSON.parse');
-        };
-    }
-})();

semanticeditor/media/semanticeditor/javascript/wymeditor/plugins/semantic/wymeditor.semantic.js

-/*
- * Plugin for WYMEditor that provides an interface to allow separation
- * of presentation and content by allowing the user to select presentation
- * elements for each section.  Depends on a server-side backend to do
- * parsing and provide list of allowed CSS classes.
- */
-
-function PresentationControls(wym, opts) {
-    this.wym = wym;
-    this.opts = opts;
-    this.name = wym._element.get(0).name;
-    // availableStyles: an array of dictionaries corresponding to PresentationInfo objects
-    // (PresentationClass objects specifically)
-    this.availableStyles = new Array();
-    // presentationInfo: a dictionary of { sectId : [PresentationInfo] }
-    this.presentationInfo = {};
-
-    // commands: an array of dictionaries corresponding to PresentationInfo objects
-    // (PresentationCommand objects specifically)
-    this.commands = new Array();
-    // commandDict: a dictionary mapping command name to PresentationInfo object.
-    this.commandDict = {};
-
-    // a selector which matches any command block.  Filled in later.
-    this.allCommandSelectors = "";
-
-    // the node of the current block level element being edited.
-    this.currentNode = null;
-
-    // Need to sync with presentation.py
-    this.blockdefSelector = "h1,h2,h3,h4,h5,h6,p,ol,ul,blockquote,li,pre";
-
-    this.setupControls(jQuery(wym._bvox).find(".wym_area_bottom"));
-}
-
-// ---- Setup and loading ----
-
-// -- Setup static controls
-
-PresentationControls.prototype.setupControls = function(container) {
-    var idPrefix = "id_prescontrol_" + this.name + "_";
-    var previewButtonId = idPrefix + 'previewbutton';
-    var showStylesButtonId = idPrefix + 'showstyles';
-    var previewBoxId = idPrefix + 'previewbox';
-    var hidePreviewButtonId = idPrefix + 'hidepreviewbutton';
-    var cleanHtmlButtonId = idPrefix + 'cleanhtmlbutton';
-    var self = this;
-
-    // Create elements
-    container.after(
-        "<div class=\"prescontrol\">" +
-            "<input type=\"submit\" value=\"Clean pasted HTML\" id=\"" + cleanHtmlButtonId  +  "\" />" +
-            "<input type=\"submit\" value=\"Preview\" id=\"" + previewButtonId + "\" />" +
-            "<input type=\"submit\" id=\"" + showStylesButtonId + "\" />" +
-            "<div class=\"prescontrolerror\" id=\"" + idPrefix + "errorbox" + "\"></div>" +
-        "</div>");
-
-    jQuery("body").append("<div style=\"position: absolute; display: none\" class=\"previewbox\" id=\"" +  previewBoxId + "\">" +
-                          "<div class=\"closebar\">" +
-                          "<input type=\"submit\" value=\"Close\" id=\"" + hidePreviewButtonId + "\">" +
-                          "</div>" +
-                          "<div class=\"content\"></div>" +
-                          "</div>");
-
-    this.classList = jQuery(this.wym._options.classesSelector).find("ul");
-    this.commandList = jQuery(this.wym._options.layoutCommandsSelector).find("ul");
-    this.errorBox = jQuery('#' + idPrefix + "errorbox");
-    this.previewButton = jQuery('#' + previewButtonId);
-    this.previewBox = jQuery('#' + previewBoxId);
-    this.hidePreviewButton = jQuery('#' + hidePreviewButtonId);
-    this.showStylesButton = jQuery('#' + showStylesButtonId);
-    this.cleanHtmlButton = jQuery('#' + cleanHtmlButtonId);
-
-    this.setupCssStorage();
-
-     // Initial set up
-
-    // retrieveCommands() must come before retrieveStyles(), due to use
-    // of calculateSelectors() which needs .commands to be set.
-    this.retrieveCommands(); // async=False
-    this.retrieveStyles(); // async=False
-    this.separatePresentation();
-
-    this.previewButton.click(function(event) {
-                                 self.showPreview();
-                                 return false;
-                              });
-    this.hidePreviewButton.click(function(event) {
-                                     self.previewBox.hide();
-                                     return false;
-                                 });
-    // start with styles hidden
-    this.showStyles(false);
-    this.showStylesButton.toggle(function(event) {
-                                     self.showStyles(true);
-                                     return false;
-                                 },
-                                 function(event) {
-                                     self.showStyles(false);
-                                     return false;
-                                 });
-    this.cleanHtmlButton.click(function(event) {
-                                   self.cleanHtml();
-                                   return false;
-                               });
-    jQuery(this.wym._doc)
-        .bind("keyup", function(evt) {
-                  // this style of binding gives docKeyup
-                  // access to 'this' PresentationControl
-                  self.docKeyup(evt);
-              })
-        .bind("keydown", function(evt) {
-                  self.docKeydown(evt);
-              })
-        .bind("mouseup", function(evt) {
-                  // In case the user clicked somewhere else
-                  // in the document, we have to update class list
-                  self.updateAppliedButtons();
-              });
-    jQuery(this.wym._options.containersSelector).find("a")
-        .bind("mousedown", function(evt) {
-                  // Need to save element id before the 'click' event
-                  // handler for the button runs and changes the id.
-                  self.saveCurrentElemId();
-              })
-        .bind("click", function(evt) {
-                  // Need to restore the element id before anything else
-                  // runs and calls ensureId
-                  self.restoreCurrentElemId();
-                  self.updateAppliedButtons();
-              });
-
-    // Insert rewriting of HTML before the WYMeditor updates the textarea.
-    jQuery(this.wym._options.updateSelector)
-        .bind(this.wym._options.updateEvent, function(event) {
-                  self.formSubmit(event);
-              });
-};
-
-// Setup document - splits the HTML into 'content HTML' and 'presentation'
-PresentationControls.prototype.separatePresentation = function() {
-    var self = this;
-    jQuery.post(this.opts.separatePresentationUrl, { html: self.wym.xhtml() } ,
-                function(data) {
-                    self.withGoodData(data,
-                        function(value) {
-                            // Store the presentation
-                            self.presentationInfo = value.presentation;
-                            // Update the HTML
-                            self.setHtml(value.html);
-                            // Update presentation of HTML
-                            self.updateAfterLoading();
-                        });
-                }, "json");
-};
-
-PresentationControls.prototype.updateAfterLoading = function() {
-    this.insertCommandBlocks();
-    this.updateAllStyleDisplay();
-};
-
-PresentationControls.prototype.retrieveCommands = function() {
-    var self = this;
-    // Needs async=false, since separatePresentation depends on data.
-    var res = jQuery.ajax({
-                  type: "GET",
-                  data: {},
-                  url: this.opts.retrieveCommandsUrl,
-                  dataType: "json",
-                  async: false
-    }).responseText;
-    var data = JSON.parse(res);
-
-    self.withGoodData(data, function(value) {
-                            self.commands = data.value;
-                            for (var i = 0; i < self.commands.length; i++) {
-                                var c = self.commands[i];
-                                self.commandDict[c.name] = c;
-                            }
-                            self.calculateSelectors(self.commands);
-                            self.allCommandSelectors = jQuery.map(self.commands,
-                                                                    function(c, i) { return self.tagnameToSelector(c.name); }
-                                                                    ).join(",");
-                            self.buildCommandList();
-                            jQuery.each(self.commands,
-                                function(i, c) {
-                                    self.addCssRule(self.tagnameToSelector(c.name) + ":after",
-                                                    'content: "' + c.verbose_name + '"');
-                                });
-                        });
-};
-
-PresentationControls.prototype.retrieveStyles = function() {
-    var self = this;
-    // Needs async=false, since separatePresentation depends on data.
-    var res = jQuery.ajax({
-                  type: "GET",
-                  data: {
-                      'template':this.opts.template,
-                      'page_id':this.opts.pageId
-                  },
-                  url: this.opts.retrieveStylesUrl,
-                  dataType: "json",
-                  async: false
-    }).responseText;
-    var data = JSON.parse(res);
-
-    self.withGoodData(data, function(value) {
-        self.availableStyles = data.value;
-        self.calculateSelectors(self.availableStyles);
-        self.buildClassList();
-    });
-};
-
-PresentationControls.prototype.calculateSelectors = function(stylelist) {
-    // Given a list of styles, add a 'allowed_elements_selector' attribute
-    // on the basis of the 'allowed_elements' attribute.
-
-    // This is just an optimisation to avoid doing this work multiple times.
-
-    var self = this;
-    for (var i = 0; i < stylelist.length; i++) {
-        var style = stylelist[i];
-        style.allowed_elements_selector =
-            jQuery.map(style.allowed_elements,
-                       function(t,j) { return self.tagnameToSelector(t); }
-                      ).join(",");
-    }
-};
-
-PresentationControls.prototype.tagnameToSelector = function(name) {
-    // Convert a tag name into a selector used to match elements that represent
-    // that tag.
-    //
-    // A 'tag' is either an HTML element name or a command name.
-    //
-    // This function accounts for the use of p elements to represent
-    // commands in the document.
-    var c = this.commandDict[name];
-    if (c == null) {
-        // not a command
-        if (name == "p") {
-            // Need to ensure we don't match commands
-            var selector = "p";
-            for (var i = 0; i < this.commands.length; i++) {
-                selector = selector + "[class!='secommand-" + this.commands[i].name + "']";
-            }
-            return selector;
-        } else {
-            return name;
-        }
-    } else {
-        return "p.secommand-" + name;
-    }
-};
-
-PresentationControls.prototype.setupCssStorage = function() {
-    // We need a place to store custom CSS rules. (Existing jQuery plugins don't
-    // allow for our need of changing style sheet of document in an iframe, and
-    // rolling our own is easier than fixing them).
-
-    // code partly copied from jquery.cssRule.js
-    var cssStorageNode = jQuery('<style rel="stylesheet" type="text/css">').appendTo(jQuery(this.wym._doc).find("head"))[0];
-    this.stylesheet = this.wym._doc.styleSheets[0];
-};
-
-// ---- Saving data ----
-
-
-PresentationControls.prototype.prepareData = function() {
-    // Prepare data/html for sending server side.
-
-    // We need to ensure all elements have ids, *and* that command block
-    // elements have correct ids (which is a side effect of the below).
-    this.ensureAllIds();
-    // this.presentationInfo might have old info, if command blocks have
-    // been removed.  Need to clean.
-    this.cleanPresentationInfo();
-};
-
-PresentationControls.prototype.cleanHtml = function() {
-    this.prepareData();
-    var self = this;
-    var html = this.wym.xhtml();
-    jQuery.post(this.opts.cleanHtmlUrl, {'html':html},
-                function(data) {
-                    self.withGoodData(data, function(value) {
-                                            self.setHtml(value.html);
-                                            self.updateAfterLoading();
-                                        });
-                }, "json");
-};
-
-// ---- Utility functions ----
-
-PresentationControls.prototype.escapeHtml = function(html) {
-    return html.replace(/&/g,"&amp;")
-        .replace(/\"/g,"&quot;")
-        .replace(/</g,"&lt;")
-        .replace(/>/g,"&gt;");
-};
-
-PresentationControls.prototype.flattenPresStyle = function(pres) {
-    // Convert a PresentationInfo object to a string
-    return pres.prestype + ":" + pres.name;
-};
-
-// If data contains an error message, display to the user,
-// otherwise clear the displayed error and perform the callback
-// with the value in the data.
-PresentationControls.prototype.withGoodData = function(data, callback) {
-    if (data.result == 'ok') {
-        this.clearError();
-        callback(data.value);
-    } else {
-        // TODO - perhaps distinguish between a server error
-        // and a user error
-        this.showError(data.message);
-    }
-};
-
-// ---- DOM utility ----
-
-PresentationControls.prototype.nextElementSibling = function(node) {
-    var next = node;
-    while (true) {
-        if (next == null) {
-            return null;
-        }
-        if (next.nextSibling == null) {
-            next = next.parentNode;
-        } else {
-            next = next.nextSibling;
-            if (next.nodeName != "#text") {
-                return next;
-            }
-        }
-    }
-};
-
-PresentationControls.prototype.previousElementSibling = function(node) {
-    var prev = node;
-    while (true) {
-        if (prev == null) {
-            return null;
-        }
-        if (prev.previousSibling == null) {
-            prev = prev.parentNode;
-        } else {
-            prev = prev.previousSibling;
-            if (prev.nodeName != "#text") {
-                return prev;
-            }
-        }
-    }
-};
-
-// ---- CSS utility ----
-
-PresentationControls.prototype.addCssRule = function(selector, rule) {
-    var ss = this.stylesheet;
-    var rules = ss.rules ? 'rules' : 'cssRules';
-    // should work with IE and Firefox, don't know about Safari
-    if (ss.addRule)
-        ss.addRule(selector, rule||'x:y' );//IE won't allow empty rules
-    else if (ss.insertRule)
-        ss.insertRule(selector + '{'+ rule +'}', ss[rules].length);
-};
-
-// ---- Event handlers ----
-
-// Long event handlers below, most are defined inline.
-
-PresentationControls.prototype.docKeyup = function(evt) {
-    // Some browsers (Firefox at least) will insert a new paragraph with the
-    // same ID as the old one when the user presses 'Enter'. This needs fixing
-    // for our styling to work. It's possible that multiple 'p' elements can be inserted
-    // for one keyup event, so we handle that. This appears to happen for any
-    // elements that are created by pressing 'Enter'
-
-    var node = this.getCurrentContainerNode();
-    var container;
-    if (node == null) {
-        return;
-    } else {
-        container = jQuery(node);
-    }
-    if (evt.keyCode == 13 && !evt.shiftKey) {
-        if (container.is("p[id],li[id]")) {
-            // Need to clear id on all elem's with that id except the first.
-            // (hoping that jQuery will return them in document order, which it
-            // seems to)
-            jQuery(this.wym._doc).find(container.get(0).tagName + "#" +  container.attr("id")).
-                each(function(i){
-                         var node = this;
-                         if (i > 0) { // skip the first
-                              jQuery(node).removeAttr('id');
-                         }
-                     });
-        }
-    }
-    // We also need to update the disable/enabled state of the classList and commandList
-    this.updateAppliedButtons(container);
-};
-
-PresentationControls.prototype.docKeydown = function(evt) {
-    // Need to intercept backspace and delete, to stop command blocks
-    // being deleted. (The system copes fairly well with them being
-    // deleted, but it can be very confusing for the user.)
-    var isBackspace = (evt.keyCode == 8);
-    var isDelete = (evt.keyCode == 46);
-    var target = null;
-
-    if (isBackspace || isDelete) {
-
-        var s = this.wym._iframe.contentWindow.getSelection();
-        if (s.anchorNode != s.focusNode) {
-            // an extended selection.  It doesn't matter if this
-            // contains command blocks - it doesn't confuse the user
-            // if these are deleted wholesale.
-            return;
-        }
-        if (s.focusNode == null) {
-            return;
-        }
-
-        var node = s.focusNode;
-        if (node.nodeName == "#text") {
-            // always true?
-            node = node.parentNode;
-        }
-        if (isBackspace) {
-            if (s.focusOffset != 0) {
-                return; // not at first character within text node
-            }
-            // need to check *previous* node
-            target = this.previousElementSibling(node);
-        }
-        if (isDelete) {
-            if (s.focusOffset != s.focusNode.textContent.length) {
-                return; // not at last character within text node
-            }
-            // need to check *next* node
-            target = this.nextElementSibling(node);
-        }
-        if (target == null) {
-            return;
-        }
-
-        if (jQuery(target).is(this.allCommandSelectors)) {
-            // stop backspace or delete from working.
-            evt.preventDefault();
-        }
-    }
-};
-
-PresentationControls.prototype.formSubmit = function(event) {
-    this.prepareData();
-    // Since we are in the middle of submitting the page, an asynchronous
-    // request will be too late! So we block instead.
-
-    // TODO - exception handling.  If this fails, the default
-    // handler will post the form, causing all formatting to be lost.
-    var res = jQuery.ajax({
-                  type: "POST",
-                  data: {
-                      html: this.wym.xhtml(),
-                      presentation: JSON.stringify(this.presentationInfo)
-                  },
-                  url: this.opts.combinePresentationUrl,
-                  dataType: "json",
-                  async: false
-    }).responseText;
-    var data = JSON.parse(res);
-    if (data.result == 'ok') {
-        // Replace existing HTML with combined.
-        this.setHtml(data.value.html);
-        // In case the normal WYMeditor update got called *before* this
-        // event handler, we do another update.
-        this.wym.update();
-    } else {
-        event.preventDefault();
-        this.showError(data.message);
-        alert("Data in " + this.name + " can't be saved - see error message.");
-    }
-};
-
-PresentationControls.prototype.saveCurrentElemId = function() {
-
-    // When a container tag is clicked, the tag of the current node is changed,
-    // and the 'id' attribute is removed. This causes the styling and commands
-    // to be lost. So we save the id and restore after the change.  This can
-    // result in strange ids e.g. an h2 element with the id='h1_1'.  However,
-    // this doesn't matter at all - it only matters that ids are unique
-    var nodeId = null;
-    var node = this.getCurrentContainerNode();
-    if (node != null) {
-        nodeId = node.id;
-    }
-    this._savedNodeId = nodeId;
-};
-
-PresentationControls.prototype.restoreCurrentElemId = function() {
-
-    if (this._savedNodeId == null) {
-        return;
-    }
-    var node = this.getCurrentContainerNode();
-    if (node != null &&
-        (node.id == null || node.id == "")) {
-        node.id = this._savedNodeId;
-    }
-    this._savedNodeId = null;
-};
-
-// ---- Manipulation of edited document ----
-
-PresentationControls.prototype.setHtml = function(html) {
-    // WUMEditor ought to call .listen() after setting
-    // the HTML (to rebind events for images),
-    // but it doesn't at the moment, so we work around it by wrapping.
-    this.wym.html(html);
-    this.wym.listen();
-};
-
-PresentationControls.prototype.ensureAllIds = function() {
-    // We check all block level elements (or all elements that can have commands
-    // applied to them)
-
-    var self = this;
-    var elems = [];
-    for (var i = 0; i < this.commands.length; i++) {
-        elems = jQuery.merge(elems, this.commands[i].allowed_elements);
-    }
-    elems = jQuery.unique(elems);
-    jQuery(this.wym._doc).find(elems.join(",")).each(function(i) {
-                                                         self.ensureId(this);
-                                                     });
-};
-
-PresentationControls.prototype.ensureId = function(node) {
-    var id = node.id;
-
-    if (id == undefined || id == "") {
-        id = this.nextId(node.tagName.toLowerCase());
-        node.id = id;
-        this.registerSection(id);
-    }
-    return id;
-};
-
-PresentationControls.prototype.nextId = function(tagName) {
-    // All sections that can receive styles need a section ID.
-    // For the initial HTML, this is assigned server-side when the
-    // HTML is split into 'semantic HTML' and 'presentation info'.
-    // For newly added HTML, however, we need to add it ourself
-
-    var i = 1;
-    var id = "";
-    while (true) {
-        id = tagName + "_" + i.toString();
-        if (jQuery(this.wym._doc).find('#' + id).is(tagName)) {
-            i++;
-        } else {
-            return id;
-        }
-    }
-};
-
-PresentationControls.prototype.commandBlockId = function(sectId, command) {
-    // Returns the ID to use for a command block that appears before
-    // section sectId for a given command.  This is also the key used
-    // in this.presentationInfo
-    return command.name + "_" + sectId;
-};
-
-PresentationControls.prototype.insertCommandBlock = function(sectId, command) {
-    // the spaces help make the command a decent target in the editor for Firefox (and we also use z-index: 1 in the CSS to bring them forward) - otherwise we can't hit them reliably with the mouse. Safari has not had a problem with this for some reason.
-    var newelem = jQuery("<p class=\"secommand secommand-" + command.name + "\">&nbsp;</p>");
-    var elem = jQuery(this.wym._doc).find("#" + sectId);
-
-    // Commands have an order they should appear in, which is defined by layout_order.
-    // We can use this to put the block in the right place.
-    var loopAgain = true;
-    var self = this;
-    while(loopAgain) {
-        loopAgain = false;
-        jQuery.each(this.commands,
-                   function(i, c) {
-                       var prev = elem.prev();
-                       if (prev.is(self.tagnameToSelector(c.name)) &&
-                                   c.layout_order > command.layout_order) {
-                           elem = prev;
-                           loopAgain = true;
-                       }
-                   });
-    }
-
-    elem.before(newelem);
-    var newId = this.commandBlockId(sectId, command);
-    newelem.attr('id', newId);
-    return newId;
-};
-
-PresentationControls.prototype.insertCommandBlocks = function() {
-    // This is run once, after loading HTML.  We can rely on 'ids' being
-    // present, since the HTML is all sent by the server.
-    for (var key in this.presentationInfo) {
-        var presinfos = this.presentationInfo[key];
-        for (var i = 0; i < presinfos.length; i++) {
-            var pi = presinfos[i];
-            if (pi.prestype == 'command') {
-                // key = e.g. 'newrow_p_1', we need 'p_1'
-                var id = key.split('_').slice(1).join('_');
-                this.insertCommandBlock(id, this.commandDict[pi.name]);
-            }
-        }
-    }
-};
-
-PresentationControls.prototype.isCommandBlock = function(node) {
-    // command blocks are like <p id="newrow_p_1" class="secommand secommand-[commandname]">
-    return (node != undefined && node.tagName != undefined &&
-            node.tagName.toLowerCase() == 'p' && node.className != undefined &&
-            node.className.match(/\bsecommand\b/));
-};
-
-// ---- Display of style information on document.
-
-PresentationControls.prototype.updateStyleDisplay = function(sectId) {
-    var styles = this.presentationInfo[sectId];
-    var self = this;
-    var stylelist = jQuery.map(styles, function(s, i) {
-                                   return self.getVerboseStyleName(s.name);
-                               }).join(", ");
-    // put a non-breaking space into the style list, so it always exists                           
-    this.addCssRule("#" + sectId + ":before", 'content: "' + stylelist + '\\00a0' + '"');
-};
-
-PresentationControls.prototype.updateAllStyleDisplay = function() {
-    for (var key in this.presentationInfo) {
-        this.updateStyleDisplay(key);
-    }
-};
-
-PresentationControls.prototype.getVerboseStyleName = function(stylename) {
-    // Full style information is not stored against individual headings, only
-    // the type and name. So sometimes we need to go from one to the other.
-
-    var styles = this.availableStyles;
-    for (var i = 0; i < styles.length; i++) {
-        if (styles[i].name == stylename) {
-            return styles[i].verbose_name;
-        }
-    }
-    var commands = this.commands;
-    for (var i2 = 0; i2 < commands; i2++) {
-        if (commands[i2].name == stylename) {
-            return commands[i2].verbose_name;
-        }
-    }
-    return undefined; // shouldn't get here
-};
-
-// ---- Manipulation of class and command list ----
-
-PresentationControls.prototype.buildClassList = function() {
-    var self = this;
-    this.classList.empty();
-    // For each new category encountered, we have an li that contains
-    // a div with a heading, and a ul with all the actual classes.
-    // NB the category could be 'null' so care must be taken. Also,
-    // the list is already sorted by category and verbose_name
-    var state = {start:true,
-                 category:null,
-                 categoryList:null
-                };
-    jQuery.each(self.availableStyles, function(i, item) {
-        if (state.start || (state.category != item.category)) {
-            var category;
-            if (item.category == null) {
-                category = "Uncategorized";
-            } else {
-                category = item.category;
-            }
-            var classListItem = jQuery("<li class=\"class-category\"><h3 class=\"categoryHeading\">" + self.escapeHtml(category) + ':</h3></li>');
-            classListItem.appendTo(self.classList);
-            state.categoryList = jQuery("<ul></ul>");
-            state.categoryList.appendTo(classListItem);
-        }
-        var btn = jQuery("<li><a href='#'>" + self.escapeHtml(item.verbose_name) + "</a></li>").appendTo(state.categoryList).find("a");
-        // event handlers
-        var style = self.availableStyles[i];
-
-        btn.click(function(event) {
-                      self.toggleStyle(style, btn);
-                      event.preventDefault();
-                  });
-        state.start = false;
-        state.category = item.category;
-    });
-};
-
-PresentationControls.prototype.buildCommandList = function () {
-    var self = this;
-    self.commandList.empty();
-    jQuery.each(self.commands, function(i, item) {
-        var btn = jQuery("<li><a href='#'>" + self.escapeHtml(item.verbose_name) + "</a></li>").appendTo(self.commandList).find("a");
-        // event handlers
-        var command = self.commands[i];
-
-        btn.click(function(event) {
-                      self.doCommand(command, btn);
-                      event.preventDefault();
-                  });
-    });
-};
-
-PresentationControls.prototype.updateClassListItem = function(btn, style) {
-    var sectId = this.getCurrentSection(style);
-    if (sectId == undefined) {
-        // Can't use it.
-        if (style.prestype == "command") {
-            // Don't want the class list (displayed below command list)
-            // to jump up and down
-            btn.addClass("disabled");
-            btn.removeClass("used");
-        } else {
-            btn.hide();
-        }
-    } else {
-        if (style.prestype == "command") {
-            btn.removeClass("disabled");
-            if (this.hasCommand(sectId, style)) {
-                btn.addClass("used");
-            } else {
-                btn.removeClass("used");
-            }
-        } else {
-            btn.show();
-            if (this.hasStyle(sectId, style)) {
-                btn.addClass("used");
-            } else {
-                btn.removeClass("used");
-            }
-        }
-    }
-};
-
-PresentationControls.prototype.updateAppliedButtons = function(curContainer) {
-    var self = this;
-    var node;
-    if (curContainer == undefined) {
-        node = this.getCurrentContainerNode();
-    } else {
-        node = curContainer.get(0);
-    }
-    if (node != undefined && node != this.currentNode) {
-        // if current node has changed, might need to update list
-        this.currentNode = node;
-        // Sets enabled/disabled on all items in classList and commands
-        var pairs = [[this.availableStyles, this.classList],
-                     [this.commands, this.commandList]];
-
-        for (var i = 0; i < pairs.length; i++) {
-            var styles = pairs[i][0];
-            var btncontainer = pairs[i][1];
-            btncontainer.find("a").each(function(k) {
-                                            self.updateClassListItem(jQuery(this), styles[k]);
-                                        });
-        }
-
-        // We also update the container list to set the class="used" for
-        // consistent styling between that list and the command/class lists.
-        jQuery(this.wym._options.containersSelector).find("a").each(function(k) {
-            var name = this.name; // stores 'P', 'H1' etc
-            if (name.toLowerCase() == node.tagName.toLowerCase() &&
-                !self.isCommandBlock(node)) {
-                jQuery(this).addClass("used");
-            } else {
-                jQuery(this).removeClass("used");
-            }
-        });
-
-    }
-};
-
-// ---- Manipulation/query of stored presentation info ----
-
-PresentationControls.prototype.registerSection = function(sectId) {
-    // Make sure we have somewhere to store styles for a section.
-    this.presentationInfo[sectId] = new Array();
-};
-
-PresentationControls.prototype.getCurrentContainerNode = function() {
-    // Returns the node of the closest container element to the selection
-    var container = jQuery(this.wym.selected()).parentsOrSelf(this.blockdefSelector);
-    if (container.is(this.blockdefSelector)) {
-        return container.get(0);
-    } else {
-        return null;
-    }
-};
-
-PresentationControls.prototype.getCurrentSection = function(style) {
-    // Returns the section ID of the current section that is applicable
-    // to the given style, or undefined if there is none.
-    // Since sections can be nested, if more than one section is possible
-    // for the given style, the first section found (starting from the
-    // current selection and moving up the HTML tree) will be returned.
-    // This function has the side effect of adding a section ID if one
-    // was not defined already.
-    var wym = this.wym;
-    var self = this;
-    var expr = style.allowed_elements_selector;
-    var container = jQuery(wym.selected()).parentsOrSelf(expr);
-    if (container.is(expr)) {
-        var first = container.get(0);
-        var id = this.ensureId(first);
-        return id;
-    }
-    return undefined;
-};
-
-PresentationControls.prototype.hasStyle = function(sectId, style) {
-    var styles = this.presentationInfo[sectId];
-    if (styles == undefined)
-        return false;
-    for (var i = 0; i < styles.length; i++) {
-        if (this.flattenPresStyle(styles[i]) == this.flattenPresStyle(style)) {
-            return true;
-        };
-    }
-    return false;
-};
-
-PresentationControls.prototype.hasCommand = function(sectId, command) {
-    // Returns true if the section has the command before it.
-    return (this.presentationInfo[this.commandBlockId(sectId, command)] != undefined);
-};
-
-PresentationControls.prototype.addStyle = function(sectId, presinfo) {
-    var styles = this.presentationInfo[sectId];
-    styles.push(presinfo);
-    this.presentationInfo[sectId] = jQuery.unique(styles);
-};
-
-PresentationControls.prototype.removeStyle = function(sectId, presinfo) {
-    var styles = this.presentationInfo[sectId];
-    styles = jQuery.grep(styles, function(item, i) {
-                             return !(item.prestype == presinfo.prestype
-                                      && item.name == presinfo.name);
-                         });
-    this.presentationInfo[sectId] = styles;
-};
-
-PresentationControls.prototype.toggleStyle = function(style, btn) {
-    // What section are we on?
-    var sectId = this.getCurrentSection(style);
-    if (sectId == undefined) {
-        // No allowed to use it there
-        alert("Cannot use this style on current element.");
-        return;
-    }
-
-    if (this.hasStyle(sectId, style)) {
-        this.removeStyle(sectId, style);
-    } else {
-        this.addStyle(sectId, style);
-    }
-    this.updateStyleDisplay(sectId);
-    this.updateClassListItem(btn, style);
-};
-
-PresentationControls.prototype.doCommand = function(command, btn) {
-    // What section are we on?
-    var sectId = this.getCurrentSection(command);
-    if (sectId == undefined) {
-        // Not allowed to use it there
-        alert("Cannot use this command on current element.");
-        return;
-    }
-    // newrow and newcol are the only commands at the moment.
-    // We handle both using inserted blocks, and both commands
-    // act as toggles (remove if already present).
-    if (this.hasCommand(sectId, command)) {
-        var id = this.commandBlockId(sectId, command);
-        jQuery(this.wym._doc).find('#' + id).remove();
-        delete this.presentationInfo[id];
-    } else {
-        var newId = this.insertCommandBlock(sectId, command);
-        this.registerSection(newId);
-        this.presentationInfo[newId].push(command);
-        this.updateStyleDisplay(newId);
-    }
-    this.updateClassListItem(btn, command);
-};
-
-PresentationControls.prototype.cleanPresentationInfo = function() {
-    // clear out orphaned items
-    var orphaned = [];
-    for (var key in this.presentationInfo) {
-        var sel = "#" + key;
-        if (!jQuery(this.wym._doc).find(sel).is(sel)) {
-            orphaned.push(key);
-        }
-    }
-    for (var i = 0; i < orphaned.length; i++) {
-        delete this.presentationInfo[orphaned[i]];
-    }
-};
-
-// ---- Error box -----
-
-PresentationControls.prototype.clearError = function() {
-    this.errorBox.empty();
-};
-
-PresentationControls.prototype.showError = function(message) {
-    this.clearError();
-    this.errorBox.append(this.escapeHtml(message));
-};
-// ---- Preview ---
-
-PresentationControls.prototype.showPreview = function() {
-    this.prepareData();
-    var self = this;
-    jQuery.post(this.opts.previewUrl, {'html': self.wym.xhtml(),
-                                       'presentation': JSON.stringify(this.presentationInfo)
-                                      },
-                function(data) {
-                    self.withGoodData(data,
-                        function(value) {
-                            var btn = self.previewButton;
-                            var box = self.previewBox;
-                            var pos = btn.offset();
-                            box.find(".content").html(value.html);
-                            var height = box.outerHeight();
-                            box.css("top", pos.top - height - 20).css("left", pos.left);
-                            box.show();
-                        });
-                }, "json");
-    return false;
-};
-
-PresentationControls.prototype.showStyles = function(show) {
-    if (show) {
-        jQuery(this.wym._doc).find('#presmodestyles').remove();
-        jQuery(this.wym._iframe.ownerDocument).find('#presmodestyles').remove();
-        this.showStylesButton.attr('value', 'Hide styles');
-        // Fix height of list of classes.
-        var h = jQuery(" .wym_area_main").outerHeight() -
-            jQuery(this.wym._options.containersSelector).outerHeight() -
-            jQuery(this.wym._options.layoutCommandsSelector).outerHeight() -
-            20; // fudge factor, padding related?
-        jQuery(this.wym._options.classesSelector).height(h);
-
-
-    } else {
-        jQuery('<style rel="stylesheet" type="text/css" id="presmodestyles">' +
-               'p.secommand { display:none; } ' +
-               '*:before    { display: none!important; } ' +
-               '*:after     { top: 0!important; } ' +
-               '</style>').appendTo(jQuery(this.wym._doc).find("head"));
-        jQuery('<style rel="stylesheet" type="text/css" id="presmodestyles">' +
-               '.wym_layout_commands, .wym_classes  {display: none!important;} ' +
-               '</style>').appendTo(jQuery(this.wym._iframe.ownerDocument).find("head"));
-        this.showStylesButton.attr('value', 'Show styles');
-    }
-};
-
-// ---- WYMeditor plugin definition ----
-
-// The actual WYMeditor plugin:
-WYMeditor.editor.prototype.semantic = function(options) {
-    var wym = this;
-    var c = new PresentationControls(wym, options);
-};
Add a comment to this file

semanticeditor/media/semanticeditor/javascript/wymeditor/skins/semanticeditor/icons.png

Removed
Old image

semanticeditor/media/semanticeditor/javascript/wymeditor/skins/semanticeditor/preview.css

-/* PREVIEW */
-.previewbox {
-    background: #fff;