Home | History | Annotate | Download | only in ui
      1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 'use strict';
      6 
      7 base.require('ui');
      8 base.requireStylesheet('ui.drag_handle');
      9 
     10 base.exportTo('ui', function() {
     11 
     12   /**
     13    * Detects when user clicks handle determines new height of container based
     14    * on user's vertical mouse move and resizes the target.
     15    * @constructor
     16    * @extends {HTMLDivElement}
     17    * You will need to set target to be the draggable element
     18    */
     19   var DragHandle = ui.define('x-drag-handle');
     20 
     21   DragHandle.prototype = {
     22     __proto__: HTMLDivElement.prototype,
     23 
     24     decorate: function() {
     25       this.lastMousePos_ = 0;
     26       this.onMouseMove_ = this.onMouseMove_.bind(this);
     27       this.onMouseUp_ = this.onMouseUp_.bind(this);
     28       this.addEventListener('mousedown', this.onMouseDown_);
     29       this.target_ = undefined;
     30       this.horizontal = true;
     31       this.observer_ = new WebKitMutationObserver(
     32           this.didTargetMutate_.bind(this));
     33       this.targetSizesByModeKey_ = {};
     34     },
     35 
     36     get modeKey_() {
     37       return this.target_.className == '' ? '.' : this.target_.className;
     38     },
     39 
     40     get target() {
     41       return this.target_;
     42     },
     43 
     44     set target(target) {
     45       this.observer_.disconnect();
     46       this.target_ = target;
     47       if (!this.target_)
     48         return;
     49       this.observer_.observe(this.target_, {
     50         attributes: true,
     51         attributeFilter: ['class']
     52       });
     53     },
     54 
     55     get horizontal() {
     56       return this.horizontal_;
     57     },
     58 
     59     set horizontal(h) {
     60       this.horizontal_ = h;
     61       if (this.horizontal_)
     62         this.className = 'horizontal-drag-handle';
     63       else
     64         this.className = 'vertical-drag-handle';
     65     },
     66 
     67     get vertical() {
     68       return !this.horizontal_;
     69     },
     70 
     71     set vertical(v) {
     72       this.horizontal = !v;
     73     },
     74 
     75     forceMutationObserverFlush_: function() {
     76       var records = this.observer_.takeRecords();
     77       if (records.length)
     78         this.didTargetMutate_(records);
     79     },
     80 
     81     didTargetMutate_: function(e) {
     82       var modeSize = this.targetSizesByModeKey_[this.modeKey_];
     83       if (modeSize !== undefined) {
     84         this.setTargetSize_(modeSize);
     85         return;
     86       }
     87 
     88       // If we hadn't previously sized the target, then just remove any manual
     89       // sizing that we applied.
     90       this.target_.style[this.targetStyleKey_] = '';
     91     },
     92 
     93     get targetStyleKey_() {
     94       return this.horizontal_ ? 'height' : 'width';
     95     },
     96 
     97     getTargetSize_: function() {
     98       // If style is not set, start off with computed height.
     99       var targetStyleKey = this.targetStyleKey_;
    100       if (!this.target_.style[targetStyleKey]) {
    101         this.target_.style[targetStyleKey] =
    102             window.getComputedStyle(this.target_)[targetStyleKey];
    103       }
    104       var size = parseInt(this.target_.style[targetStyleKey]);
    105       this.targetSizesByModeKey_[this.modeKey_] = size;
    106       return size;
    107     },
    108 
    109     setTargetSize_: function(s) {
    110       this.target_.style[this.targetStyleKey_] = s + 'px';
    111       this.targetSizesByModeKey_[this.modeKey_] = s;
    112     },
    113 
    114     applyDelta_: function(delta) {
    115       // Apply new size to the container.
    116       var curSize = this.getTargetSize_();
    117       var newSize;
    118       if (this.target_ == this.nextSibling) {
    119         newSize = curSize + delta;
    120       } else {
    121         if (this.target_ != this.previousSibling)
    122           throw Error('Must be next sibling');
    123         newSize = curSize - delta;
    124       }
    125       this.setTargetSize_(newSize);
    126     },
    127 
    128     onMouseMove_: function(e) {
    129       // Compute the difference in height position.
    130       var curMousePos = this.horizontal_ ? e.clientY : e.clientX;
    131       var delta = this.lastMousePos_ - curMousePos;
    132 
    133       this.applyDelta_(delta);
    134 
    135       this.lastMousePos_ = curMousePos;
    136       e.preventDefault();
    137       return true;
    138     },
    139 
    140     onMouseDown_: function(e) {
    141       if (!this.target_)
    142         return;
    143       this.forceMutationObserverFlush_();
    144       this.lastMousePos_ = this.horizontal_ ? e.clientY : e.clientX;
    145       document.addEventListener('mousemove', this.onMouseMove_);
    146       document.addEventListener('mouseup', this.onMouseUp_);
    147       e.preventDefault();
    148       return true;
    149     },
    150 
    151     onMouseUp_: function(e) {
    152       document.removeEventListener('mousemove', this.onMouseMove_);
    153       document.removeEventListener('mouseup', this.onMouseUp_);
    154       e.preventDefault();
    155     }
    156   };
    157 
    158   return {
    159     DragHandle: DragHandle
    160   };
    161 });
    162