Galeria skanów z przewijaniem, bez zoom'a.
authorŁukasz Rekucki <lrekucki@gmail.com>
Wed, 30 Sep 2009 14:17:10 +0000 (16:17 +0200)
committerŁukasz Rekucki <lrekucki@gmail.com>
Wed, 30 Sep 2009 14:17:10 +0000 (16:17 +0200)
apps/api/handlers/library_handlers.py
apps/api/utils.py
apps/explorer/models.py
project/settings.py
project/static/css/master.css
project/static/js/views/gallery.js
project/templates/explorer/editor.html

index b2ff94b..b9e6314 100644 (file)
@@ -80,10 +80,7 @@ class LibraryHandler(BaseHandler):
             # not top-level anymore
             document_tree.pop(part)
             parent['parts'].append(child)
-
-        # sort the right way
         
-
         for doc in documents.itervalues():
             doc['parts'].sort(key=natural_order(lambda d: d['name']))
             
index 9b45a32..c072d35 100644 (file)
@@ -58,10 +58,12 @@ def hglibrary(func):
 
 
 import re
+import locale
+
 NAT_EXPR = re.compile(r'(\d+)', re.LOCALE | re.UNICODE)
 def natural_order(get_key=lambda x: x):
     def getter(key):
-        key = [int(x) if n%2 else x for (n,x) in enumerate(NAT_EXPR.split(get_key(key))) ]
+        key = [int(x) if n%2 else locale.strxfrm(x.encode('utf-8')) for (n,x) in enumerate(NAT_EXPR.split(get_key(key))) ]
         return key
 
     return getter
index a8a8c3b..7ab6b09 100644 (file)
@@ -102,4 +102,4 @@ class GalleryForDocument(models.Model):
     document = models.CharField(max_length=255)
 
     def __unicode__(self):
-        return u"%s:%s" % self.subpath, self.document
\ No newline at end of file
+        return u"%s:%s" % (self.subpath, self.document)
\ No newline at end of file
index 0551819..7d2ed64 100644 (file)
@@ -30,6 +30,9 @@ TIME_ZONE = 'Europe/Warsaw Poland'
 # http://www.i18nguy.com/unicode/language-identifiers.html
 LANGUAGE_CODE = 'pl'
 
+import locale
+locale.setlocale(locale.LC_ALL, '')
+
 SITE_ID = 1
 
 # If you set this to False, Django will make some optimizations so as not
index a3857e6..32ed073 100644 (file)
@@ -224,16 +224,62 @@ label {
     background-color: #DDD;
 }
 
-
-
-
 /* ================= */
 /* = Gallery panel = */
 /* ================= */
-.images-wrap {
-    overflow-x: hidden;
-    overflow-y: scroll;
+.image-gallery-view-template {
+    position: absolute;
+    top: 0px; left: 0px; right: 0px; bottom: 0px;
+    overflow: hidden;
+}
+
+.image-gallery-header {
+    position: absolute;
+    bottom: 0px;
+    left: 0px;
+    right: 0px;
+    height: 30px;
+
+    background: blue;
+    border: 1px solid green;
+}
+
+.image-gallery-header p {
+    margin: 0px;
+    padding: 3px 1em;
+    height: 30px;
+    line-height: 24px;
+    text-align: center;
+}
+
+.image-gallery-page-list {
+    position: absolute;
+    top: 0px;
+    left: 0px;
+    right: 0px;
+    bottom: 30px;
+    border: 1px solid red;
+    background: black;
+    z-index: 0;
+    
+    overflow: hidden;
 }
+.image-gallery-page-container {
+    display: none;        
+    border: none;
+
+    position: absolute;
+    top: 0px; left: 0px;
+        
+    text-align: center;    
+    padding: 0px;
+}
+
+.image-gallery-page-container img {
+    /* border: 2px solid green; */
+    margin: 0px;
+}
+
 
 /* =========================== */
 /* = DublinCore Editor panel = */
index 390aaad..9573656 100644 (file)
@@ -7,7 +7,7 @@ var ImageGalleryView = View.extend({
   
   init: function(element, model, parent, template) 
   {
-    this.currentPage = 0;
+    this.currentPage = -1;
     
     this._super(element, model, template);
     this.parent = parent;
@@ -22,23 +22,60 @@ var ImageGalleryView = View.extend({
   },
   
   modelDataChanged: function(property, value) 
-  {
-    $.log('updating pages', property, value);
+  {    
     if( property == 'data')
-    {        
-        this.gotoPage(this.currentPage);        
-        this.element.html(render_template(this.template, this));        
+    {
+        this.render();
+        this.gotoPage(this.currentPage);
     }   
   },
 
-  gotoPage: function(index) {
+  gotoPage: function(index) 
+  {
      if (index < 0) 
          index = 0;
-
-     var n = this.model.get('pages').length;
+    
+     var n = this.$pages.length;
      if (index >= n) index = n-1;
 
+     if( (this.currentPage == index) )
+         return;
+
+     var cpage = this.$currentPage();
+
+     if(cpage) {
+         var offset = this.pageViewOffset(cpage);
+         this.cleanPage(cpage);
+     }
+     
      this.currentPage = index;
+
+     cpage = this.$currentPage()
+     this.renderImage(cpage, cpage.attr('ui:model'));
+
+     if(offset) {
+         cpage.css({top: offset.y, left: offset.x});
+     }
+
+     var self = this;
+     $('img', cpage).bind('load', function() {
+        if(offset)
+             self.setPageViewOffset(cpage, offset);
+     });
+     
+     cpage.show();
+
+     if(this.currentPage == n-1)
+          this.$nextButton.attr('disabled', 'disabled');
+     else
+          this.$nextButton.removeAttr('disabled');
+
+      if(this.currentPage == 0)
+          this.$prevButton.attr('disabled', 'disabled');
+      else
+          this.$prevButton.removeAttr('disabled');
+
+      this.$pageInput.val( (this.currentPage+1) );
   },
   
   modelStateChanged: function(property, value) {
@@ -52,6 +89,150 @@ var ImageGalleryView = View.extend({
       this.parent.freeze('Zapisywanie...');
     }
   },
+
+  $currentPage: function() {
+      if(this.currentPage >= 0 && this.currentPage < this.$pages.length)
+          return $(this.$pages[this.currentPage]);
+      else
+          return undefined;
+  },    
+
+  cleanPage: function($page) {
+    $page.hide();
+    $('img', $page).unbind();
+    
+    $page.empty();
+    
+    this.setPageViewOffset($page, {x:0, y:0});
+  },
+
+  pageDragStart: function(event)
+  {      
+      this.dragStart = {x: event.clientX, y: event.clientY};
+      $(window).bind('mousemove.imagedrag', this.pageDrag.bind(this));
+      $(window).bind('mouseup.imagedrag', this.pageDragStop.bind(this));
+      
+      this.$currentPage().css('cursor', 'move');
+
+      return false;
+  },
+
+  pageDrag: function(event)
+  {
+      if(!this.dragStart) return;
+
+      var delta = {
+           x: this.dragStart.x - event.clientX,
+           y: this.dragStart.y - event.clientY };     
+
+      var offset = this.pageViewOffset( $(this.$pages[this.currentPage]) );
+      offset.x -= delta.x;
+      offset.y -= delta.y;
+      this.setPageViewOffset( $(this.$pages[this.currentPage]), offset);
+      
+      this.dragStart = {x: event.clientX, y: event.clientY };     
+      return false;
+  },
+
+  pageDragStop: function(event) {
+      this.$currentPage().css('cursor', 'auto');
+
+      this.dragStart = undefined;
+      $(window).unbind('mousemove.imagedrag');
+      $(window).unbind('mouseup.imagedrag');
+
+      return false;
+  },
+
+  pageViewOffset: function($page) {
+      var left = parseInt($page.css('left'));
+      var top = parseInt($page.css('top'));
+
+      return {x: left, y: top};
+  },
+
+  setPageViewOffset: function($page, offset) {
+      // check if the image will be actually visible
+      // and correct
+      var MARGIN = 30;
+
+      var vp_width = this.$pageListRoot.width();
+      var vp_height = this.$pageListRoot.height();
+      
+      var width = $page.outerWidth();
+      var height = $page.outerHeight();      
+
+      if( offset.x+width-MARGIN < 0 ) {
+        // console.log('too much on the left', offset.x, -width)
+        offset.x = -width+MARGIN;
+      }
+      
+      // too much on the right
+      if( offset.x > vp_width-MARGIN) {
+          offset.x = vp_width-MARGIN;
+          // console.log('too much on the right', offset.x, vp_width, width)
+      }
+
+      
+      if( offset.y+height-MARGIN < 0)
+        offset.y = -height+MARGIN;      
+
+      if( offset.y > vp_height-MARGIN)
+          offset.y = vp_height-MARGIN;               
+      
+      $page.css({left: offset.x, top: offset.y});           
+  }, 
+  
+  renderImage: function(target, source) {
+      target.html('<img src="'+source+'" />');
+      $('img', target).
+        css({
+            'user-select': 'none',
+            '-webkit-user-select': 'none',
+            '-khtml-user-select': 'none',
+            '-moz-user-select': 'none',
+        }).
+        attr('unselectable', 'on').
+        mousedown(this.pageDragStart.bind(this));
+  },
+
+  render: function() {
+      /* first unbind all */
+      
+      // this.pageListElement
+      if(this.$nextButton) this.$nextButton.unbind();
+      if(this.$prevButton) this.$prevButton.unbind();
+      if(this.$jumpButton) this.$jumpButton.unbind();
+
+      /* render */
+      this.element.html(render_template(this.template, this));
+
+      /* fetch important parts */
+      this.$pageListRoot = $('.image-gallery-page-list', this.element);
+      this.$pages = $('.image-gallery-page-container', this.$pageListRoot);
+
+      this.$nextButton = $('.image-gallery-next-button', this.element);
+      this.$prevButton = $('.image-gallery-prev-button', this.element);
+      this.$pageInput = $('.image-gallery-current-page', this.element);
+
+      /* re-bind events */
+      this.$nextButton.click( this.nextPage.bind(this) );
+      this.$prevButton.click( this.prevPage.bind(this) );
+
+      this.$pageInput.change( this.jumpToPage.bind(this) );
+  },
+
+  jumpToPage: function() {     
+        this.gotoPage(parseInt(this.$pageInput.val())-1);
+  },
+  
+  nextPage: function() {
+      this.gotoPage(this.currentPage + 1);    
+  },
+
+  prevPage: function() {
+      this.gotoPage(this.currentPage - 1);
+  },
   
   dispose: function() {
     this.model.removeObserver(this);
index c38dfc4..b51af17 100644 (file)
         <div class="image-gallery-header">
         <p>
         <button type="button" class="image-gallery-prev-button">Previous</button>
-        <input type="input" class="image-gallery-current-page" />
+        <input type="input" class="image-gallery-current-page"
+            size="6"
+            value="<%= (currentPage + 1) %>" />
+        <button type="button" class="image-gallery-jump-button">Go</button>
         <button type="button" class="image-gallery-next-button">Next</button>
+
+        <button type="button" class="image-gallery-zoom-in">+</button>
+        <select class="image-gallery-current-zoom">
+            <option>50%</option>
+            <option selected="selected">100%</option>
+            <option>150%</option>
+            <option>200%</option>
+        </select>
+        <button type="button" class="image-gallery-zoom-out">-</button>
         </p>
         </div>
 
-        <div>
+        <div class="image-gallery-page-list">
             <% for(var i=0; i < model.data.length; i++) { %>
-                <p class="image-gallery-page-container"><%= model.data[i] %></p>
+                <div class="image-gallery-page-container" ui:model="<%= model.data[i] %>">
+                </div>
             <% }; %>
        </div>