• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
7base.require('ui');
8base.requireStylesheet('ui.drag_handle');
9
10base.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