Adam Gomaa avatar Adam Gomaa committed 509eaa1

Move notes view into it's own module, might package it up separately.

Comments (0)

Files changed (7)

propaneweb/note.py

 "Module for Note object, server-side equivalent of the Backbone Note model"
+import propaneweb.shortcuts as ps
 
 
 class Note(object):
     def model_data(self):
         "The data for this Note that should be supplied to the Backbone model"
         return {
+            "id": self.fname,
             "timestamp": self.timestamp(),
             "text": self.text(),
+            "html": self.html(),
             }
 
     def timestamp(self):
         return splitext(self.fname)[0]
 
     def text(self):
+        return self.fd().read()
+
+    def html(self):
         from django.utils.html import urlize
         # urlize to make http:// clickable
-        return urlize(self.fd().read())
+        return urlize(self.text())
 
-    def fd(self):
+    def fd(self, mode='r'):
         from os.path import join
-        return open(join(self.directory, self.fname))
+        return open(join(self.directory, self.fname), mode=mode)
+
+    def save_text(self, text):
+        self.fd('w').write(text)
+
+
+
+
+def notes(request, path):
+    "Render notes template"
+    from .note import Note
+
+    if not request.user.is_authenticated():
+        return ps.HttpResponseRedirect("/")
+
+    # I'm the only user anyway
+    if request.user.username != 'akg':
+        return ps.HttpResponseRedirect('/')
+
+    if request.method == "PUT":
+        if '/' in path:
+            first, rest = path.split("/", 1)
+        else:
+            first, rest = path, ''
+        try:
+            _view = getattr(Put, first)
+        except AttributeError:
+            raise ps.Http404
+        return _view(request, *rest.split("/"))
+
+    def js_source():
+        "Get static JS"
+        from .util import get_jinja2_env
+        env = get_jinja2_env()
+        source, fname, uptodate = env.loader.get_source(None, "notes.js")
+        return ps.HttpResponse(source, content_type="text/javascript")
+
+    def css_source():
+        "Get static CSS"
+        from .util import get_jinja2_env
+        env = get_jinja2_env()
+        source, fname, uptodate = env.loader.get_source(None, "notes.css")
+        return ps.HttpResponse(source, content_type="text/css")
+
+    def notes_data():
+        "Get JSON data for backbone models"
+        from os import listdir
+        from os.path import join, splitext
+        directory = Note.directory
+
+        notes = []
+        for fname in sorted(listdir(directory), reverse=True):
+            if fname.startswith("."): continue
+            notes.append(Note(fname))
+
+        page = int(request.GET.get("page"))
+        per_page = int(request.GET.get("perPage"))
+        slice_start = per_page * (page - 1)
+        slice_end = slice_start + per_page
+        # data that'll be serialized & passed to Collection.reset()
+        resp_data = {
+            "page": page, "perPage": per_page, "total": len(notes),
+            "models": [n.model_data() for n in notes[slice_start:slice_end]]}
+        return ps.HttpResponse(ps.json.dumps(resp_data), content_type="text/javascript")
+
+
+    if "js" in request.GET:
+        return js_source()
+    if "css" in request.GET:
+        return css_source()
+    if "col" in request.GET:
+        return notes_data()
+
+    return ps.render(request, "notes.html", {})
+
+
+# Oh why the hell not, Python. You should have given me
+# namespaces. Just a nice little syntax for grouping code, that's
+# all. Functions of a type, but not really related in the sense of
+# being on the same class.
+class Put:
+    @staticmethod
+    def note(request, fname):
+        posted_text = ps.json.loads(request.raw_post_data)['text']
+        n = Note(fname)
+        n.save_text(posted_text)
+        return ps.HttpResponse(ps.json.dumps(n.model_data()), content_type="text/javascript")
+

propaneweb/shortcuts.py

 from django.http import HttpResponsePermanentRedirect
 from django.http import HttpResponseRedirect
 from django.http import HttpResponseServerError
+from django.http import Http404
 
 from django.contrib.auth.decorators import login_required
 

propaneweb/templates/notes.css

     border: solid #eee 1px;
     margin-bottom: 20px;
     padding: 10px;
-    background-color: #eee
+    background-color: #eee;
+    position: relative;
 }
 
 .note h3
     font-weight: normal;
     font-size: 25px;
 }
+.note .edit
+{
+    cursor:pointer;
+    text-decoration: underline;
+    color: #00f;
+    position: absolute;
+    top: 0;
+    right: 0;
+}
 
 .note-text
 {

propaneweb/templates/notes.html

 
 {% block head_js %}{{ super() }}
 <script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/jquery/1.7/jquery.min.js"></script>
-<script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/underscore.js/1.2.2/underscore-min.js"></script>
-<script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/backbone.js/0.5.3/backbone-min.js"></script>
+<script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/underscore.js/1.3.1/underscore-min.js"></script>
+<script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/backbone.js/0.9.0/backbone-min.js"></script>
 <script type="text/javascript" src="?js"></script>
 {% endblock %}
 
 {% block body %}
 <div id="note-template" style="display: none;">
   <div class="note" data-note-timestamp="<%- timestamp %>" >
+    <a class="edit">edit</a>
     <h3><%- timestamp %></h3>
-    <div class="note-text"><%= text %></div>
+    <div class="note-text"><%= html %></div>
+  </div>
+</div>
+<div id="note-edit-template" style="display: none;">
+  <div class="note" data-note-timestamp="<%- timestamp %>" >
+    <h3><%- timestamp %></h3>
+    <textarea style="width: 100%;" name="text"><%= text %></textarea>
   </div>
 </div>
 <div id="notes-view">

propaneweb/templates/notes.js

     timestamp_display: function(){
         var ts = this.get("timestamp");
         return ts.substr(0, 10);
+    },
+    url: function(){
+        return "/notes/note/" + this.id;
     }
 });
 
     'click a.next': 'next'
   },
   render: function() {
-      this.el.find(".notes-pagination").html(get_template("#pagination-template")(this.collection.pageInfo()));
+      this.$el.find(".notes-pagination").html(get_template("#pagination-template")(this.collection.pageInfo()));
   },
 
   previous: function() {
     this.collection.previousPage();
     return false;
   },
-
   next: function() {
     this.collection.nextPage();
     return false;
   }
 });
 
-window.NoteView = Backbone.View.extend({
+// http://stackoverflow.com/a/499158/16361
+
+function setSelectionRange(input, selectionStart, selectionEnd) {
+  if (input.setSelectionRange) {
+    input.focus();
+    input.setSelectionRange(selectionStart, selectionEnd);
+  }
+  else if (input.createTextRange) {
+    var range = input.createTextRange();
+    range.collapse(true);
+    range.moveEnd('character', selectionEnd);
+    range.moveStart('character', selectionStart);
+    range.select();
+  }
+}
+
+function setCaretToPos (input, pos) {
+  setSelectionRange(input, pos, pos);
+}
+
+var NoteView = Backbone.View.extend({
     tagName: "div",
     template: get_template("#note-template"),
+    events: {
+        "click .edit": "startEdit",
+    },
+    initialize: function(){
+        _.bindAll(this, "startEdit");
+        this.edit_view = null;
+        this.model.bind("sync", function(){
+            this.render();
+        }, this);
+    },
     render: function(){
         $(this.el).html(this.template(this.template_context()));
         return this;
     },
     template_context: function(){
         return this.model.attributes;
+    },
+    startEdit: function(){
+        if(!this.edit_view){
+            this.edit_view = new EditableNoteView({model: this.model, el: this.el, view: this});
+        }
+        this.edit_view.render();
     }
 });
 
+    var EditableNoteView = Backbone.View.extend({
+        template: get_template("#note-edit-template"),
+        events: {
+            "blur textarea": "stopEdit"
+        },
+        initialize: function(){
+            _.bindAll(this, "stopEdit");
+        },
+        render: function(){
+            var width = this.$(".note-text").width();
+            var height = this.$(".note-text").height();
+            $(this.el).html(this.template(this.model.attributes));
+            this.$("textarea").width(width).height(height).focus();
+            setCaretToPos(this.$("textarea")[0], this.$("textarea").val().length);
+        },
+        stopEdit: function(){
+            var val = this.$("textarea").val()
+            this.model.set({"text": val});
+            this.model.save();
+            this.options.view.render().$(".note-text").css({"color": "#888"});
+        }
+    });
+
 var AppView = PaginatedView.extend({
     el: $("#notes-view"),
     initialize: function(options){

propaneweb/urls.py

     url(r'^music/(.*)', music, {}),
     url(r'^do/bbping', 'do_bbping', {}),
     url(r'^meta', 'meta', {}),
-    url(r'^notes/', 'notes', {}),
 )
 
+urlpatterns += patterns(
+    'propaneweb.note',
+    url(r'^notes/(.*)$', 'notes', {}),
+);

propaneweb/views.py

 
 
 
-
-def notes(request):
-    "Render notes template"
-    from .note import Note
-
-    if not request.user.is_authenticated():
-        return ps.HttpResponseRedirect("/")
-
-    # I'm the only user anyway
-    if request.user.username != 'akg':
-        return ps.HttpResponseRedirect('/')
-
-    def js_source():
-        "Get static JS"
-        from .util import get_jinja2_env
-        env = get_jinja2_env()
-        source, fname, uptodate = env.loader.get_source(None, "notes.js")
-        return ps.HttpResponse(source, content_type="text/javascript")
-
-    def css_source():
-        "Get static CSS"
-        from .util import get_jinja2_env
-        env = get_jinja2_env()
-        source, fname, uptodate = env.loader.get_source(None, "notes.css")
-        return ps.HttpResponse(source, content_type="text/css")
-
-    def notes_data():
-        "Get JSON data for backbone models"
-        from os import listdir
-        from os.path import join, splitext
-        directory = Note.directory
-
-        notes = []
-        for fname in sorted(listdir(directory), reverse=True):
-            if fname.startswith("."): continue
-            notes.append(Note(fname))
-
-        page = int(request.GET.get("page"))
-        per_page = int(request.GET.get("perPage"))
-        slice_start = per_page * (page - 1)
-        slice_end = slice_start + per_page
-        # data that'll be serialized & passed to Collection.reset()
-        resp_data = {
-            "page": page, "perPage": per_page, "total": len(notes),
-            "models": [n.model_data() for n in notes[slice_start:slice_end]]}
-        return ps.HttpResponse(ps.json.dumps(resp_data), content_type="text/javascript")
-
-
-    if "js" in request.GET:
-        return js_source()
-    if "css" in request.GET:
-        return css_source()
-    if "col" in request.GET:
-        return notes_data()
-
-    return ps.render(request, "notes.html", {})
-
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.