Disable view source for Developer Tools.
[chromium-blink-merge.git] / chrome / browser / resources / print_preview / previewarea / margin_control.js
blob3f3c78d5c63a540f0855576395b1549e3ec7bc31
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.
5 cr.define('print_preview', function() {
6   'use strict';
8   /**
9    * Draggable control for setting a page margin.
10    * @param {!print_preview.ticket_items.CustomMargins.Orientation} orientation
11    *     Orientation of the margin control that determines where the margin
12    *     textbox will be placed.
13    * @constructor
14    * @extends {print_preview.Component}
15    */
16   function MarginControl(orientation) {
17     print_preview.Component.call(this);
19     /**
20      * Determines where the margin textbox will be placed.
21      * @type {!print_preview.ticket_items.CustomMargins.Orientation}
22      * @private
23      */
24     this.orientation_ = orientation;
26     /**
27      * Position of the margin control in points.
28      * @type {number}
29      * @private
30      */
31     this.positionInPts_ = 0;
33     /**
34      * Page size of the document to print.
35      * @type {!print_preview.Size}
36      * @private
37      */
38     this.pageSize_ = new print_preview.Size(0, 0);
40     /**
41      * Amount to scale pixel values by to convert to pixel space.
42      * @type {number}
43      * @private
44      */
45     this.scaleTransform_ = 1;
47     /**
48      * Amount to translate values in pixel space.
49      * @type {!print_preview.Coordinate2d}
50      * @private
51      */
52     this.translateTransform_ = new print_preview.Coordinate2d(0, 0);
54     /**
55      * Position of the margin control when dragging starts.
56      * @type {print_preview.Coordinate2d}
57      * @private
58      */
59     this.marginStartPositionInPixels_ = null;
61     /**
62      * Position of the mouse when the dragging starts.
63      * @type {print_preview.Coordinate2d}
64      * @private
65      */
66     this.mouseStartPositionInPixels_ = null;
68     /**
69      * Processing timeout for the textbox.
70      * @type {?number}
71      * @private
72      */
73     this.textTimeout_ = null;
75     /**
76      * Value of the textbox when the timeout was started.
77      * @type {?string}
78      * @private
79      */
80     this.preTimeoutValue_ = null;
82     /**
83      * Textbox used to display and receive the value of the margin.
84      * @type {HTMLInputElement}
85      * @private
86      */
87     this.textbox_ = null;
89     /**
90      * Element of the margin control line.
91      * @type {HTMLElement}
92      * @private
93      */
94     this.marginLineEl_ = null;
96     /**
97      * Whether this margin control's textbox has keyboard focus.
98      * @type {boolean}
99      * @private
100      */
101     this.isFocused_ = false;
103     /**
104      * Whether the margin control is in an error state.
105      * @type {boolean}
106      * @private
107      */
108     this.isInError_ = false;
109   };
111   /**
112    * Event types dispatched by the margin control.
113    * @enum {string}
114    */
115   MarginControl.EventType = {
116     // Dispatched when the margin control starts dragging.
117     DRAG_START: 'print_preview.MarginControl.DRAG_START',
119     // Dispatched when the text in the margin control's textbox changes.
120     TEXT_CHANGE: 'print_preview.MarginControl.TEXT_CHANGE'
121   };
123   /**
124    * CSS classes used by this component.
125    * @enum {string}
126    * @private
127    */
128   MarginControl.Classes_ = {
129     TOP: 'margin-control-top',
130     RIGHT: 'margin-control-right',
131     BOTTOM: 'margin-control-bottom',
132     LEFT: 'margin-control-left',
133     TEXTBOX: 'margin-control-textbox',
134     INVALID: 'invalid',
135     INVISIBLE: 'invisible',
136     DISABLED: 'margin-control-disabled',
137     DRAGGING: 'margin-control-dragging',
138     LINE: 'margin-control-line'
139   };
141   /**
142    * Map from orientation to CSS class name.
143    * @type {!Object.<
144    *     !print_preview.ticket_items.CustomMargins.Orientation,
145    *     !MarginControl.Classes_>}
146    * @private
147    */
148   MarginControl.OrientationToClass_ = {};
149   MarginControl.OrientationToClass_[
150       print_preview.ticket_items.CustomMargins.Orientation.TOP] =
151       MarginControl.Classes_.TOP;
152   MarginControl.OrientationToClass_[
153       print_preview.ticket_items.CustomMargins.Orientation.RIGHT] =
154       MarginControl.Classes_.RIGHT;
155   MarginControl.OrientationToClass_[
156       print_preview.ticket_items.CustomMargins.Orientation.BOTTOM] =
157       MarginControl.Classes_.BOTTOM;
158   MarginControl.OrientationToClass_[
159       print_preview.ticket_items.CustomMargins.Orientation.LEFT] =
160       MarginControl.Classes_.LEFT;
162   /**
163    * Radius of the margin control in pixels. Padding of control + 1 for border.
164    * @type {number}
165    * @const
166    * @private
167    */
168   MarginControl.RADIUS_ = 9;
170   /**
171    * Timeout after a text change after which the text in the textbox is saved to
172    * the print ticket. Value in milliseconds.
173    * @type {number}
174    * @const
175    * @private
176    */
177   MarginControl.TEXTBOX_TIMEOUT_ = 1000;
179   MarginControl.prototype = {
180     __proto__: print_preview.Component.prototype,
182     /** @return {boolean} Whether this margin control is in focus. */
183     getIsFocused: function() {
184       return this.isFocused_;
185     },
187     /**
188      * @return {!print_preview.ticket_items.CustomMargins.Orientation}
189      *     Orientation of the margin control.
190      */
191     getOrientation: function() {
192       return this.orientation_;
193     },
195     /**
196      * @param {number} scaleTransform New scale transform of the margin control.
197      */
198     setScaleTransform: function(scaleTransform) {
199       this.scaleTransform_ = scaleTransform;
200       // Reset position
201       this.setPositionInPts(this.positionInPts_);
202     },
204     /**
205      * @param {!print_preview.Coordinate2d} translateTransform New translate
206      *     transform of the margin control.
207      */
208     setTranslateTransform: function(translateTransform) {
209       this.translateTransform_ = translateTransform;
210       // Reset position
211       this.setPositionInPts(this.positionInPts_);
212     },
214     /**
215      * @param {!print_preview.Size} pageSize New size of the document's pages.
216      */
217     setPageSize: function(pageSize) {
218       this.pageSize_ = pageSize;
219       this.setPositionInPts(this.positionInPts_);
220     },
222     /** @param {boolean} isVisible Whether the margin control is visible. */
223     setIsVisible: function(isVisible) {
224       if (isVisible) {
225         this.getElement().classList.remove(MarginControl.Classes_.INVISIBLE);
226       } else {
227         this.getElement().classList.add(MarginControl.Classes_.INVISIBLE);
228       }
229     },
231     /** @return {boolean} Whether the margin control is in an error state. */
232     getIsInError: function() {
233       return this.isInError_;
234     },
236     /**
237      * @param {boolean} isInError Whether the margin control is in an error
238      *     state.
239      */
240     setIsInError: function(isInError) {
241       this.isInError_ = isInError;
242       if (isInError) {
243         this.textbox_.classList.add(MarginControl.Classes_.INVALID);
244       } else {
245         this.textbox_.classList.remove(MarginControl.Classes_.INVALID);
246       }
247     },
249     /** @param {boolean} isEnabled Whether to enable the margin control. */
250     setIsEnabled: function(isEnabled) {
251       this.textbox_.disabled = !isEnabled;
252       if (isEnabled) {
253         this.getElement().classList.remove(MarginControl.Classes_.DISABLED);
254       } else {
255         this.getElement().classList.add(MarginControl.Classes_.DISABLED);
256       }
257     },
259     /** @return {number} Current position of the margin control in points. */
260     getPositionInPts: function() {
261       return this.positionInPts_;
262     },
264     /**
265      * @param {number} posInPts New position of the margin control in points.
266      */
267     setPositionInPts: function(posInPts) {
268       this.positionInPts_ = posInPts;
269       var orientationEnum =
270           print_preview.ticket_items.CustomMargins.Orientation;
271       var x = this.translateTransform_.x;
272       var y = this.translateTransform_.y;
273       var width = null, height = null;
274       if (this.orientation_ == orientationEnum.TOP) {
275         y = this.scaleTransform_ * posInPts + this.translateTransform_.y -
276             MarginControl.RADIUS_;
277         width = this.scaleTransform_ * this.pageSize_.width;
278       } else if (this.orientation_ == orientationEnum.RIGHT) {
279         x = this.scaleTransform_ * (this.pageSize_.width - posInPts) +
280             this.translateTransform_.x - MarginControl.RADIUS_;
281         height = this.scaleTransform_ * this.pageSize_.height;
282       } else if (this.orientation_ == orientationEnum.BOTTOM) {
283         y = this.scaleTransform_ * (this.pageSize_.height - posInPts) +
284             this.translateTransform_.y - MarginControl.RADIUS_;
285         width = this.scaleTransform_ * this.pageSize_.width;
286       } else {
287         x = this.scaleTransform_ * posInPts + this.translateTransform_.x -
288             MarginControl.RADIUS_;
289         height = this.scaleTransform_ * this.pageSize_.height;
290       }
291       this.getElement().style.left = Math.round(x) + 'px';
292       this.getElement().style.top = Math.round(y) + 'px';
293       if (width != null) {
294         this.getElement().style.width = Math.round(width) + 'px';
295       }
296       if (height != null) {
297         this.getElement().style.height = Math.round(height) + 'px';
298       }
299     },
301     /** @return {string} The value in the margin control's textbox. */
302     getTextboxValue: function() {
303       return this.textbox_.value;
304     },
306     /** @param {string} value New value of the margin control's textbox. */
307     setTextboxValue: function(value) {
308       if (this.textbox_.value != value) {
309         this.textbox_.value = value;
310       }
311     },
313     /**
314      * Converts a value in pixels to points.
315      * @param {number} Pixel value to convert.
316      * @return {number} Given value expressed in points.
317      */
318     convertPixelsToPts: function(pixels) {
319       var pts;
320       var orientationEnum =
321           print_preview.ticket_items.CustomMargins.Orientation;
322       if (this.orientation_ == orientationEnum.TOP) {
323         pts = pixels - this.translateTransform_.y + MarginControl.RADIUS_;
324         pts /= this.scaleTransform_;
325       } else if (this.orientation_ == orientationEnum.RIGHT) {
326         pts = pixels - this.translateTransform_.x + MarginControl.RADIUS_;
327         pts /= this.scaleTransform_;
328         pts = this.pageSize_.width - pts;
329       } else if (this.orientation_ == orientationEnum.BOTTOM) {
330         pts = pixels - this.translateTransform_.y + MarginControl.RADIUS_;
331         pts /= this.scaleTransform_;
332         pts = this.pageSize_.height - pts;
333       } else {
334         pts = pixels - this.translateTransform_.x + MarginControl.RADIUS_;
335         pts /= this.scaleTransform_;
336       }
337       return pts;
338     },
340     /**
341      * Translates the position of the margin control relative to the mouse
342      * position in pixels.
343      * @param {!print_preview.Coordinate2d} mousePosition New position of
344      *     the mouse.
345      * @return {!print_preview.Coordinate2d} New position of the margin control.
346      */
347     translateMouseToPositionInPixels: function(mousePosition) {
348       return new print_preview.Coordinate2d(
349           mousePosition.x - this.mouseStartPositionInPixels_.x +
350               this.marginStartPositionInPixels_.x,
351           mousePosition.y - this.mouseStartPositionInPixels_.y +
352               this.marginStartPositionInPixels_.y);
353     },
355     /** @override */
356     createDom: function() {
357       this.setElementInternal(this.cloneTemplateInternal(
358           'margin-control-template'));
359       this.getElement().classList.add(MarginControl.OrientationToClass_[
360           this.orientation_]);
361       this.textbox_ = this.getElement().getElementsByClassName(
362           MarginControl.Classes_.TEXTBOX)[0];
363       this.marginLineEl_ = this.getElement().getElementsByClassName(
364           MarginControl.Classes_.LINE)[0];
365     },
367     /** @override */
368     enterDocument: function() {
369       print_preview.Component.prototype.enterDocument.call(this);
370       this.tracker.add(
371           this.getElement(), 'mousedown', this.onMouseDown_.bind(this));
372       this.tracker.add(
373           this.textbox_, 'keydown', this.onTextboxKeyDown_.bind(this));
374       this.tracker.add(
375           this.textbox_, 'focus', this.setIsFocused_.bind(this, true));
376       this.tracker.add(this.textbox_, 'blur', this.onTexboxBlur_.bind(this));
377     },
379     /** @override */
380     exitDocument: function() {
381       print_preview.Component.prototype.exitDocument.call(this);
382       this.textbox_ = null;
383       this.marginLineEl_ = null;
384     },
386     /**
387      * @param {boolean} isFocused Whether the margin control is in focus.
388      * @private
389      */
390     setIsFocused_: function(isFocused) {
391       this.isFocused_ = isFocused;
392     },
394     /**
395      * Called whenever a mousedown event occurs on the component.
396      * @param {MouseEvent} event The event that occured.
397      * @private
398      */
399     onMouseDown_: function(event) {
400       if (!this.textbox_.disabled &&
401           event.button == 0 &&
402           (event.target == this.getElement() ||
403               event.target == this.marginLineEl_)) {
404         this.mouseStartPositionInPixels_ =
405             new print_preview.Coordinate2d(event.x, event.y);
406         this.marginStartPositionInPixels_ = new print_preview.Coordinate2d(
407             this.getElement().offsetLeft, this.getElement().offsetTop);
408         this.setIsInError(false);
409         cr.dispatchSimpleEvent(this, MarginControl.EventType.DRAG_START);
410       }
411     },
413     /**
414      * Called when a key down event occurs on the textbox. Dispatches a
415      * TEXT_CHANGE event if the "Enter" key was pressed.
416      * @param {Event} event Contains the key that was pressed.
417      * @private
418      */
419     onTextboxKeyDown_: function(event) {
420       if (this.textTimeout_) {
421         clearTimeout(this.textTimeout_);
422         this.textTimeout_ = null;
423       }
424       if (event.keyIdentifier == 'Enter') {
425         this.preTimeoutValue_ = null;
426         cr.dispatchSimpleEvent(this, MarginControl.EventType.TEXT_CHANGE);
427       } else {
428         if (this.preTimeoutValue_ == null) {
429           this.preTimeoutValue_ = this.textbox_.value;
430         }
431         this.textTimeout_ = setTimeout(
432             this.onTextboxTimeout_.bind(this), MarginControl.TEXTBOX_TIMEOUT_);
433       }
434     },
436     /**
437      * Called after a timeout after the text in the textbox has changed. Saves
438      * the textbox's value to the print ticket.
439      * @private
440      */
441     onTextboxTimeout_: function() {
442       this.textTimeout_ = null;
443       if (this.textbox_.value != this.preTimeoutValue_) {
444         cr.dispatchSimpleEvent(this, MarginControl.EventType.TEXT_CHANGE);
445       }
446       this.preTimeoutValue_ = null;
447     },
449     /**
450      * Called when the textbox loses focus. Dispatches a TEXT_CHANGE event.
451      */
452     onTexboxBlur_: function() {
453       if (this.textTimeout_) {
454         clearTimeout(this.textTimeout_);
455         this.textTimeout_ = null;
456         this.preTimeoutValue_ = null;
457       }
458       this.setIsFocused_(false);
459       cr.dispatchSimpleEvent(this, MarginControl.EventType.TEXT_CHANGE);
460     }
461   };
463   // Export
464   return {
465     MarginControl: MarginControl
466   };