Don't like ads? PRO users don't see any ads ;-)
Guest

Untitled

By: a guest on May 7th, 2012  |  syntax: None  |  size: 6.48 KB  |  hits: 11  |  expires: Never
download  |  raw  |  embed  |  report abuse  |  print
Text below is selected. Please press Ctrl+C to copy to your clipboard. (⌘+C on Mac)
  1. /*
  2. ---
  3.  
  4. script: Position.js
  5.  
  6. description: An observable object
  7.  
  8. license: Public domain (http://unlicense.org).
  9.  
  10. authors: Yaroslaff Fedin
  11.  
  12. requires:
  13.   - LSD
  14.  
  15. provides:
  16.   - LSD.Position
  17.  
  18. ...
  19. */
  20.  
  21. LSD.Position = function(object, options) {
  22.   this.object = object;
  23.   this.setOptions(options);
  24. };
  25.  
  26. LSD.Position.fallback = ['flip', 'hug', 'invert'];
  27.  
  28. (function() {
  29.   var expanded = {};
  30.   var vertical = {top: true, bottom: true, left: false, right: false};
  31.   var props = ['left', 'top'], dimensions = ['x', 'y']
  32.   var oprops = {left: true, top: true};
  33.   var flip = {left: 'right', right: 'left', bottom: 'top', top: 'bottom'};
  34.   LSD.Position.calculate = function(object, boundaries, position, anchor, fallback) {
  35.     if (!position) position = ['center', 'center']
  36.     else if (position.split) {
  37.       // expand position (left -> [left, center])
  38.       var pos = expanded[position];
  39.       if (!pos) expanded[position] = pos = position.split('-');
  40.       position = pos;
  41.     }
  42.     if (!position[1] || vertical[position[0]] == vertical[position[1]]) position[1] = 'center';
  43.     if (!anchor) {
  44.       // no anchor given, object either fits into boundaries or not
  45.       if (object.x > boundaries.x || object.y > boundaries.y) return false;
  46.       var result = {};
  47.       for (var i = 0, pos; pos = position[i]; i++)
  48.         if (pos == 'center') {
  49.           var index = vertical[position[+!i]];
  50.           index = (index == null) ? i : +!index;
  51.           result[index ? 'y' : 'x'] = ((boundaries[dimensions[index]] || 0) - object[dimensions[index]]) / 2;
  52.         } else {
  53.           var index = +vertical[pos];
  54.           result[index ? 'y' : 'x'] = (oprops[pos] ? 0 : ((boundaries[dimensions[index]] || 0) - object[dimensions[index]]))
  55.         }
  56.       if (boundaries.left) result.x += boundaries.left;  
  57.       if (boundaries.top) result.y += boundaries.top;
  58.     } else {
  59.       // positioning against anchor means different boundaries and another position
  60.       var pos = position[0], otherpos = position[1];
  61.       var index = +!!vertical[position[0]];
  62.       var query = {};
  63.       switch (position[0]) {
  64.         case "center":
  65.        
  66.           break;
  67.         case props[index]:
  68.           query[props[index]] = 0;
  69.           query[dimensions[index]] = anchor[props[index]];
  70.           break;
  71.         default:
  72.           query[props[index]] = anchor[props[index]] + anchor[dimensions[index]];
  73.           query[dimensions[index]] = (boundaries[props[index]] || 0) + boundaries[dimensions[index]] - anchor[dimensions[index]] - anchor[props[index]];
  74.       };
  75.       switch (position[1]) {
  76.         case "center":
  77.           query[props[+!index]] = anchor[props[+!index]] + anchor[dimensions[+!index]] / 2
  78.           break;
  79.         case props[+!index]:
  80.           query[props[+!index]] = anchor[props[+!index]];
  81.           break;
  82.         default:
  83.           query[dimensions[+!index]] = anchor[props[+!index]] + anchor[dimensions[+!index]];
  84.       }
  85.       // positioning an object at top right relative to anchor,
  86.       // means positioning at bottom right of rectangle space above the anchor.
  87.       var result = LSD.Position.calculate(object, query, [flip[position[0]], position[1]]);
  88.       if (result) {
  89.         //result[dimensions[+!index]] -= anchor[props[+!index]];
  90.       } else if (fallback) {
  91.         // if the object does not fit there, fallback method may be used
  92.         if (fallback === true) fallback = LSD.Position.fallback;
  93.         else if (fallback.match) fallback = [fallback];
  94.         for (var i = 0, method, attempt; method = fallback[i++];) {
  95.           switch (method) {
  96.             case "flip":
  97.               // tries "right top", closest corner on the same side
  98.               attempt = [position[1], position[0]];
  99.               break;
  100.             case "hug":
  101.               // tries "left top", distant corner on the same side
  102.               attempt = [flip[position[1]], position[0]];
  103.               break;
  104.             case "invert":
  105.               // tries "bottom right", same corner on the other side
  106.               attempt = [flip[position[0]], position[1]];
  107.           }
  108.           var result = LSD.Position.calculate(object, boundaries, attempt, anchor);
  109.           if (result) break;
  110.         }
  111.       }
  112.     }
  113.     return result;
  114.   };
  115. })();
  116.  
  117. LSD.Position.getDefaultBoundaries = function() {
  118.   return this.object.ownerDocument.body;
  119. };
  120.  
  121. LSD.Position.prototype = {
  122.   setOptions: function(options) {
  123.     if (!options.match && !options.push) {
  124.       for (var name in options) {
  125.         var value = options[name];
  126.         switch (name) {
  127.           case "boundaries":
  128.             if (value === true) value = LSD.Position.getDefaultBoundaries;
  129.             break;
  130.         }
  131.         this[name] = value;
  132.       }
  133.     } else this.attachment = options;
  134.     this.attach(this.attachment)
  135.   },
  136.   attach: function(attachment) {
  137.     this.update(attachment)
  138.   },
  139.  
  140.   detach: function() {
  141.    
  142.   },
  143.  
  144.   resolve: function(object, position) {
  145.     if (object.call) object = object.call(this);
  146.     if (object.lsd) {
  147.       var result = {};
  148.       result.x = object.size.width;
  149.       result.y = object.size.height;
  150.       if (position) {
  151.         result.left = object.style.left || 0;
  152.         result.top = object.style.top || 0
  153.       }
  154.     } else if (object.localName) {
  155.       var result = Element.getSize(object);
  156.       if (position) {
  157.         position = Element.getPosition(object);
  158.         result.left = position.x;
  159.         result.top = position.y;
  160.       }
  161.     }
  162.     return result || object;
  163.   },
  164.  
  165.   update: function(attachment) {
  166.     var coordinates = this.coordinates;
  167.     var object, boundaries, anchor, fallback;
  168.     var object = this.resolve(this.object);
  169.     if ((boundaries = this.boundaries)) boundaries = this.resolve(boundaries);
  170.     if ((anchor = this.anchor)) anchor = this.resolve(anchor, true);
  171.     if ((fallback = this.fallback) && fallback.call) fallback = fallback.call(this);
  172.     this.coordinates = LSD.Position.calculate(object, boundaries, attachment, anchor, fallback);
  173.     if (!this.coordinates) {
  174.       this.unset(coordinates);
  175.       delete this.coordinates;
  176.     } else this.set(this.coordinates);
  177.   },
  178.  
  179.   set: function(styles) {
  180.     for (var name in styles) {
  181.       var prop = name == 'x' ? 'left' : 'top';
  182.       if (this.object.lsd)
  183.         this.object.setStyle(name, styles[name]);
  184.       else
  185.         this.object.style[prop] = styles[name] + 'px';
  186.     }
  187.   },
  188.  
  189.   unset: function(styles) {
  190.     for (var name in styles){
  191.       var prop = name == 'x' ? 'left' : 'top';
  192.       if (this.object.lsd)
  193.         this.object.setStyle(name, 0);
  194.       else
  195.         delete this.object.style[prop];
  196.     }
  197.   },
  198.  
  199.   unsetStyle: function() {
  200.    
  201.   }
  202. };