Upstream version 11.39.266.0
[platform/framework/web/crosswalk.git] / src / third_party / trace-viewer / third_party / tvcm / src / tvcm / ui / mouse_mode_selector.html
1 <!DOCTYPE html>
2 <!--
3 Copyright (c) 2013 The Chromium Authors. All rights reserved.
4 Use of this source code is governed by a BSD-style license that can be
5 found in the LICENSE file.
6 -->
7
8 <link rel="import" href="/tvcm/events.html">
9 <link rel="import" href="/tvcm/iteration_helpers.html">
10 <link rel="import" href="/tvcm/utils.html">
11 <link rel="import" href="/tvcm/key_event_manager.html">
12 <link rel="import" href="/tvcm/ui.html">
13 <link rel="import" href="/tvcm/ui/mouse_tracker.html">
14
15 <link rel="stylesheet" href="/tvcm/ui/mouse_mode_selector.css">
16 <link rel="stylesheet" href="/tvcm/ui/tool_button.css">
17
18 <template id="mouse-mode-selector-template">
19   <div class="drag-handle"></div>
20   <div class="buttons">
21   </div>
22 </template>
23
24 <script>
25 'use strict';
26
27 tvcm.exportTo('tvcm.ui', function() {
28
29   var THIS_DOC = document.currentScript.ownerDocument;
30
31   var MIN_MOUSE_SELECTION_DISTANCE = 4;
32
33   var MOUSE_SELECTOR_MODE = {};
34   MOUSE_SELECTOR_MODE.SELECTION = 0x1;
35   MOUSE_SELECTOR_MODE.PANSCAN = 0x2;
36   MOUSE_SELECTOR_MODE.ZOOM = 0x4;
37   MOUSE_SELECTOR_MODE.TIMING = 0x8;
38   MOUSE_SELECTOR_MODE.ROTATE = 0x10;
39   MOUSE_SELECTOR_MODE.ALL_MODES = 0x1F;
40
41   var allModeInfo = {};
42   allModeInfo[MOUSE_SELECTOR_MODE.PANSCAN] = {
43     title: 'pan',
44     className: 'pan-scan-mode-button',
45     eventNames: {
46       enter: 'enterpan',
47       begin: 'beginpan',
48       update: 'updatepan',
49       end: 'endpan',
50       exit: 'exitpan'
51     }
52   };
53   allModeInfo[MOUSE_SELECTOR_MODE.SELECTION] = {
54     title: 'selection',
55     className: 'selection-mode-button',
56     eventNames: {
57       enter: 'enterselection',
58       begin: 'beginselection',
59       update: 'updateselection',
60       end: 'endselection',
61       exit: 'exitselection'
62     }
63   };
64
65   allModeInfo[MOUSE_SELECTOR_MODE.ZOOM] = {
66     title: 'zoom',
67     className: 'zoom-mode-button',
68     eventNames: {
69       enter: 'enterzoom',
70       begin: 'beginzoom',
71       update: 'updatezoom',
72       end: 'endzoom',
73       exit: 'exitzoom'
74     }
75   };
76   allModeInfo[MOUSE_SELECTOR_MODE.TIMING] = {
77     title: 'timing',
78     className: 'timing-mode-button',
79     eventNames: {
80       enter: 'entertiming',
81       begin: 'begintiming',
82       update: 'updatetiming',
83       end: 'endtiming',
84       exit: 'exittiming'
85     }
86   };
87   allModeInfo[MOUSE_SELECTOR_MODE.ROTATE] = {
88     title: 'rotate',
89     className: 'rotate-mode-button',
90     eventNames: {
91       enter: 'enterrotate',
92       begin: 'beginrotate',
93       update: 'updaterotate',
94       end: 'endrotate',
95       exit: 'exitrotate'
96     }
97   };
98
99   var MODIFIER = {
100     SHIFT: 0x1,
101     SPACE: 0x2,
102     CMD_OR_CTRL: 0x4
103   };
104
105   /**
106    * Provides a panel for switching the interaction mode of the mouse.
107    * It handles the user interaction and dispatches events for the various
108    * modes.
109    *
110    * @constructor
111    * @extends {HTMLDivElement}
112    */
113   var MouseModeSelector = tvcm.ui.define('div');
114
115   MouseModeSelector.prototype = {
116     __proto__: HTMLDivElement.prototype,
117
118     decorate: function(opt_targetElement) {
119       this.classList.add('mouse-mode-selector');
120
121       var node = tvcm.instantiateTemplate('#mouse-mode-selector-template',
122                                           THIS_DOC);
123       this.appendChild(node);
124
125       this.buttonsEl_ = this.querySelector('.buttons');
126       this.dragHandleEl_ = this.querySelector('.drag-handle');
127
128       this.supportedModeMask = MOUSE_SELECTOR_MODE.ALL_MODES;
129
130       this.initialRelativeMouseDownPos_ = {x: 0, y: 0};
131
132       this.defaultMode_ = MOUSE_SELECTOR_MODE.PANSCAN;
133       this.settingsKey_ = undefined;
134       this.mousePos_ = {x: 0, y: 0};
135       this.mouseDownPos_ = {x: 0, y: 0};
136
137       this.dragHandleEl_.addEventListener('mousedown',
138           this.onDragHandleMouseDown_.bind(this));
139
140       this.onMouseDown_ = this.onMouseDown_.bind(this);
141       this.onMouseMove_ = this.onMouseMove_.bind(this);
142       this.onMouseUp_ = this.onMouseUp_.bind(this);
143
144       this.buttonsEl_.addEventListener('mouseup', this.onButtonMouseUp_);
145       this.buttonsEl_.addEventListener('mousedown', this.onButtonMouseDown_);
146       this.buttonsEl_.addEventListener('click', this.onButtonPress_.bind(this));
147
148       tvcm.KeyEventManager.instance.addListener(
149           'keydown', this.onKeyDown_, this);
150       tvcm.KeyEventManager.instance.addListener(
151           'keyup', this.onKeyUp_, this);
152
153       this.mode_ = undefined;
154       this.modeToKeyCodeMap_ = {};
155       this.modifierToModeMap_ = {};
156
157       this.targetElement = opt_targetElement;
158       this.spacePressed_ = false;
159       this.modeBeforeAlternativeModeActivated_ = null;
160
161       this.isInteracting_ = false;
162       this.isClick_ = false;
163     },
164
165     get targetElement() {
166       return this.targetElement_;
167     },
168
169     set targetElement(target) {
170       if (this.targetElement_)
171         this.targetElement_.removeEventListener('mousedown', this.onMouseDown_);
172       this.targetElement_ = target;
173       if (this.targetElement_)
174         this.targetElement_.addEventListener('mousedown', this.onMouseDown_);
175     },
176
177     get defaultMode() {
178       return this.defaultMode_;
179     },
180
181     set defaultMode(defaultMode) {
182       this.defaultMode_ = defaultMode;
183     },
184
185     get settingsKey() {
186       return this.settingsKey_;
187     },
188
189     set settingsKey(settingsKey) {
190       this.settingsKey_ = settingsKey;
191       if (!this.settingsKey_)
192         return;
193
194       var mode = tvcm.Settings.get(this.settingsKey_ + '.mode', undefined);
195       // Modes changed from 1,2,3,4 to 0x1, 0x2, 0x4, 0x8. Fix any stray
196       // settings to the best of our abilities.
197       if (allModeInfo[mode] === undefined)
198         mode = undefined;
199
200       // Restoring settings against unsupported modes should just go back to the
201       // default mode.
202       if ((mode & this.supportedModeMask_) === 0)
203         mode = undefined;
204
205       if (!mode)
206         mode = this.defaultMode_;
207       this.mode = mode;
208
209       var pos = tvcm.Settings.get(this.settingsKey_ + '.pos', undefined);
210       if (pos)
211         this.pos = pos;
212     },
213
214     get supportedModeMask() {
215       return this.supportedModeMask_;
216     },
217
218     /**
219      * Sets the supported modes. Should be an OR-ing of MOUSE_SELECTOR_MODE
220      * values.
221      */
222     set supportedModeMask(supportedModeMask) {
223       if (this.mode && (supportedModeMask & this.mode) === 0)
224         throw new Error('supportedModeMask must include current mode.');
225
226       function createButtonForMode(mode) {
227         var button = document.createElement('div');
228         button.mode = mode;
229         button.title = allModeInfo[mode].title;
230         button.classList.add('tool-button');
231         button.classList.add(allModeInfo[mode].className);
232         return button;
233       }
234
235       this.supportedModeMask_ = supportedModeMask;
236       this.buttonsEl_.textContent = '';
237       for (var modeName in MOUSE_SELECTOR_MODE) {
238         if (modeName == 'ALL_MODES')
239           continue;
240         var mode = MOUSE_SELECTOR_MODE[modeName];
241         if ((this.supportedModeMask_ & mode) === 0)
242           continue;
243         this.buttonsEl_.appendChild(createButtonForMode(mode));
244       }
245     },
246
247     get mode() {
248       return this.currentMode_;
249     },
250
251     set mode(newMode) {
252       if (newMode !== undefined) {
253         if (typeof newMode !== 'number')
254           throw new Error('Mode must be a number');
255         if ((newMode & this.supportedModeMask_) === 0)
256           throw new Error('Cannot switch to this mode, it is not supported');
257         if (allModeInfo[newMode] === undefined)
258           throw new Error('Unrecognized mode');
259       }
260
261       var modeInfo;
262
263       if (this.currentMode_ === newMode)
264         return;
265
266       if (this.currentMode_) {
267         modeInfo = allModeInfo[this.currentMode_];
268         var buttonEl = this.buttonsEl_.querySelector('.' + modeInfo.className);
269         if (buttonEl)
270           buttonEl.classList.remove('active');
271
272         // End event.
273         if (this.isInteracting_) {
274
275           var mouseEvent = this.createEvent_(
276               allModeInfo[this.mode].eventNames.end);
277           this.dispatchEvent(mouseEvent);
278         }
279
280         // Exit event.
281         tvcm.dispatchSimpleEvent(this, modeInfo.eventNames.exit, true);
282       }
283
284       this.currentMode_ = newMode;
285
286       if (this.currentMode_) {
287         modeInfo = allModeInfo[this.currentMode_];
288         var buttonEl = this.buttonsEl_.querySelector('.' + modeInfo.className);
289         if (buttonEl)
290           buttonEl.classList.add('active');
291
292         // Entering a new mode resets mouse down pos.
293         this.mouseDownPos_.x = this.mousePos_.x;
294         this.mouseDownPos_.y = this.mousePos_.y;
295
296         // Enter event.
297         if (!this.isInAlternativeMode_)
298           tvcm.dispatchSimpleEvent(this, modeInfo.eventNames.enter, true);
299
300         // Begin event.
301         if (this.isInteracting_) {
302           var mouseEvent = this.createEvent_(
303               allModeInfo[this.mode].eventNames.begin);
304           this.dispatchEvent(mouseEvent);
305         }
306
307
308       }
309
310       if (this.settingsKey_ && !this.isInAlternativeMode_)
311         tvcm.Settings.set(this.settingsKey_ + '.mode', this.mode);
312     },
313
314     setKeyCodeForMode: function(mode, keyCode) {
315       if ((mode & this.supportedModeMask_) === 0)
316         throw new Error('Mode not supported');
317       this.modeToKeyCodeMap_[mode] = keyCode;
318
319       if (!this.buttonsEl_)
320         return;
321
322       var modeInfo = allModeInfo[mode];
323       var buttonEl = this.buttonsEl_.querySelector('.' + modeInfo.className);
324       if (buttonEl) {
325         buttonEl.title =
326             modeInfo.title + ' (' + String.fromCharCode(keyCode) + ')';
327       }
328     },
329
330     setCurrentMousePosFromEvent_: function(e) {
331       this.mousePos_.x = e.clientX;
332       this.mousePos_.y = e.clientY;
333     },
334
335     createEvent_: function(eventName, sourceEvent) {
336       var event = new tvcm.Event(eventName, true);
337       event.clientX = this.mousePos_.x;
338       event.clientY = this.mousePos_.y;
339       event.deltaX = this.mousePos_.x - this.mouseDownPos_.x;
340       event.deltaY = this.mousePos_.y - this.mouseDownPos_.y;
341       event.mouseDownX = this.mouseDownPos_.x;
342       event.mouseDownY = this.mouseDownPos_.y;
343       event.didPreventDefault = false;
344       event.preventDefault = function() {
345         event.didPreventDefault = true;
346         if (sourceEvent)
347           sourceEvent.preventDefault();
348       };
349       event.stopPropagation = function() {
350         sourceEvent.stopPropagation();
351       };
352       event.stopImmediatePropagation = function() {
353         throw new Error('Not implemented');
354       };
355       return event;
356     },
357
358     onMouseDown_: function(e) {
359       if (e.button !== 0)
360         return;
361       this.setCurrentMousePosFromEvent_(e);
362       var mouseEvent = this.createEvent_(
363           allModeInfo[this.mode].eventNames.begin, e);
364       this.dispatchEvent(mouseEvent);
365       this.isInteracting_ = true;
366       this.isClick_ = true;
367       tvcm.ui.trackMouseMovesUntilMouseUp(this.onMouseMove_, this.onMouseUp_);
368     },
369
370     onMouseMove_: function(e) {
371       this.setCurrentMousePosFromEvent_(e);
372
373       var mouseEvent = this.createEvent_(
374           allModeInfo[this.mode].eventNames.update, e);
375       this.dispatchEvent(mouseEvent);
376
377       if (this.isInteracting_)
378         this.checkIsClick_(e);
379     },
380
381     onMouseUp_: function(e) {
382       if (e.button !== 0)
383         return;
384
385       var mouseEvent = this.createEvent_(
386           allModeInfo[this.mode].eventNames.end, e);
387       mouseEvent.isClick = this.isClick_;
388       this.dispatchEvent(mouseEvent);
389
390       if (this.isClick_ && !mouseEvent.didPreventDefault)
391         this.dispatchClickEvents_(e);
392
393       this.isInteracting_ = false;
394       this.updateAlternativeModeState_(e);
395     },
396
397     onButtonMouseDown_: function(e) {
398       e.preventDefault();
399       e.stopImmediatePropagation();
400     },
401
402     onButtonMouseUp_: function(e) {
403       e.preventDefault();
404       e.stopImmediatePropagation();
405     },
406
407     onButtonPress_: function(e) {
408       this.modeBeforeAlternativeModeActivated_ = undefined;
409       this.mode = e.target.mode;
410       e.preventDefault();
411     },
412
413     onKeyDown_: function(e) {
414       if (e.keyCode === ' '.charCodeAt(0))
415         this.spacePressed_ = true;
416       this.updateAlternativeModeState_(e);
417     },
418
419     onKeyUp_: function(e) {
420       if (e.keyCode === ' '.charCodeAt(0))
421         this.spacePressed_ = false;
422
423       var didHandleKey = false;
424       tvcm.iterItems(this.modeToKeyCodeMap_, function(modeStr, keyCode) {
425         if (e.keyCode === keyCode) {
426           this.modeBeforeAlternativeModeActivated_ = undefined;
427           var mode = parseInt(modeStr);
428           this.mode = mode;
429           didHandleKey = true;
430         }
431       }, this);
432
433       if (didHandleKey) {
434         e.preventDefault();
435         e.stopPropagation();
436         return;
437       }
438       this.updateAlternativeModeState_(e);
439     },
440
441     updateAlternativeModeState_: function(e) {
442       var shiftPressed = e.shiftKey;
443       var spacePressed = this.spacePressed_;
444       var cmdOrCtrlPressed =
445           (tvcm.isMac && e.metaKey) || (!tvcm.isMac && e.ctrlKey);
446
447       // Figure out the new mode
448       var smm = this.supportedModeMask_;
449       var newMode;
450       var isNewModeAnAlternativeMode = false;
451       if (shiftPressed &&
452           (this.modifierToModeMap_[MODIFIER.SHIFT] & smm) !== 0) {
453         newMode = this.modifierToModeMap_[MODIFIER.SHIFT];
454         isNewModeAnAlternativeMode = true;
455       } else if (spacePressed &&
456                  (this.modifierToModeMap_[MODIFIER.SPACE] & smm) !== 0) {
457         newMode = this.modifierToModeMap_[MODIFIER.SPACE];
458         isNewModeAnAlternativeMode = true;
459       } else if (cmdOrCtrlPressed &&
460                  (this.modifierToModeMap_[MODIFIER.CMD_OR_CTRL] & smm) !== 0) {
461         newMode = this.modifierToModeMap_[MODIFIER.CMD_OR_CTRL];
462         isNewModeAnAlternativeMode = true;
463       } else {
464         // Go to the old mode, if there is one.
465         if (this.isInAlternativeMode_) {
466           newMode = this.modeBeforeAlternativeModeActivated_;
467           isNewModeAnAlternativeMode = false;
468         } else {
469           newMode = undefined;
470         }
471       }
472
473       // Maybe a mode change isn't needed.
474       if (this.mode === newMode || newMode === undefined)
475         return;
476
477       // Okay, we're changing.
478       if (isNewModeAnAlternativeMode)
479         this.modeBeforeAlternativeModeActivated_ = this.mode;
480       this.mode = newMode;
481     },
482
483     get isInAlternativeMode_() {
484       return !!this.modeBeforeAlternativeModeActivated_;
485     },
486
487     setModifierForAlternateMode: function(mode, modifier) {
488       this.modifierToModeMap_[modifier] = mode;
489     },
490
491     get pos() {
492       return {
493         x: parseInt(this.style.left),
494         y: parseInt(this.style.top)
495       };
496     },
497
498     set pos(pos) {
499       pos = this.constrainPositionToBounds_(pos);
500
501       this.style.left = pos.x + 'px';
502       this.style.top = pos.y + 'px';
503
504       if (this.settingsKey_)
505         tvcm.Settings.set(this.settingsKey_ + '.pos', this.pos);
506     },
507
508     constrainPositionToBounds_: function(pos) {
509       var parent = this.offsetParent || document.body;
510       var parentRect = tvcm.windowRectForElement(parent);
511
512       var top = 0;
513       var bottom = parentRect.height - this.offsetHeight;
514       var left = 0;
515       var right = parentRect.width - this.offsetWidth;
516
517       var res = {};
518       res.x = Math.max(pos.x, left);
519       res.x = Math.min(res.x, right);
520
521       res.y = Math.max(pos.y, top);
522       res.y = Math.min(res.y, bottom);
523       return res;
524     },
525
526     onDragHandleMouseDown_: function(e) {
527       e.preventDefault();
528       e.stopImmediatePropagation();
529
530       var mouseDownPos = {
531         x: e.clientX - this.offsetLeft,
532         y: e.clientY - this.offsetTop
533       };
534       tvcm.ui.trackMouseMovesUntilMouseUp(function(e) {
535         var pos = {};
536         pos.x = e.clientX - mouseDownPos.x;
537         pos.y = e.clientY - mouseDownPos.y;
538         this.pos = pos;
539       }.bind(this));
540     },
541
542     checkIsClick_: function(e) {
543       if (!this.isInteracting_ || !this.isClick_)
544         return;
545
546       var deltaX = this.mousePos_.x - this.mouseDownPos_.x;
547       var deltaY = this.mousePos_.y - this.mouseDownPos_.y;
548       var minDist = MIN_MOUSE_SELECTION_DISTANCE;
549
550       if (deltaX * deltaX + deltaY * deltaY > minDist * minDist)
551         this.isClick_ = false;
552     },
553
554     dispatchClickEvents_: function(e) {
555       if (!this.isClick_)
556         return;
557
558       var eventNames = allModeInfo[MOUSE_SELECTOR_MODE.SELECTION].eventNames;
559
560       var mouseEvent = this.createEvent_(eventNames.begin);
561       this.dispatchEvent(mouseEvent);
562
563       mouseEvent = this.createEvent_(eventNames.end);
564       this.dispatchEvent(mouseEvent);
565     }
566   };
567
568   return {
569     MIN_MOUSE_SELECTION_DISTANCE: MIN_MOUSE_SELECTION_DISTANCE,
570     MouseModeSelector: MouseModeSelector,
571     MOUSE_SELECTOR_MODE: MOUSE_SELECTOR_MODE,
572     MODIFIER: MODIFIER
573   };
574 });
575 </script>