Supervised user whitelists: Cleanup
[chromium-blink-merge.git] / ui / file_manager / gallery / js / image_editor / image_transform.js
blob6c557828d6bbac0aa0cdcc950da0521689e0f16b
1 // Copyright 2014 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.
5 /**
6  * Crop mode.
7  *
8  * @extends {ImageEditor.Mode}
9  * @constructor
10  * @struct
11  */
12 ImageEditor.Mode.Crop = function() {
13   ImageEditor.Mode.call(this, 'crop', 'GALLERY_CROP');
15   /**
16    * @type {HTMLDivElement}
17    * @private
18    */
19   this.domOverlay_ = null;
21   /**
22    * @type {HTMLDivElement}
23    * @private
24    */
25   this.shadowTop_ = null;
27   /**
28    * @type {HTMLDivElement}
29    * @private
30    */
31   this.middleBox_ = null;
33   /**
34    * @type {HTMLDivElement}
35    * @private
36    */
37   this.shadowLeft_ = null;
39   /**
40    * @type {HTMLDivElement}
41    * @private
42    */
43   this.cropFrame_ = null;
45   /**
46    * @type {HTMLDivElement}
47    * @private
48    */
49   this.shadowRight_ = null;
51   /**
52    * @type {HTMLDivElement}
53    * @private
54    */
55   this.shadowBottom_ = null;
57   /**
58    * @type {ImageEditor.Toolbar}
59    * @private
60    */
61   this.toolbar_ = null;
63   /**
64    * @type {?function()}
65    * @private
66    */
67   this.onResizedBound_ = null;
69   /**
70    * @type {DraggableRect}
71    * @private
72    */
73   this.cropRect_ = null;
76 ImageEditor.Mode.Crop.prototype = {__proto__: ImageEditor.Mode.prototype};
78 /**
79  * Sets the mode up.
80  * @override
81  */
82 ImageEditor.Mode.Crop.prototype.setUp = function() {
83   ImageEditor.Mode.prototype.setUp.apply(this, arguments);
85   var container = this.getImageView().container_;
86   var doc = container.ownerDocument;
88   this.domOverlay_ = doc.createElement('div');
89   this.domOverlay_.className = 'crop-overlay';
90   container.appendChild(this.domOverlay_);
92   this.shadowTop_ = doc.createElement('div');
93   this.shadowTop_.className = 'shadow';
94   this.domOverlay_.appendChild(this.shadowTop_);
96   this.middleBox_ = doc.createElement('div');
97   this.middleBox_.className = 'middle-box';
98   this.domOverlay_.appendChild(this.middleBox_);
100   this.shadowLeft_ = doc.createElement('div');
101   this.shadowLeft_.className = 'shadow';
102   this.middleBox_.appendChild(this.shadowLeft_);
104   this.cropFrame_ = doc.createElement('div');
105   this.cropFrame_.className = 'crop-frame';
106   this.middleBox_.appendChild(this.cropFrame_);
108   this.shadowRight_ = doc.createElement('div');
109   this.shadowRight_.className = 'shadow';
110   this.middleBox_.appendChild(this.shadowRight_);
112   this.shadowBottom_ = doc.createElement('div');
113   this.shadowBottom_.className = 'shadow';
114   this.domOverlay_.appendChild(this.shadowBottom_);
116   var cropFrame = this.cropFrame_;
117   function addCropFrame(className) {
118     var div = doc.createElement('div');
119     div.className = className;
120     cropFrame.appendChild(div);
121   }
123   addCropFrame('left top corner');
124   addCropFrame('top horizontal');
125   addCropFrame('right top corner');
126   addCropFrame('left vertical');
127   addCropFrame('right vertical');
128   addCropFrame('left bottom corner');
129   addCropFrame('bottom horizontal');
130   addCropFrame('right bottom corner');
132   this.onResizedBound_ = this.onResized_.bind(this);
133   window.addEventListener('resize', this.onResizedBound_);
135   this.createDefaultCrop();
139  * @override
140  */
141 ImageEditor.Mode.Crop.prototype.createTools = function(toolbar) {
142   var aspects = {
143     GALLERY_ASPECT_RATIO_1_1: 1 / 1,
144     GALLERY_ASPECT_RATIO_6_4: 6 / 4,
145     GALLERY_ASPECT_RATIO_7_5: 7 / 5,
146     GALLERY_ASPECT_RATIO_16_9: 16 / 9
147   };
148   for (var name in aspects) {
149     var button = toolbar.addButton(
150         name,
151         name,
152         function(aspect, event) {
153           var button = event.target;
154           if (button.classList.contains('selected')) {
155             button.classList.remove('selected');
156             this.cropRect_.fixedAspectRatio = null;
157           } else {
158             var selectedButtons =
159                 toolbar.getElement().querySelectorAll('button.selected');
160             for (var i = 0; i < selectedButtons.length; i++) {
161               selectedButtons[i].classList.remove('selected');
162             }
163             button.classList.add('selected');
164             var clipRect = this.viewport_.screenToImageRect(
165                 this.viewport_.getImageBoundsOnScreenClipped());
166             this.cropRect_.fixedAspectRatio = aspect;
167             this.cropRect_.forceAspectRatio(aspect, clipRect);
168             this.markUpdated();
169             this.positionDOM();
170             this.toolbar_.getElement().classList.remove('dimmable');
171             this.toolbar_.getElement().removeAttribute('dimmed');
172           }
173         }.bind(this, aspects[name]));
174     // Prevent from cropping by Enter key if the button is focused.
175     button.addEventListener('keydown', function(event) {
176       var key = util.getKeyModifiers(event) + event.keyIdentifier;
177       if (key === 'Enter')
178         event.stopPropagation();
179     });
180   }
181   this.toolbar_ = toolbar;
185  * Handles resizing of the window and updates the crop rectangle.
186  * @private
187  */
188 ImageEditor.Mode.Crop.prototype.onResized_ = function() {
189   this.positionDOM();
193  * Resets the mode.
194  */
195 ImageEditor.Mode.Crop.prototype.reset = function() {
196   ImageEditor.Mode.prototype.reset.call(this);
197   this.createDefaultCrop();
198   if (this.toolbar_) {
199     this.toolbar_.getElement().classList.add('dimmable');
200     this.toolbar_ = null;
201   }
205  * Updates the position of DOM elements.
206  */
207 ImageEditor.Mode.Crop.prototype.positionDOM = function() {
208   var screenClipped = this.viewport_.getImageBoundsOnScreenClipped();
210   var screenCrop = this.viewport_.imageToScreenRect(this.cropRect_.getRect());
211   var delta = ImageEditor.Mode.Crop.MOUSE_GRAB_RADIUS;
212   this.editor_.hideOverlappingTools(
213       screenCrop.inflate(delta, delta),
214       screenCrop.inflate(-delta, -delta));
216   this.domOverlay_.style.left = screenClipped.left + 'px';
217   this.domOverlay_.style.top = screenClipped.top + 'px';
218   this.domOverlay_.style.width = screenClipped.width + 'px';
219   this.domOverlay_.style.height = screenClipped.height + 'px';
221   this.shadowLeft_.style.width = screenCrop.left - screenClipped.left + 'px';
223   this.shadowTop_.style.height = screenCrop.top - screenClipped.top + 'px';
225   this.shadowRight_.style.width = screenClipped.left + screenClipped.width -
226       (screenCrop.left + screenCrop.width) + 'px';
228   this.shadowBottom_.style.height = screenClipped.top + screenClipped.height -
229       (screenCrop.top + screenCrop.height) + 'px';
233  * Removes the overlay elements from the document.
234  */
235 ImageEditor.Mode.Crop.prototype.cleanUpUI = function() {
236   ImageEditor.Mode.prototype.cleanUpUI.apply(this, arguments);
237   this.domOverlay_.parentNode.removeChild(this.domOverlay_);
238   this.domOverlay_ = null;
239   this.editor_.hideOverlappingTools();
240   window.removeEventListener('resize', this.onResizedBound_);
241   this.onResizedBound_ = null;
245  * @const
246  * @type {number}
247  */
248 ImageEditor.Mode.Crop.MOUSE_GRAB_RADIUS = 6;
251  * @const
252  * @type {number}
253  */
254 ImageEditor.Mode.Crop.TOUCH_GRAB_RADIUS = 20;
257  * Gets command to do the crop depending on the current state.
259  * @return {!Command.Crop} Crop command.
260  */
261 ImageEditor.Mode.Crop.prototype.getCommand = function() {
262   var cropImageRect = this.cropRect_.getRect();
263   return new Command.Crop(cropImageRect);
267  * Creates default (initial) crop.
268  */
269 ImageEditor.Mode.Crop.prototype.createDefaultCrop = function() {
270   var viewport = this.getViewport();
271   assert(viewport);
273   var rect = viewport.screenToImageRect(
274       viewport.getImageBoundsOnScreenClipped());
275   rect = rect.inflate(
276       -Math.round(rect.width / 6), -Math.round(rect.height / 6));
278   this.cropRect_ = new DraggableRect(rect, viewport);
280   this.positionDOM();
284  * Obtains the cursor style depending on the mouse state.
286  * @param {number} x X coordinate for cursor.
287  * @param {number} y Y coordinate for cursor.
288  * @param {boolean} mouseDown If mouse button is down.
289  * @return {string} A value for style.cursor CSS property.
290  */
291 ImageEditor.Mode.Crop.prototype.getCursorStyle = function(x, y, mouseDown) {
292   return this.cropRect_.getCursorStyle(x, y, mouseDown);
296  * Obtains handler function depending on the mouse state.
298  * @param {number} x Event X coordinate.
299  * @param {number} y Event Y coordinate.
300  * @param {boolean} touch True if it's a touch event, false if mouse.
301  * @return {?function(number,number,boolean)} A function to be called on mouse
302  *     drag. It takes x coordinate value, y coordinate value, and shift key
303  *     flag.
304  */
305 ImageEditor.Mode.Crop.prototype.getDragHandler = function(x, y, touch) {
306   var cropDragHandler = this.cropRect_.getDragHandler(x, y, touch);
307   if (!cropDragHandler)
308     return null;
310   return function(x, y, shiftKey) {
311     if (this.toolbar_)
312       this.toolbar_.getElement().classList.add('dimmable');
313     cropDragHandler(x, y, shiftKey);
314     this.markUpdated();
315     this.positionDOM();
316   }.bind(this);
320  * Obtains the double tap action depending on the coordinate.
322  * @param {number} x X coordinate of the event.
323  * @param {number} y Y coordinate of the event.
324  * @return {!ImageBuffer.DoubleTapAction} Action to perform as result.
325  */
326 ImageEditor.Mode.Crop.prototype.getDoubleTapAction = function(x, y) {
327   return this.cropRect_.getDoubleTapAction(x, y);
331  * A draggable rectangle over the image.
333  * @param {!ImageRect} rect Initial size of the image.
334  * @param {!Viewport} viewport Viewport.
335  * @constructor
336  * @struct
337  */
338 function DraggableRect(rect, viewport) {
339   /**
340    * The bounds are not held in a regular rectangle (with width/height).
341    * left/top/right/bottom held instead for convenience.
342    *
343    * @type {{left: number, right: number, top: number, bottom: number}}
344    * @private
345    */
346   this.bounds_ = {
347     left: rect.left,
348     right: rect.left + rect.width,
349     top: rect.top,
350     bottom: rect.top + rect.height
351   };
353   /**
354    * Viewport.
355    *
356    * @type {!Viewport}
357    * @private
358    * @const
359    */
360   this.viewport_ = viewport;
362   /**
363    * Drag mode.
364    *
365    * @type {Object}
366    * @private
367    */
368   this.dragMode_ = null;
370   /**
371    * Fixed aspect ratio.
372    * The aspect ratio is not fixed when null.
373    * @type {?number}
374    */
375   this.fixedAspectRatio = null;
378 // Static members to simplify reflective access to the bounds.
380  * @const
381  * @type {string}
382  */
383 DraggableRect.LEFT = 'left';
386  * @const
387  * @type {string}
388  */
389 DraggableRect.RIGHT = 'right';
392  * @const
393  * @type {string}
394  */
395 DraggableRect.TOP = 'top';
398  * @const
399  * @type {string}
400  */
401 DraggableRect.BOTTOM = 'bottom';
404  * @const
405  * @type {string}
406  */
407 DraggableRect.NONE = 'none';
410  * Obtains the left position.
411  * @return {number} Position.
412  */
413 DraggableRect.prototype.getLeft = function() {
414   return this.bounds_[DraggableRect.LEFT];
418  * Obtains the right position.
419  * @return {number} Position.
420  */
421 DraggableRect.prototype.getRight = function() {
422   return this.bounds_[DraggableRect.RIGHT];
426  * Obtains the top position.
427  * @return {number} Position.
428  */
429 DraggableRect.prototype.getTop = function() {
430   return this.bounds_[DraggableRect.TOP];
434  * Obtains the bottom position.
435  * @return {number} Position.
436  */
437 DraggableRect.prototype.getBottom = function() {
438   return this.bounds_[DraggableRect.BOTTOM];
442  * Obtains the geometry of the rectangle.
443  * @return {!ImageRect} Geometry of the rectangle.
444  */
445 DraggableRect.prototype.getRect = function() {
446   return ImageRect.createFromBounds(this.bounds_);
450  * Obtains the drag mode depending on the coordinate.
452  * @param {number} x X coordinate for cursor.
453  * @param {number} y Y coordinate for cursor.
454  * @param {boolean=} opt_touch  Whether the operation is done by touch or not.
455  * @return {{xSide: string, ySide:string, whole:boolean, newCrop:boolean}}
456  *     Drag mode.
457  */
458 DraggableRect.prototype.getDragMode = function(x, y, opt_touch) {
459   var touch = opt_touch || false;
461   var result = {
462     xSide: DraggableRect.NONE,
463     ySide: DraggableRect.NONE,
464     whole: false,
465     newCrop: false
466   };
468   var bounds = this.bounds_;
469   var R = this.viewport_.screenToImageSize(
470       touch ? ImageEditor.Mode.Crop.TOUCH_GRAB_RADIUS :
471               ImageEditor.Mode.Crop.MOUSE_GRAB_RADIUS);
473   var circle = new Circle(x, y, R);
475   var xBetween = ImageUtil.between(bounds.left, x, bounds.right);
476   var yBetween = ImageUtil.between(bounds.top, y, bounds.bottom);
478   if (circle.inside(bounds.left, bounds.top)) {
479     result.xSide = DraggableRect.LEFT;
480     result.ySide = DraggableRect.TOP;
481   } else if (circle.inside(bounds.left, bounds.bottom)) {
482     result.xSide = DraggableRect.LEFT;
483     result.ySide = DraggableRect.BOTTOM;
484   } else if (circle.inside(bounds.right, bounds.top)) {
485     result.xSide = DraggableRect.RIGHT;
486     result.ySide = DraggableRect.TOP;
487   } else if (circle.inside(bounds.right, bounds.bottom)) {
488     result.xSide = DraggableRect.RIGHT;
489     result.ySide = DraggableRect.BOTTOM;
490   } else if (yBetween && Math.abs(x - bounds.left) <= R) {
491     result.xSide = DraggableRect.LEFT;
492   } else if (yBetween && Math.abs(x - bounds.right) <= R) {
493     result.xSide = DraggableRect.RIGHT;
494   } else if (xBetween && Math.abs(y - bounds.top) <= R) {
495     result.ySide = DraggableRect.TOP;
496   } else if (xBetween && Math.abs(y - bounds.bottom) <= R) {
497     result.ySide = DraggableRect.BOTTOM;
498   } else if (xBetween && yBetween) {
499     result.whole = true;
500   } else {
501     result.newcrop = true;
502     result.xSide = DraggableRect.RIGHT;
503     result.ySide = DraggableRect.BOTTOM;
504   }
506   return result;
510  * Obtains the cursor style depending on the coordinate.
512  * @param {number} x X coordinate for cursor.
513  * @param {number} y Y coordinate for cursor.
514  * @param {boolean} mouseDown  If mouse button is down.
515  * @return {string} Cursor style.
516  */
517 DraggableRect.prototype.getCursorStyle = function(x, y, mouseDown) {
518   var mode;
519   if (mouseDown) {
520     mode = this.dragMode_;
521   } else {
522     mode = this.getDragMode(
523         this.viewport_.screenToImageX(x), this.viewport_.screenToImageY(y));
524   }
525   if (mode.whole)
526     return 'move';
527   if (mode.newcrop)
528     return 'crop';
530   var xSymbol = '';
531   switch (mode.xSide) {
532     case 'left': xSymbol = 'w'; break;
533     case 'right': xSymbol = 'e'; break;
534   }
535   var ySymbol = '';
536   switch (mode.ySide) {
537     case 'top': ySymbol = 'n'; break;
538     case 'bottom': ySymbol = 's'; break;
539   }
540   return ySymbol + xSymbol + '-resize';
544  * Obtains the drag handler depending on the coordinate.
546  * @param {number} initialScreenX X coordinate for cursor in the screen.
547  * @param {number} initialScreenY Y coordinate for cursor in the screen.
548  * @param {boolean} touch Whether the operation is done by touch or not.
549  * @return {?function(number,number,boolean)} Drag handler that takes x
550  *     coordinate value, y coordinate value, and shift key flag.
551  */
552 DraggableRect.prototype.getDragHandler = function(
553     initialScreenX, initialScreenY, touch) {
554   // Check if the initial coordinate in the clip rect.
555   var initialX = this.viewport_.screenToImageX(initialScreenX);
556   var initialY = this.viewport_.screenToImageY(initialScreenY);
557   var initialWidth = this.bounds_.right - this.bounds_.left;
558   var initialHeight = this.bounds_.bottom - this.bounds_.top;
559   var clipRect = this.viewport_.screenToImageRect(
560       this.viewport_.getImageBoundsOnScreenClipped());
561   if (!clipRect.inside(initialX, initialY))
562     return null;
564   // Obtain the drag mode.
565   this.dragMode_ = this.getDragMode(initialX, initialY, touch);
567   if (this.dragMode_.whole) {
568     // Calc constant values during the operation.
569     var mouseBiasX = this.bounds_.left - initialX;
570     var mouseBiasY = this.bounds_.top - initialY;
571     var maxX = clipRect.left + clipRect.width - initialWidth;
572     var maxY = clipRect.top + clipRect.height - initialHeight;
574     // Returns a handler.
575     return function(newScreenX, newScreenY) {
576       var newX = this.viewport_.screenToImageX(newScreenX);
577       var newY = this.viewport_.screenToImageY(newScreenY);
578       var clamppedX = ImageUtil.clamp(clipRect.left, newX + mouseBiasX, maxX);
579       var clamppedY = ImageUtil.clamp(clipRect.top, newY + mouseBiasY, maxY);
580       this.bounds_.left = clamppedX;
581       this.bounds_.right = clamppedX + initialWidth;
582       this.bounds_.top = clamppedY;
583       this.bounds_.bottom = clamppedY + initialHeight;
584     }.bind(this);
585   } else {
586     // Calc constant values during the operation.
587     var mouseBiasX = this.bounds_[this.dragMode_.xSide] - initialX;
588     var mouseBiasY = this.bounds_[this.dragMode_.ySide] - initialY;
589     var maxX = clipRect.left + clipRect.width;
590     var maxY = clipRect.top + clipRect.height;
592     // Returns a handler.
593     return function(newScreenX, newScreenY, shiftKey) {
594       var newX = this.viewport_.screenToImageX(newScreenX);
595       var newY = this.viewport_.screenToImageY(newScreenY);
597       // Check new crop.
598       if (this.dragMode_.newcrop) {
599         this.dragMode_.newcrop = false;
600         this.bounds_.left = this.bounds_.right = initialX;
601         this.bounds_.top = this.bounds_.bottom = initialY;
602         mouseBiasX = 0;
603         mouseBiasY = 0;
604       }
606       // Update X coordinate.
607       if (this.dragMode_.xSide !== DraggableRect.NONE) {
608         this.bounds_[this.dragMode_.xSide] =
609             ImageUtil.clamp(clipRect.left, newX + mouseBiasX, maxX);
610         if (this.bounds_.left > this.bounds_.right) {
611           var left = this.bounds_.left;
612           var right = this.bounds_.right;
613           this.bounds_.left = right - 1;
614           this.bounds_.right = left + 1;
615           this.dragMode_.xSide =
616               this.dragMode_.xSide == 'left' ? 'right' : 'left';
617         }
618       }
620       // Update Y coordinate.
621       if (this.dragMode_.ySide !== DraggableRect.NONE) {
622         this.bounds_[this.dragMode_.ySide] =
623             ImageUtil.clamp(clipRect.top, newY + mouseBiasY, maxY);
624         if (this.bounds_.top > this.bounds_.bottom) {
625           var top = this.bounds_.top;
626           var bottom = this.bounds_.bottom;
627           this.bounds_.top = bottom - 1;
628           this.bounds_.bottom = top + 1;
629           this.dragMode_.ySide =
630               this.dragMode_.ySide === 'top' ? 'bottom' : 'top';
631         }
632       }
634       // Update aspect ratio.
635       if (this.fixedAspectRatio)
636         this.forceAspectRatio(this.fixedAspectRatio, clipRect);
637       else if (shiftKey)
638         this.forceAspectRatio(initialWidth / initialHeight, clipRect);
639     }.bind(this);
640   }
644  * Obtains double tap action depending on the coordinate.
646  * @param {number} x X coordinate for cursor.
647  * @param {number} y Y coordinate for cursor.
648  * @return {!ImageBuffer.DoubleTapAction} Double tap action.
649  */
650 DraggableRect.prototype.getDoubleTapAction = function(x, y) {
651   var clipRect = this.viewport_.getImageBoundsOnScreenClipped();
652   if (clipRect.inside(x, y))
653     return ImageBuffer.DoubleTapAction.COMMIT;
654   else
655     return ImageBuffer.DoubleTapAction.NOTHING;
659  * Forces the aspect ratio.
661  * @param {number} aspectRatio Aspect ratio.
662  * @param {!Object} clipRect Clip rect.
663  */
664 DraggableRect.prototype.forceAspectRatio = function(aspectRatio, clipRect) {
665   // Get current rectangle scale.
666   var width = this.bounds_.right - this.bounds_.left;
667   var height = this.bounds_.bottom - this.bounds_.top;
668   var currentScale;
669   if (!this.dragMode_)
670     currentScale = ((width / aspectRatio) + height) / 2;
671   else if (this.dragMode_.xSide === 'none')
672     currentScale = height;
673   else if (this.dragMode_.ySide === 'none')
674     currentScale = width / aspectRatio;
675   else
676     currentScale = Math.max(width / aspectRatio, height);
678   // Get maximum width/height scale.
679   var maxWidth;
680   var maxHeight;
681   var center = (this.bounds_.left + this.bounds_.right) / 2;
682   var middle = (this.bounds_.top + this.bounds_.bottom) / 2;
683   var xSide = this.dragMode_ ? this.dragMode_.xSide : 'none';
684   var ySide = this.dragMode_ ? this.dragMode_.ySide : 'none';
685   switch (xSide) {
686     case 'left':
687       maxWidth = this.bounds_.right - clipRect.left;
688       break;
689     case 'right':
690       maxWidth = clipRect.left + clipRect.width - this.bounds_.left;
691       break;
692     case 'none':
693       maxWidth = Math.min(
694           clipRect.left + clipRect.width - center,
695           center - clipRect.left) * 2;
696       break;
697   }
698   switch (ySide) {
699     case 'top':
700       maxHeight = this.bounds_.bottom - clipRect.top;
701       break;
702     case 'bottom':
703       maxHeight = clipRect.top + clipRect.height - this.bounds_.top;
704       break;
705     case 'none':
706       maxHeight = Math.min(
707           clipRect.top + clipRect.height - middle,
708           middle - clipRect.top) * 2;
709       break;
710   }
712   // Obtains target scale.
713   var targetScale = Math.min(
714       currentScale,
715       maxWidth / aspectRatio,
716       maxHeight);
718   // Update bounds.
719   var newWidth = targetScale * aspectRatio;
720   var newHeight = targetScale;
721   switch (xSide) {
722     case 'left':
723       this.bounds_.left = this.bounds_.right - newWidth;
724       break;
725     case 'right':
726       this.bounds_.right = this.bounds_.left + newWidth;
727       break;
728     case 'none':
729       this.bounds_.left = center - newWidth / 2;
730       this.bounds_.right = center + newWidth / 2;
731       break;
732   }
733   switch (ySide) {
734     case 'top':
735       this.bounds_.top = this.bounds_.bottom - newHeight;
736       break;
737     case 'bottom':
738       this.bounds_.bottom = this.bounds_.top + newHeight;
739       break;
740     case 'none':
741       this.bounds_.top = middle - newHeight / 2;
742       this.bounds_.bottom = middle + newHeight / 2;
743       break;
744   }