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() {
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.
14 * @extends {print_preview.Component}
16 function MarginControl(orientation) {
17 print_preview.Component.call(this);
20 * Determines where the margin textbox will be placed.
21 * @type {!print_preview.ticket_items.CustomMargins.Orientation}
24 this.orientation_ = orientation;
27 * Position of the margin control in points.
31 this.positionInPts_ = 0;
34 * Page size of the document to print.
35 * @type {!print_preview.Size}
38 this.pageSize_ = new print_preview.Size(0, 0);
41 * Amount to scale pixel values by to convert to pixel space.
45 this.scaleTransform_ = 1;
48 * Amount to translate values in pixel space.
49 * @type {!print_preview.Coordinate2d}
52 this.translateTransform_ = new print_preview.Coordinate2d(0, 0);
55 * Position of the margin control when dragging starts.
56 * @type {print_preview.Coordinate2d}
59 this.marginStartPositionInPixels_ = null;
62 * Position of the mouse when the dragging starts.
63 * @type {print_preview.Coordinate2d}
66 this.mouseStartPositionInPixels_ = null;
69 * Processing timeout for the textbox.
73 this.textTimeout_ = null;
76 * Textbox used to display and receive the value of the margin.
77 * @type {HTMLInputElement}
83 * Element of the margin control line.
87 this.marginLineEl_ = null;
90 * Whether this margin control's textbox has keyboard focus.
94 this.isFocused_ = false;
97 * Whether the margin control is in an error state.
101 this.isInError_ = false;
105 * Event types dispatched by the margin control.
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'
117 * CSS classes used by this component.
121 MarginControl.Classes_ = {
122 TEXTBOX: 'margin-control-textbox',
123 DRAGGING: 'margin-control-dragging',
124 LINE: 'margin-control-line'
128 * Radius of the margin control in pixels. Padding of control + 1 for border.
133 MarginControl.RADIUS_ = 9;
136 * Timeout after a text change after which the text in the textbox is saved to
137 * the print ticket. Value in milliseconds.
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_;
153 * @return {!print_preview.ticket_items.CustomMargins.Orientation}
154 * Orientation of the margin control.
156 getOrientation: function() {
157 return this.orientation_;
161 * @param {number} scaleTransform New scale transform of the margin control.
163 setScaleTransform: function(scaleTransform) {
164 this.scaleTransform_ = scaleTransform;
166 this.setPositionInPts(this.positionInPts_);
170 * @param {!print_preview.Coordinate2d} translateTransform New translate
171 * transform of the margin control.
173 setTranslateTransform: function(translateTransform) {
174 this.translateTransform_ = translateTransform;
176 this.setPositionInPts(this.positionInPts_);
180 * @param {!print_preview.Size} pageSize New size of the document's pages.
182 setPageSize: function(pageSize) {
183 this.pageSize_ = pageSize;
184 this.setPositionInPts(this.positionInPts_);
187 /** @param {boolean} isVisible Whether the margin control is visible. */
188 setIsVisible: function(isVisible) {
189 this.getElement().classList.toggle('invisible', !isVisible);
192 /** @return {boolean} Whether the margin control is in an error state. */
193 getIsInError: function() {
194 return this.isInError_;
198 * @param {boolean} isInError Whether the margin control is in an error
201 setIsInError: function(isInError) {
202 this.isInError_ = isInError;
203 this.textbox_.classList.toggle('invalid', isInError);
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);
212 /** @return {number} Current position of the margin control in points. */
213 getPositionInPts: function() {
214 return this.positionInPts_;
218 * @param {number} posInPts New position of the margin control in points.
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;
240 x = this.scaleTransform_ * posInPts + this.translateTransform_.x -
241 MarginControl.RADIUS_;
242 height = this.scaleTransform_ * this.pageSize_.height;
244 this.getElement().style.left = Math.round(x) + 'px';
245 this.getElement().style.top = Math.round(y) + 'px';
247 this.getElement().style.width = Math.round(width) + 'px';
249 if (height != null) {
250 this.getElement().style.height = Math.round(height) + 'px';
254 /** @return {string} The value in the margin control's textbox. */
255 getTextboxValue: function() {
256 return this.textbox_.value;
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;
267 * Converts a value in pixels to points.
268 * @param {number} pixels Pixel value to convert.
269 * @return {number} Given value expressed in points.
271 convertPixelsToPts: function(pixels) {
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;
287 pts = pixels - this.translateTransform_.x + MarginControl.RADIUS_;
288 pts /= this.scaleTransform_;
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
298 * @return {!print_preview.Coordinate2d} New position of the margin control.
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);
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];
322 enterDocument: function() {
323 print_preview.Component.prototype.enterDocument.call(this);
325 this.getElement(), 'mousedown', this.onMouseDown_.bind(this));
328 'webkitTransitionEnd',
329 this.onWebkitTransitionEnd_.bind(this));
331 this.textbox_, 'input', this.onTextboxInput_.bind(this));
333 this.textbox_, 'keydown', this.onTextboxKeyDown_.bind(this));
335 this.textbox_, 'focus', this.setIsFocused_.bind(this, true));
336 this.tracker.add(this.textbox_, 'blur', this.onTexboxBlur_.bind(this));
340 exitDocument: function() {
341 print_preview.Component.prototype.exitDocument.call(this);
342 this.textbox_ = null;
343 this.marginLineEl_ = null;
347 * @param {boolean} isFocused Whether the margin control is in focus.
350 setIsFocused_: function(isFocused) {
351 this.isFocused_ = isFocused;
355 * Called whenever a mousedown event occurs on the component.
356 * @param {MouseEvent} event The event that occured.
359 onMouseDown_: function(event) {
360 if (!this.textbox_.disabled &&
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);
374 * Called when opacity CSS transition ends.
377 onWebkitTransitionEnd_: function(event) {
378 if (event.propertyName != 'opacity')
380 var elStyle = window.getComputedStyle(this.getElement());
381 var opacity = parseInt(elStyle.getPropertyValue('opacity'), 10);
382 this.textbox_.setAttribute('aria-hidden', opacity == 0);
386 * Called when textbox content changes. Starts text change timeout.
389 onTextboxInput_: function(event) {
390 if (this.textTimeout_) {
391 clearTimeout(this.textTimeout_);
392 this.textTimeout_ = null;
394 this.textTimeout_ = setTimeout(
395 this.onTextboxTimeout_.bind(this), MarginControl.TEXTBOX_TIMEOUT_);
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.
404 onTextboxKeyDown_: function(event) {
405 if (this.textTimeout_) {
406 clearTimeout(this.textTimeout_);
407 this.textTimeout_ = null;
409 if (event.keyCode == 13 /*enter*/) {
410 cr.dispatchSimpleEvent(this, MarginControl.EventType.TEXT_CHANGE);
415 * Called after a timeout after the text in the textbox has changed. Saves
416 * the textbox's value to the print ticket.
419 onTextboxTimeout_: function() {
420 this.textTimeout_ = null;
421 cr.dispatchSimpleEvent(this, MarginControl.EventType.TEXT_CHANGE);
425 * Called when the textbox loses focus. Dispatches a TEXT_CHANGE event.
427 onTexboxBlur_: function() {
428 if (this.textTimeout_) {
429 clearTimeout(this.textTimeout_);
430 this.textTimeout_ = null;
432 this.setIsFocused_(false);
433 cr.dispatchSimpleEvent(this, MarginControl.EventType.TEXT_CHANGE);
439 MarginControl: MarginControl