Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / chrome / browser / resources / print_preview / previewarea / margin_control.js
blob7ec2527e7a17e02389387adba3c9c806b7ec8b3d
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      * Textbox used to display and receive the value of the margin.
77      * @type {HTMLInputElement}
78      * @private
79      */
80     this.textbox_ = null;
82     /**
83      * Element of the margin control line.
84      * @type {HTMLElement}
85      * @private
86      */
87     this.marginLineEl_ = null;
89     /**
90      * Whether this margin control's textbox has keyboard focus.
91      * @type {boolean}
92      * @private
93      */
94     this.isFocused_ = false;
96     /**
97      * Whether the margin control is in an error state.
98      * @type {boolean}
99      * @private
100      */
101     this.isInError_ = false;
102   };
104   /**
105    * Event types dispatched by the margin control.
106    * @enum {string}
107    */
108   MarginControl.EventType = {
109     // Dispatched when the margin control starts dragging.
110     DRAG_START: 'print_preview.MarginControl.DRAG_START',
112     // Dispatched when the text in the margin control's textbox changes.
113     TEXT_CHANGE: 'print_preview.MarginControl.TEXT_CHANGE'
114   };
116   /**
117    * CSS classes used by this component.
118    * @enum {string}
119    * @private
120    */
121   MarginControl.Classes_ = {
122     TEXTBOX: 'margin-control-textbox',
123     DRAGGING: 'margin-control-dragging',
124     LINE: 'margin-control-line'
125   };
127   /**
128    * Radius of the margin control in pixels. Padding of control + 1 for border.
129    * @type {number}
130    * @const
131    * @private
132    */
133   MarginControl.RADIUS_ = 9;
135   /**
136    * Timeout after a text change after which the text in the textbox is saved to
137    * the print ticket. Value in milliseconds.
138    * @type {number}
139    * @const
140    * @private
141    */
142   MarginControl.TEXTBOX_TIMEOUT_ = 1000;
144   MarginControl.prototype = {
145     __proto__: print_preview.Component.prototype,
147     /** @return {boolean} Whether this margin control is in focus. */
148     getIsFocused: function() {
149       return this.isFocused_;
150     },
152     /**
153      * @return {!print_preview.ticket_items.CustomMargins.Orientation}
154      *     Orientation of the margin control.
155      */
156     getOrientation: function() {
157       return this.orientation_;
158     },
160     /**
161      * @param {number} scaleTransform New scale transform of the margin control.
162      */
163     setScaleTransform: function(scaleTransform) {
164       this.scaleTransform_ = scaleTransform;
165       // Reset position
166       this.setPositionInPts(this.positionInPts_);
167     },
169     /**
170      * @param {!print_preview.Coordinate2d} translateTransform New translate
171      *     transform of the margin control.
172      */
173     setTranslateTransform: function(translateTransform) {
174       this.translateTransform_ = translateTransform;
175       // Reset position
176       this.setPositionInPts(this.positionInPts_);
177     },
179     /**
180      * @param {!print_preview.Size} pageSize New size of the document's pages.
181      */
182     setPageSize: function(pageSize) {
183       this.pageSize_ = pageSize;
184       this.setPositionInPts(this.positionInPts_);
185     },
187     /** @param {boolean} isVisible Whether the margin control is visible. */
188     setIsVisible: function(isVisible) {
189       this.getElement().classList.toggle('invisible', !isVisible);
190     },
192     /** @return {boolean} Whether the margin control is in an error state. */
193     getIsInError: function() {
194       return this.isInError_;
195     },
197     /**
198      * @param {boolean} isInError Whether the margin control is in an error
199      *     state.
200      */
201     setIsInError: function(isInError) {
202       this.isInError_ = isInError;
203       this.textbox_.classList.toggle('invalid', isInError);
204     },
206     /** @param {boolean} isEnabled Whether to enable the margin control. */
207     setIsEnabled: function(isEnabled) {
208       this.textbox_.disabled = !isEnabled;
209       this.getElement().classList.toggle('margin-control-disabled', !isEnabled);
210     },
212     /** @return {number} Current position of the margin control in points. */
213     getPositionInPts: function() {
214       return this.positionInPts_;
215     },
217     /**
218      * @param {number} posInPts New position of the margin control in points.
219      */
220     setPositionInPts: function(posInPts) {
221       this.positionInPts_ = posInPts;
222       var orientationEnum =
223           print_preview.ticket_items.CustomMargins.Orientation;
224       var x = this.translateTransform_.x;
225       var y = this.translateTransform_.y;
226       var width = null, height = null;
227       if (this.orientation_ == orientationEnum.TOP) {
228         y = this.scaleTransform_ * posInPts + this.translateTransform_.y -
229             MarginControl.RADIUS_;
230         width = this.scaleTransform_ * this.pageSize_.width;
231       } else if (this.orientation_ == orientationEnum.RIGHT) {
232         x = this.scaleTransform_ * (this.pageSize_.width - posInPts) +
233             this.translateTransform_.x - MarginControl.RADIUS_;
234         height = this.scaleTransform_ * this.pageSize_.height;
235       } else if (this.orientation_ == orientationEnum.BOTTOM) {
236         y = this.scaleTransform_ * (this.pageSize_.height - posInPts) +
237             this.translateTransform_.y - MarginControl.RADIUS_;
238         width = this.scaleTransform_ * this.pageSize_.width;
239       } else {
240         x = this.scaleTransform_ * posInPts + this.translateTransform_.x -
241             MarginControl.RADIUS_;
242         height = this.scaleTransform_ * this.pageSize_.height;
243       }
244       this.getElement().style.left = Math.round(x) + 'px';
245       this.getElement().style.top = Math.round(y) + 'px';
246       if (width != null) {
247         this.getElement().style.width = Math.round(width) + 'px';
248       }
249       if (height != null) {
250         this.getElement().style.height = Math.round(height) + 'px';
251       }
252     },
254     /** @return {string} The value in the margin control's textbox. */
255     getTextboxValue: function() {
256       return this.textbox_.value;
257     },
259     /** @param {string} value New value of the margin control's textbox. */
260     setTextboxValue: function(value) {
261       if (this.textbox_.value != value) {
262         this.textbox_.value = value;
263       }
264     },
266     /**
267      * Converts a value in pixels to points.
268      * @param {number} pixels Pixel value to convert.
269      * @return {number} Given value expressed in points.
270      */
271     convertPixelsToPts: function(pixels) {
272       var pts;
273       var orientationEnum =
274           print_preview.ticket_items.CustomMargins.Orientation;
275       if (this.orientation_ == orientationEnum.TOP) {
276         pts = pixels - this.translateTransform_.y + MarginControl.RADIUS_;
277         pts /= this.scaleTransform_;
278       } else if (this.orientation_ == orientationEnum.RIGHT) {
279         pts = pixels - this.translateTransform_.x + MarginControl.RADIUS_;
280         pts /= this.scaleTransform_;
281         pts = this.pageSize_.width - pts;
282       } else if (this.orientation_ == orientationEnum.BOTTOM) {
283         pts = pixels - this.translateTransform_.y + MarginControl.RADIUS_;
284         pts /= this.scaleTransform_;
285         pts = this.pageSize_.height - pts;
286       } else {
287         pts = pixels - this.translateTransform_.x + MarginControl.RADIUS_;
288         pts /= this.scaleTransform_;
289       }
290       return pts;
291     },
293     /**
294      * Translates the position of the margin control relative to the mouse
295      * position in pixels.
296      * @param {!print_preview.Coordinate2d} mousePosition New position of
297      *     the mouse.
298      * @return {!print_preview.Coordinate2d} New position of the margin control.
299      */
300     translateMouseToPositionInPixels: function(mousePosition) {
301       return new print_preview.Coordinate2d(
302           mousePosition.x - this.mouseStartPositionInPixels_.x +
303               this.marginStartPositionInPixels_.x,
304           mousePosition.y - this.mouseStartPositionInPixels_.y +
305               this.marginStartPositionInPixels_.y);
306     },
308     /** @override */
309     createDom: function() {
310       this.setElementInternal(this.cloneTemplateInternal(
311           'margin-control-template'));
312       this.getElement().classList.add('margin-control-' + this.orientation_);
313       this.textbox_ = this.getElement().getElementsByClassName(
314           MarginControl.Classes_.TEXTBOX)[0];
315       this.textbox_.setAttribute(
316           'aria-label', loadTimeData.getString(this.orientation_));
317       this.marginLineEl_ = this.getElement().getElementsByClassName(
318           MarginControl.Classes_.LINE)[0];
319     },
321     /** @override */
322     enterDocument: function() {
323       print_preview.Component.prototype.enterDocument.call(this);
324       this.tracker.add(
325           this.getElement(), 'mousedown', this.onMouseDown_.bind(this));
326       this.tracker.add(
327           this.getElement(),
328           'webkitTransitionEnd',
329           this.onWebkitTransitionEnd_.bind(this));
330       this.tracker.add(
331           this.textbox_, 'input', this.onTextboxInput_.bind(this));
332       this.tracker.add(
333           this.textbox_, 'keydown', this.onTextboxKeyDown_.bind(this));
334       this.tracker.add(
335           this.textbox_, 'focus', this.setIsFocused_.bind(this, true));
336       this.tracker.add(this.textbox_, 'blur', this.onTexboxBlur_.bind(this));
337     },
339     /** @override */
340     exitDocument: function() {
341       print_preview.Component.prototype.exitDocument.call(this);
342       this.textbox_ = null;
343       this.marginLineEl_ = null;
344     },
346     /**
347      * @param {boolean} isFocused Whether the margin control is in focus.
348      * @private
349      */
350     setIsFocused_: function(isFocused) {
351       this.isFocused_ = isFocused;
352     },
354     /**
355      * Called whenever a mousedown event occurs on the component.
356      * @param {MouseEvent} event The event that occured.
357      * @private
358      */
359     onMouseDown_: function(event) {
360       if (!this.textbox_.disabled &&
361           event.button == 0 &&
362           (event.target == this.getElement() ||
363               event.target == this.marginLineEl_)) {
364         this.mouseStartPositionInPixels_ =
365             new print_preview.Coordinate2d(event.x, event.y);
366         this.marginStartPositionInPixels_ = new print_preview.Coordinate2d(
367             this.getElement().offsetLeft, this.getElement().offsetTop);
368         this.setIsInError(false);
369         cr.dispatchSimpleEvent(this, MarginControl.EventType.DRAG_START);
370       }
371     },
373     /**
374      * Called when opacity CSS transition ends.
375      * @private
376      */
377     onWebkitTransitionEnd_: function(event) {
378       if (event.propertyName != 'opacity')
379         return;
380       var elStyle = window.getComputedStyle(this.getElement());
381       var opacity = parseInt(elStyle.getPropertyValue('opacity'), 10);
382       this.textbox_.setAttribute('aria-hidden', opacity == 0);
383     },
385     /**
386      * Called when textbox content changes. Starts text change timeout.
387      * @private
388      */
389     onTextboxInput_: function(event) {
390       if (this.textTimeout_) {
391         clearTimeout(this.textTimeout_);
392         this.textTimeout_ = null;
393       }
394       this.textTimeout_ = setTimeout(
395           this.onTextboxTimeout_.bind(this), MarginControl.TEXTBOX_TIMEOUT_);
396     },
398     /**
399      * Called when a key down event occurs on the textbox. Dispatches a
400      * TEXT_CHANGE event if the "Enter" key was pressed.
401      * @param {Event} event Contains the key that was pressed.
402      * @private
403      */
404     onTextboxKeyDown_: function(event) {
405       if (this.textTimeout_) {
406         clearTimeout(this.textTimeout_);
407         this.textTimeout_ = null;
408       }
409       if (event.keyCode == 13 /*enter*/) {
410         cr.dispatchSimpleEvent(this, MarginControl.EventType.TEXT_CHANGE);
411       }
412     },
414     /**
415      * Called after a timeout after the text in the textbox has changed. Saves
416      * the textbox's value to the print ticket.
417      * @private
418      */
419     onTextboxTimeout_: function() {
420       this.textTimeout_ = null;
421       cr.dispatchSimpleEvent(this, MarginControl.EventType.TEXT_CHANGE);
422     },
424     /**
425      * Called when the textbox loses focus. Dispatches a TEXT_CHANGE event.
426      */
427     onTexboxBlur_: function() {
428       if (this.textTimeout_) {
429         clearTimeout(this.textTimeout_);
430         this.textTimeout_ = null;
431       }
432       this.setIsFocused_(false);
433       cr.dispatchSimpleEvent(this, MarginControl.EventType.TEXT_CHANGE);
434     }
435   };
437   // Export
438   return {
439     MarginControl: MarginControl
440   };