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