[snowy] Import the first cut of funcooker



commit 297c08377b7e4b691bf248e678f0b5c4e51d2851
Author: Brad Taylor <brad getcoded net>
Date:   Sun May 3 18:38:21 2009 -0400

    Import the first cut of funcooker
---
 tomboy-note.xsl => data/note2xhtml.xsl |   46 ++----
 notes/templates/notes/note_detail.html |   18 ++-
 notes/urls.py                          |    2 +-
 notes/views.py                         |   32 ++++
 site_media/js/DUI.js                   |  303 ++++++++++++++++++++++++++++++++
 site_media/js/funcooker.js             |   78 ++++++++
 templates/base.html                    |    5 +
 7 files changed, 447 insertions(+), 37 deletions(-)

diff --git a/tomboy-note.xsl b/data/note2xhtml.xsl
similarity index 79%
rename from tomboy-note.xsl
rename to data/note2xhtml.xsl
index 0b0b55d..d690afc 100644
--- a/tomboy-note.xsl
+++ b/data/note2xhtml.xsl
@@ -16,32 +16,7 @@
 <xsl:param name="newline" select="'&#xA;'" />
 
 <xsl:template match="/">
-	<html>
-	<head>
-	<title><xsl:value-of select="/tomboy:note/tomboy:title" /></title>
-	<style type="text/css">
-	body { <xsl:value-of select="$font" /> }
-	h1 { font-size: xx-large;
-     	     font-weight: bold;
-     	     border-bottom: 1px solid black; }
-	div.note { overflow: auto;
-		   position: relative;
-		   display: block;
-		   padding: 5pt;
-		   margin: 5pt; 
-		   white-space: -moz-pre-wrap; /* Mozilla */
- 	      	   white-space: -pre-wrap;     /* Opera 4 - 6 */
- 	      	   white-space: -o-pre-wrap;   /* Opera 7 */
- 	      	   white-space: pre-wrap;      /* CSS3 */
- 	      	   word-wrap: break-word;      /* IE 5.5+ */ }
-	</style>
-	</head>
-	<body>
-
 	<xsl:apply-templates select="tomboy:note"/>
-
-	</body>
-	</html>
 </xsl:template>
 
 <xsl:template match="text()">
@@ -102,27 +77,28 @@
 </xsl:template>
 
 <xsl:template match="tomboy:highlight">
-	<span style="background:yellow"><xsl:apply-templates select="node()"/></span>
+	<span class="note-highlight"><xsl:apply-templates select="node()"/></span>
 </xsl:template>
 
 <xsl:template match="tomboy:datetime">
-	<span style="font-style:italic;font-size:small;color:#888A85">
+	<span class="note-datetime">
 		<xsl:apply-templates select="node()"/>
 	</span>
 </xsl:template>
 
 <xsl:template match="size:small">
-	<span style="font-size:small"><xsl:apply-templates select="node()"/></span>
+	<span class="note-size-small"><xsl:apply-templates select="node()"/></span>
 </xsl:template>
 
 <xsl:template match="size:large">
-	<span style="font-size:large"><xsl:apply-templates select="node()"/></span>
+	<span class="note-size-large"><xsl:apply-templates select="node()"/></span>
 </xsl:template>
 
 <xsl:template match="size:huge">
-	<span style="font-size:xx-large"><xsl:apply-templates select="node()"/></span>
+	<span class="note-size-huge"><xsl:apply-templates select="node()"/></span>
 </xsl:template>
 
+<!-- TODO:
 <xsl:template match="link:broken">
 	<span style="color:#555753;text-decoration:underline">
 		<xsl:value-of select="node()"/>
@@ -156,27 +132,31 @@
 		<xsl:apply-templates select="node()" />
 	</li>
 </xsl:template>
+-->
 
 <!-- Evolution.dll Plugin -->
+<!--
 <xsl:template match="link:evo-mail">
 	<a href="{./@uri}">
 		<img alt="Open Email Link" width="16" height="10" border="0">
-			<!-- Inline Base64 encoded stock_mail.png =) -->
+			Inline Base64 encoded stock_mail.png =)
 			<xsl:attribute name="src">data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAKCAYAAAC9vt6cAAAABmJLR0QA/wD/AP+gvaeTAAAACXBI WXMAAAsQAAALEAGtI711AAAAB3RJTUUH1QkeAjYaRAvZgAAAALxJREFUKM+NkjGKw1AMRN+GhRS/ 2xP4EHZr0E1UxFVuoiKdikCKfxMfwKdw+3t1gb/F4hASe50BgZjRDEII/jAAtWmaCnxSAy+oZlYj YrfMbAkB4GsJiAjcnfPpRNzvrCHnjIjQdd3De3geUFX8diMdj6tmVX3jD6+EquLXKz9p37waANC2 LRfPpJTIOdP3PXuoEVFLKdXMaills5+m6f8jbq26dcTvRXR3RIR5njcDRIRxHFe14cMHenukX9eX mbvfl0q9AAAAAElFTkSuQmCC</xsl:attribute>
 		</img>
 		<xsl:value-of select="node()"/>
 	</a>
 </xsl:template>
+-->
 
 <!-- FixedWidth.dll Plugin -->
 <xsl:template match="tomboy:monospace">
-	<span style="font-family:monospace"><xsl:apply-templates select="node()"/></span>
+	<span class="note-monospace"><xsl:apply-templates select="node()"/></span>
 </xsl:template>
 
 <!-- Bugzilla.dll Plugin -->
+<!--
 <xsl:template match="link:bugzilla">
 	<a href="{ uri}"><xsl:value-of select="node()" /></a>
 </xsl:template>
+-->
 
 </xsl:stylesheet>
-
diff --git a/notes/templates/notes/note_detail.html b/notes/templates/notes/note_detail.html
index f79296b..f95aba7 100644
--- a/notes/templates/notes/note_detail.html
+++ b/notes/templates/notes/note_detail.html
@@ -1,12 +1,24 @@
 {% extends 'notes/base.html' %}
 
-{% block title %}{{ object.title }} | Notes | {{ block.super }}{% endblock %}
+{% block extra_head %}
+    <script type="text/javascript" src="{{ MEDIA_URL }}js/DUI.js" charset="utf-8"></script>
+
+    <script type="text/javascript" src="{{ MEDIA_URL }}js/funcooker.js" charset="utf-8"></script>
+{% endblock %}
+
+{% block title %}{{ note.title }} | Notes | {{ block.super }}{% endblock %}
 
 {% block sidebar %}
 {{ block.super }}
 {% endblock %}
 
 {% block content %}
-<h2>{{ object.title }}</h2>
-<p>{{ object.body }}</p>
+<div id="funcooker">
+{{ body|safe|linebreaksbr }}
+</div>
+<script type="text/javascript">
+$(document).ready(function() {
+    new FunCooker("#funcooker");
+});
+</script>
 {% endblock %}
diff --git a/notes/urls.py b/notes/urls.py
index 03be0ab..f3a2072 100644
--- a/notes/urls.py
+++ b/notes/urls.py
@@ -29,5 +29,5 @@ notes_dict = {'queryset': Note.objects.all(), }
 
 urlpatterns = patterns('',
     (r'^$', object_list, notes_dict),
-    url(r'^(?P<object_id>\d+)/$', object_detail, notes_dict, name='note_detail'),
+    url(r'^(?P<note_id>\d+)/$', 'snowy.notes.views.note_detail', name='note_detail'),
 )
diff --git a/notes/views.py b/notes/views.py
index 0eb258e..9db7835 100644
--- a/notes/views.py
+++ b/notes/views.py
@@ -20,3 +20,35 @@
 # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 
 # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 
 #
+
+from django.template import RequestContext
+from django.shortcuts import render_to_response, get_object_or_404
+
+from snowy.notes.models import *
+
+def note_detail(request, note_id,
+                template_name='notes/note_detail.html'):
+    note = get_object_or_404(Note, pk=note_id)
+    
+    # break this out into a function
+    import libxslt
+    import libxml2
+    
+    style, doc, result = None, None, None
+ 
+    try:
+        styledoc = libxml2.parseFile('data/note2xhtml.xsl')
+        style = libxslt.parseStylesheetDoc(styledoc)
+    
+        doc = libxml2.parseDoc(note.body)
+        result = style.applyStylesheet(doc, None)
+    
+        body = style.saveResultToString(result)
+    finally:
+        if style != None: style.freeStylesheet()
+        if doc != None: doc.freeDoc()
+        if result != None: result.freeDoc()
+
+    return render_to_response(template_name,
+                              {'note': note, 'body': body },
+                              context_instance=RequestContext(request))
diff --git a/site_media/js/DUI.js b/site_media/js/DUI.js
new file mode 100644
index 0000000..759e07f
--- /dev/null
+++ b/site_media/js/DUI.js
@@ -0,0 +1,303 @@
+/**
+ * DUI: The Digg User Interface Library
+ *
+ * Copyright (c) 2008-2009, Digg, Inc.
+ * All rights reserved.
+ * 
+ * Redistribution and use in source and binary forms, with or without 
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright notice, 
+ *   this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright notice, 
+ *   this list of conditions and the following disclaimer in the documentation 
+ *   and/or other materials provided with the distribution.
+ * - Neither the name of the Digg, Inc. nor the names of its contributors 
+ *   may be used to endorse or promote products derived from this software 
+ *   without specific prior written permission.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ * @module DUI
+ * @author Micah Snyder <micah digg com>
+ * @description The Digg User Interface Library
+ * @version 0.0.4
+ * @link http://code.google.com/p/digg
+ *
+ */
+
+/* Add Array.prototype.indexOf -- Guess which major browser doesn't support it natively yet? */
+[].indexOf || (Array.prototype.indexOf = function(v, n){
+    n = (n == null) ? 0 : n; var m = this.length;
+    
+    for(var i = n; i < m; i++) {
+        if(this[i] == v) return i;
+    }
+    
+    return -1;
+});
+
+(function($) {
+
+/* Create our top-level namespace */
+DUI = {
+    version: "0.0.4"
+};
+
+/**
+ * @class Class Class creation and management for use with jQuery. Class is a singleton that handles static and dynamic classes, as well as namespaces
+ */
+DUI.Class = {
+    /**
+     * @var {Array} _dontEnum Internal array of keys to omit when looking through a class' properties. Once the real DontEnum bit is writable we won't have to deal with this.
+     */
+    _dontEnum: ['_ident', '_dontEnum', 'create', 'namespace', 'ns', 'supers', 'sup', 'init', 'each'],
+    
+    /**
+     * @function create Make a class! Do work son, do work
+     * @param {optional Object} methods Any number of objects can be passed in as arguments to be added to the class upon creation
+     * @param {optional Boolean} static If the last argument is Boolean, it will be treated as the static flag. Defaults to false (dynamic)
+     */
+    create: function() {
+        //Set _this to DUI.Class
+        var _this = this;
+        
+        //Figure out if we're creating a static or dynamic class
+        var s = (arguments.length > 0 && //if we have arguments...
+                arguments[arguments.length - 1].constructor == Boolean) ? //...and the last one is Boolean...
+                    arguments[arguments.length - 1] : //...then it's the static flag...
+                    false; //...otherwise default to a dynamic class
+        
+        //Static: Object, dynamic: Function
+        var c = s ? {} : function() {
+            this.init.apply(this, arguments);
+        }
+        
+        //All of our classes have these in common
+        var methods = {
+            _ident: {
+                library: "DUI.Class",
+                version: "0.0.4",
+                dynamic: true
+            },
+            
+            //_dontEnum should exist in our classes as well
+            _dontEnum: this._dontEnum,
+            
+            //A basic namespace container to pass objects through
+            ns: [],
+            
+            //A container to hold one level of overwritten methods
+            supers: {},
+            
+            //A constructor
+            init: function() {},
+            
+            //Our namespace function
+            namespace:function(ns) {
+                //Don't add nothing
+                if (!ns) return null;
+                
+                //Set _this to the current class, not the DUI.Class lib itself
+                var _this = this;
+                
+                //Handle ['ns1', 'ns2'... 'nsN'] format
+                if(ns.constructor == Array) {
+                    //Call namespace normally for each array item...
+                    $.each(ns, function() {
+                        _this.namespace.apply(_this, [this]);
+                    });
+                    
+                    //...then get out of this call to namespace
+                    return;
+                
+                //Handle {'ns': contents} format
+                } else if(ns.constructor == Object) {
+                    //Loop through the object passed to namespace
+                    for(var key in ns) {
+                        //Only operate on Objects and Functions
+                        if([Object, Function].indexOf(ns[key].constructor) > -1) {
+                            //In case this.ns has been deleted
+                            if(!this.ns) this.ns = [];
+                            
+                            //Copy the namespace into an array holder
+                            this.ns[key] = ns[key];
+                            
+                            //Apply namespace, this will be caught by the ['ns1', 'ns2'... 'nsN'] format above
+                            this.namespace.apply(this, [key]);
+                        }
+                    }
+                    
+                    //We're done with namespace for now
+                    return;
+                }
+                
+                //Note: [{'ns': contents}, {'ns2': contents2}... {'nsN': contentsN}] is inherently handled by the above two cases
+                
+                var levels = ns.split(".");
+                
+                /* Dynamic classes are Functions, so we'll extend their prototype.
+                   Static classes are Objects, so we'll extend them directly */
+                var nsobj = this.prototype ? this.prototype : this;
+                
+                $.each(levels, function() {
+                    /* When adding a namespace check to see, in order:
+                     * 1) Does the ns exist in our ns passthrough object?
+                     * 2) Does the ns already exist in our class
+                     * 3) Does the ns exist as a global var?
+                     *    NOTE: Support for this was added so that you can namespace classes
+                     *    into other classes, i.e. MyContainer.namespace('MyUtilClass'). this
+                     *    behaviour is dangerously greedy though, so it may be removed.
+                     * 4) If none of the above, make a new static class
+                     */
+                    nsobj[this] = _this.ns[this] || nsobj[this] || window[this] || DUI.Class.create(true);
+                    
+                    /* If our parent and child are both dynamic classes, copy the child out of Parent.prototype and into Parent.
+                     * It seems weird at first, but this allows you to instantiate a dynamic sub-class without instantiating
+                     * its parent, e.g. var baz = new Foo.Bar();
+                     */
+                    if(_this.prototype && DUI.isClass(nsobj[this]) && nsobj[this].prototype) {
+                        _this[this] = nsobj[this];
+                    }
+                    
+                    //Remove our temp passthrough if it exists
+                    delete _this.ns[this];
+                    
+                    //Move one level deeper for the next iteration
+                     nsobj = nsobj[this];
+                });
+                
+                //TODO: Do we really need to return this? It's not that useful anymore
+                return nsobj;
+            },
+            
+            /* Create exists inside classes too. neat huh?
+             * Usage differs slightly: MyClass.create('MySubClass', { myMethod: function() }); */
+            create: function() {
+                //Turn arguments into a regular Array
+                var args = Array.prototype.slice.call(arguments);
+                
+                //Pull the name of the new class out
+                var name = args.shift();
+                
+                //Create a new class with the rest of the arguments
+                var temp = DUI.Class.create.apply(DUI.Class, args);
+                
+                //Load our new class into the {name: class} format to pass it into namespace()
+                var ns = {};
+                ns[name] = temp;
+                
+                //Put the new class into the current one
+                this.namespace(ns);
+            },
+            
+            //Iterate over a class' members, omitting built-ins
+            each: function(cb) {
+                if(!$.isFunction(cb)) {
+                    throw new Error('DUI.Class.each must be called with a function as its first argument.');
+                }
+                
+                //Set _this to the current class, not the DUI.Class lib itself
+                var _this = this;
+                
+                $.each(this, function(key) {
+                    if(_this._dontEnum.indexOf(key) != -1) return;
+                    
+                    cb.apply(this, [key, this]);
+                });
+            },
+            
+            //Call the super of a method
+            sup: function() {
+                try {
+                    var caller = this.sup.caller.name;
+                    this.supers[caller].apply(this, arguments);
+                } catch(noSuper) {
+                    return false;
+                }
+            }
+        }
+        
+        //Static classes don't need a constructor
+        s ? delete methods.init : null;
+        
+        //...nor should they be identified as dynamic classes
+        s ? methods._ident.dynamic = false : null;
+        
+        /* Put default methods into the class before anything else,
+         *   so that they'll be overwritten by the user-specified ones */
+        $.extend(c, methods);
+        
+        /* Second copy of methods for dynamic classes: They get our 
+         * common utils in their class definition AND their prototype */
+        if(!s) $.extend(c.prototype, methods);
+        
+        //Static: extend the Object, Dynamic: extend the prototype
+        var extendee = s ? c : c.prototype;
+        
+        //Loop through arguments. If they're the right type, tack them on
+        $.each(arguments, function() {
+            //Either we're passing in an object full of methods, or the prototype of an existing class
+            if(this.constructor == Object || typeof this.init != undefined) {
+                /* Here we're going per-property instead of doing $.extend(extendee, this) so that
+                 * we overwrite each property instead of the whole namespace. Also: we omit the 'namespace'
+                 * helper method that DUI.Class tacks on, as there's no point in storing it as a super */
+                for(i in this) {
+                    /* If a property is a function (other than our built-in helpers) and it already exists
+                     * in the class, save it as a super. note that this only saves the last occurrence */
+                    if($.isFunction(extendee[i]) && _this._dontEnum.indexOf(i) == -1) {
+                        //since Function.name is almost never set for us, do it manually
+                        this[i].name = extendee[i].name = i;
+                        
+                        //throw the existing function into this.supers before it's overwritten
+                        extendee.supers[i] = extendee[i];
+                    }
+                    
+                    //Special case! If 'dontEnum' is passed in as an array, add its contents to DUI.Class._dontEnum
+                    if(i == 'dontEnum' && this[i].constructor == Array) {
+                        extendee._dontEnum = $.merge(extendee._dontEnum, this[i]);
+                    }
+                    
+                    //extend the current property into our class
+                    extendee[i] = this[i];
+                }
+            }
+        });
+        
+        //Shiny new class, ready to go
+        return c;
+    }
+};
+
+})(jQuery);
+
+//Simple check so see if the object passed in is a DUI Class
+DUI.isClass = function(check, type)
+{
+    type = type || false;
+    
+    try {
+        if(check._ident.library == 'DUI.Class') {
+            if((type == 'dynamic' && !check._ident.dynamic)
+               || (type == 'static' && check._ident.dynamic)) {
+                return false;
+            }
+            
+            return true;
+        }
+    } catch(noIdentUhOh) {
+        return false;
+    }
+    
+    return false;
+}
diff --git a/site_media/js/funcooker.js b/site_media/js/funcooker.js
new file mode 100644
index 0000000..03906a9
--- /dev/null
+++ b/site_media/js/funcooker.js
@@ -0,0 +1,78 @@
+/*
+ * Copyright (c) 2009 Brad Taylor <brad getcoded net>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the 
+ * "Software"), to deal in the Software without restriction, including 
+ * without limitation the rights to use, copy, modify, merge, publish, 
+ * distribute, sublicense, and/or sell copies of the Software, and to 
+ * permit persons to whom the Software is furnished to do so, subject to 
+ * the following conditions: 
+ *  
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software. 
+ *  
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 
+ *
+ */
+
+/* Here comes the funcooker! */
+var FunCooker = DUI.Class.create({
+    target: null,
+
+    init: function(target) {
+        this.target = $(target);
+
+        if (this.editingEnabled()) {
+            this.target.attr('contenteditable', true);
+        }
+    },
+
+    editingEnabled: function() {
+        // TODO: Support other platforms
+        return $.browser.mozilla;
+    },
+
+    boldSelection: function() {
+    },
+
+    wrapSelection: function(wrapper) {
+        var range = this.getDocumentSelection();
+        if (range) {
+            // insure that the selection range is inside of the editor
+            var parent = this.findParentById(range.commonAncestorContainer,
+                                             target.id);
+            if (!parent) {
+                return;
+            }
+
+	    // TODO: make this work in the general case, e.g.: when sel spans
+	    // multiple elements (<li>, <p>, etc)
+            range.surroundContents(wrapper);
+        }
+    },
+
+    getDocumentSelection: function() {
+        if ($.browser.mozilla) {
+            return window.getSelection().getRangeAt(0);
+        }
+    },
+
+    findParentById: function(subject, parentId) {
+        if (subject == null || subject.tagName == "BODY") {
+            return false;
+        }
+        
+        if (subject.id == parentId) {
+            return subject;
+        }
+        
+        return this.findParentById(subject.parentNode, parentId); 
+    },
+});
diff --git a/templates/base.html b/templates/base.html
index ba8dc55..1692e9d 100644
--- a/templates/base.html
+++ b/templates/base.html
@@ -5,6 +5,11 @@
     <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
     <title>{% block title %}home{% endblock %}</title>
     <link rel="stylesheet" type="text/css" href="{{ MEDIA_URL }}css/screen.css" media="screen">
+{% if DEBUG %}
+    <script type="text/javascript" src="{{ MEDIA_URL }}js/jquery.js" charset="utf-8"></script>
+{% else %}
+    <script type="text/javascript" src="{{ MEDIA_URL }}js/jquery.min.js" charset="utf-8"></script>
+{% endif %}
 {% block extra_head %}
 {% endblock %}
 </head>



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]