123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265 |
- /**
- * @license
- * Visual Blocks Editor
- *
- * Copyright 2016 Google Inc.
- * https://developers.google.com/blockly/
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- /**
- * @fileoverview Touch handling for Blockly.
- * @author fenichel@google.com (Rachel Fenichel)
- */
- 'use strict';
- goog.provide('Blockly.Touch');
- goog.require('goog.events');
- goog.require('goog.events.BrowserFeature');
- goog.require('goog.string');
- /**
- * Which touch events are we currently paying attention to?
- * @type {DOMString}
- * @private
- */
- Blockly.Touch.touchIdentifier_ = null;
- /**
- * Wrapper function called when a touch mouseUp occurs during a drag operation.
- * @type {Array.<!Array>}
- * @private
- */
- Blockly.Touch.onTouchUpWrapper_ = null;
- /**
- * The TOUCH_MAP lookup dictionary specifies additional touch events to fire,
- * in conjunction with mouse events.
- * @type {Object}
- */
- Blockly.Touch.TOUCH_MAP = {};
- if (goog.events.BrowserFeature.TOUCH_ENABLED) {
- Blockly.Touch.TOUCH_MAP = {
- 'mousedown': ['touchstart'],
- 'mousemove': ['touchmove'],
- 'mouseup': ['touchend', 'touchcancel']
- };
- }
- /**
- * PID of queued long-press task.
- * @private
- */
- Blockly.longPid_ = 0;
- /**
- * Context menus on touch devices are activated using a long-press.
- * Unfortunately the contextmenu touch event is currently (2015) only suported
- * by Chrome. This function is fired on any touchstart event, queues a task,
- * which after about a second opens the context menu. The tasks is killed
- * if the touch event terminates early.
- * @param {!Event} e Touch start event.
- * @param {!Blockly.Block|!Blockly.WorkspaceSvg} uiObject The block or workspace
- * under the touchstart event.
- * @private
- */
- Blockly.longStart_ = function(e, uiObject) {
- Blockly.longStop_();
- Blockly.longPid_ = setTimeout(function() {
- e.button = 2; // Simulate a right button click.
- uiObject.onMouseDown_(e);
- }, Blockly.LONGPRESS);
- };
- /**
- * Nope, that's not a long-press. Either touchend or touchcancel was fired,
- * or a drag hath begun. Kill the queued long-press task.
- * @private
- */
- Blockly.longStop_ = function() {
- if (Blockly.longPid_) {
- clearTimeout(Blockly.longPid_);
- Blockly.longPid_ = 0;
- }
- };
- /**
- * Handle a mouse-up anywhere on the page.
- * @param {!Event} e Mouse up event.
- * @private
- */
- Blockly.onMouseUp_ = function(e) {
- var workspace = Blockly.getMainWorkspace();
- if (workspace.dragMode_ == Blockly.DRAG_NONE) {
- return;
- }
- Blockly.Touch.clearTouchIdentifier();
- Blockly.Css.setCursor(Blockly.Css.Cursor.OPEN);
- workspace.dragMode_ = Blockly.DRAG_NONE;
- // Unbind the touch event if it exists.
- if (Blockly.Touch.onTouchUpWrapper_) {
- Blockly.unbindEvent_(Blockly.Touch.onTouchUpWrapper_);
- Blockly.Touch.onTouchUpWrapper_ = null;
- }
- if (Blockly.onMouseMoveWrapper_) {
- Blockly.unbindEvent_(Blockly.onMouseMoveWrapper_);
- Blockly.onMouseMoveWrapper_ = null;
- }
- };
- /**
- * Handle a mouse-move on SVG drawing surface.
- * @param {!Event} e Mouse move event.
- * @private
- */
- Blockly.onMouseMove_ = function(e) {
- var workspace = Blockly.getMainWorkspace();
- if (workspace.dragMode_ != Blockly.DRAG_NONE) {
- var dx = e.clientX - workspace.startDragMouseX;
- var dy = e.clientY - workspace.startDragMouseY;
- var metrics = workspace.startDragMetrics;
- var x = workspace.startScrollX + dx;
- var y = workspace.startScrollY + dy;
- x = Math.min(x, -metrics.contentLeft);
- y = Math.min(y, -metrics.contentTop);
- x = Math.max(x, metrics.viewWidth - metrics.contentLeft -
- metrics.contentWidth);
- y = Math.max(y, metrics.viewHeight - metrics.contentTop -
- metrics.contentHeight);
- // Move the scrollbars and the page will scroll automatically.
- workspace.scrollbar.set(-x - metrics.contentLeft,
- -y - metrics.contentTop);
- // Cancel the long-press if the drag has moved too far.
- if (Math.sqrt(dx * dx + dy * dy) > Blockly.DRAG_RADIUS) {
- Blockly.longStop_();
- workspace.dragMode_ = Blockly.DRAG_FREE;
- }
- e.stopPropagation();
- e.preventDefault();
- }
- };
- /**
- * Clear the touch identifier that tracks which touch stream to pay attention
- * to. This ends the current drag/gesture and allows other pointers to be
- * captured.
- */
- Blockly.Touch.clearTouchIdentifier = function() {
- Blockly.Touch.touchIdentifier_ = null;
- };
- /**
- * Decide whether Blockly should handle or ignore this event.
- * Mouse and touch events require special checks because we only want to deal
- * with one touch stream at a time. All other events should always be handled.
- * @param {!Event} e The event to check.
- * @return {boolean} True if this event should be passed through to the
- * registered handler; false if it should be blocked.
- */
- Blockly.Touch.shouldHandleEvent = function(e) {
- return !Blockly.Touch.isMouseOrTouchEvent(e) ||
- Blockly.Touch.checkTouchIdentifier(e);
- };
- /**
- * Check whether the touch identifier on the event matches the current saved
- * identifier. If there is no identifier, that means it's a mouse event and
- * we'll use the identifier "mouse". This means we won't deal well with
- * multiple mice being used at the same time. That seems okay.
- * If the current identifier was unset, save the identifier from the
- * event. This starts a drag/gesture, during which touch events with other
- * identifiers will be silently ignored.
- * @param {!Event} e Mouse event or touch event.
- * @return {boolean} Whether the identifier on the event matches the current
- * saved identifier.
- */
- Blockly.Touch.checkTouchIdentifier = function(e) {
- var identifier = (e.changedTouches && e.changedTouches[0] &&
- e.changedTouches[0].identifier != undefined &&
- e.changedTouches[0].identifier != null) ?
- e.changedTouches[0].identifier : 'mouse';
- // if (Blockly.touchIdentifier_ )is insufficient because android touch
- // identifiers may be zero.
- if (Blockly.Touch.touchIdentifier_ != undefined &&
- Blockly.Touch.touchIdentifier_ != null) {
- // We're already tracking some touch/mouse event. Is this from the same
- // source?
- return Blockly.Touch.touchIdentifier_ == identifier;
- }
- if (e.type == 'mousedown' || e.type == 'touchstart') {
- // No identifier set yet, and this is the start of a drag. Set it and
- // return.
- Blockly.Touch.touchIdentifier_ = identifier;
- return true;
- }
- // There was no identifier yet, but this wasn't a start event so we're going
- // to ignore it. This probably means that another drag finished while this
- // pointer was down.
- return false;
- };
- /**
- * Set an event's clientX and clientY from its first changed touch. Use this to
- * make a touch event work in a mouse event handler.
- * @param {!Event} e A touch event.
- */
- Blockly.Touch.setClientFromTouch = function(e) {
- if (goog.string.startsWith(e.type, 'touch')) {
- // Map the touch event's properties to the event.
- var touchPoint = e.changedTouches[0];
- e.clientX = touchPoint.clientX;
- e.clientY = touchPoint.clientY;
- }
- };
- /**
- * Check whether a given event is a mouse or touch event.
- * @param {!Event} e An event.
- * @return {boolean} true if it is a mouse or touch event; false otherwise.
- */
- Blockly.Touch.isMouseOrTouchEvent = function(e) {
- return goog.string.startsWith(e.type, 'touch') ||
- goog.string.startsWith(e.type, 'mouse');
- };
- /**
- * Split an event into an array of events, one per changed touch or mouse
- * point.
- * @param {!Event} e A mouse event or a touch event with one or more changed
- * touches.
- * @return {!Array.<!Event>} An array of mouse or touch events. Each touch
- * event will have exactly one changed touch.
- */
- Blockly.Touch.splitEventByTouches = function(e) {
- var events = [];
- if (e.changedTouches) {
- for (var i = 0; i < e.changedTouches.length; i++) {
- var newEvent = {
- type: e.type,
- changedTouches: [e.changedTouches[i]],
- target: e.target,
- stopPropagation: function(){ e.stopPropagation(); },
- preventDefault: function(){ e.preventDefault(); }
- };
- events[i] = newEvent;
- }
- } else {
- events.push(e);
- }
- return events;
- };
|