// Copyright (c) 2012 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. cr.define('print_preview', function() { 'use strict'; /** * Draggable control for setting a page margin. * @param {!print_preview.ticket_items.CustomMargins.Orientation} orientation * Orientation of the margin control that determines where the margin * textbox will be placed. * @constructor * @extends {print_preview.Component} */ function MarginControl(orientation) { print_preview.Component.call(this); /** * Determines where the margin textbox will be placed. * @type {!print_preview.ticket_items.CustomMargins.Orientation} * @private */ this.orientation_ = orientation; /** * Position of the margin control in points. * @type {number} * @private */ this.positionInPts_ = 0; /** * Page size of the document to print. * @type {!print_preview.Size} * @private */ this.pageSize_ = new print_preview.Size(0, 0); /** * Amount to scale pixel values by to convert to pixel space. * @type {number} * @private */ this.scaleTransform_ = 1; /** * Amount to translate values in pixel space. * @type {!print_preview.Coordinate2d} * @private */ this.translateTransform_ = new print_preview.Coordinate2d(0, 0); /** * Position of the margin control when dragging starts. * @type {print_preview.Coordinate2d} * @private */ this.marginStartPositionInPixels_ = null; /** * Position of the mouse when the dragging starts. * @type {print_preview.Coordinate2d} * @private */ this.mouseStartPositionInPixels_ = null; /** * Processing timeout for the textbox. * @type {?number} * @private */ this.textTimeout_ = null; /** * Textbox used to display and receive the value of the margin. * @type {HTMLInputElement} * @private */ this.textbox_ = null; /** * Element of the margin control line. * @type {HTMLElement} * @private */ this.marginLineEl_ = null; /** * Whether this margin control's textbox has keyboard focus. * @type {boolean} * @private */ this.isFocused_ = false; /** * Whether the margin control is in an error state. * @type {boolean} * @private */ this.isInError_ = false; }; /** * Event types dispatched by the margin control. * @enum {string} */ MarginControl.EventType = { // Dispatched when the margin control starts dragging. DRAG_START: 'print_preview.MarginControl.DRAG_START', // Dispatched when the text in the margin control's textbox changes. TEXT_CHANGE: 'print_preview.MarginControl.TEXT_CHANGE' }; /** * CSS classes used by this component. * @enum {string} * @private */ MarginControl.Classes_ = { TEXTBOX: 'margin-control-textbox', DRAGGING: 'margin-control-dragging', LINE: 'margin-control-line' }; /** * Radius of the margin control in pixels. Padding of control + 1 for border. * @type {number} * @const * @private */ MarginControl.RADIUS_ = 9; /** * Timeout after a text change after which the text in the textbox is saved to * the print ticket. Value in milliseconds. * @type {number} * @const * @private */ MarginControl.TEXTBOX_TIMEOUT_ = 1000; MarginControl.prototype = { __proto__: print_preview.Component.prototype, /** @return {boolean} Whether this margin control is in focus. */ getIsFocused: function() { return this.isFocused_; }, /** * @return {!print_preview.ticket_items.CustomMargins.Orientation} * Orientation of the margin control. */ getOrientation: function() { return this.orientation_; }, /** * @param {number} scaleTransform New scale transform of the margin control. */ setScaleTransform: function(scaleTransform) { this.scaleTransform_ = scaleTransform; // Reset position this.setPositionInPts(this.positionInPts_); }, /** * @param {!print_preview.Coordinate2d} translateTransform New translate * transform of the margin control. */ setTranslateTransform: function(translateTransform) { this.translateTransform_ = translateTransform; // Reset position this.setPositionInPts(this.positionInPts_); }, /** * @param {!print_preview.Size} pageSize New size of the document's pages. */ setPageSize: function(pageSize) { this.pageSize_ = pageSize; this.setPositionInPts(this.positionInPts_); }, /** @param {boolean} isVisible Whether the margin control is visible. */ setIsVisible: function(isVisible) { this.getElement().classList.toggle('invisible', !isVisible); }, /** @return {boolean} Whether the margin control is in an error state. */ getIsInError: function() { return this.isInError_; }, /** * @param {boolean} isInError Whether the margin control is in an error * state. */ setIsInError: function(isInError) { this.isInError_ = isInError; this.textbox_.classList.toggle('invalid', isInError); }, /** @param {boolean} isEnabled Whether to enable the margin control. */ setIsEnabled: function(isEnabled) { this.textbox_.disabled = !isEnabled; this.getElement().classList.toggle('margin-control-disabled', !isEnabled); }, /** @return {number} Current position of the margin control in points. */ getPositionInPts: function() { return this.positionInPts_; }, /** * @param {number} posInPts New position of the margin control in points. */ setPositionInPts: function(posInPts) { this.positionInPts_ = posInPts; var orientationEnum = print_preview.ticket_items.CustomMargins.Orientation; var x = this.translateTransform_.x; var y = this.translateTransform_.y; var width = null, height = null; if (this.orientation_ == orientationEnum.TOP) { y = this.scaleTransform_ * posInPts + this.translateTransform_.y - MarginControl.RADIUS_; width = this.scaleTransform_ * this.pageSize_.width; } else if (this.orientation_ == orientationEnum.RIGHT) { x = this.scaleTransform_ * (this.pageSize_.width - posInPts) + this.translateTransform_.x - MarginControl.RADIUS_; height = this.scaleTransform_ * this.pageSize_.height; } else if (this.orientation_ == orientationEnum.BOTTOM) { y = this.scaleTransform_ * (this.pageSize_.height - posInPts) + this.translateTransform_.y - MarginControl.RADIUS_; width = this.scaleTransform_ * this.pageSize_.width; } else { x = this.scaleTransform_ * posInPts + this.translateTransform_.x - MarginControl.RADIUS_; height = this.scaleTransform_ * this.pageSize_.height; } this.getElement().style.left = Math.round(x) + 'px'; this.getElement().style.top = Math.round(y) + 'px'; if (width != null) { this.getElement().style.width = Math.round(width) + 'px'; } if (height != null) { this.getElement().style.height = Math.round(height) + 'px'; } }, /** @return {string} The value in the margin control's textbox. */ getTextboxValue: function() { return this.textbox_.value; }, /** @param {string} value New value of the margin control's textbox. */ setTextboxValue: function(value) { if (this.textbox_.value != value) { this.textbox_.value = value; } }, /** * Converts a value in pixels to points. * @param {number} pixels Pixel value to convert. * @return {number} Given value expressed in points. */ convertPixelsToPts: function(pixels) { var pts; var orientationEnum = print_preview.ticket_items.CustomMargins.Orientation; if (this.orientation_ == orientationEnum.TOP) { pts = pixels - this.translateTransform_.y + MarginControl.RADIUS_; pts /= this.scaleTransform_; } else if (this.orientation_ == orientationEnum.RIGHT) { pts = pixels - this.translateTransform_.x + MarginControl.RADIUS_; pts /= this.scaleTransform_; pts = this.pageSize_.width - pts; } else if (this.orientation_ == orientationEnum.BOTTOM) { pts = pixels - this.translateTransform_.y + MarginControl.RADIUS_; pts /= this.scaleTransform_; pts = this.pageSize_.height - pts; } else { pts = pixels - this.translateTransform_.x + MarginControl.RADIUS_; pts /= this.scaleTransform_; } return pts; }, /** * Translates the position of the margin control relative to the mouse * position in pixels. * @param {!print_preview.Coordinate2d} mousePosition New position of * the mouse. * @return {!print_preview.Coordinate2d} New position of the margin control. */ translateMouseToPositionInPixels: function(mousePosition) { return new print_preview.Coordinate2d( mousePosition.x - this.mouseStartPositionInPixels_.x + this.marginStartPositionInPixels_.x, mousePosition.y - this.mouseStartPositionInPixels_.y + this.marginStartPositionInPixels_.y); }, /** @override */ createDom: function() { this.setElementInternal(this.cloneTemplateInternal( 'margin-control-template')); this.getElement().classList.add('margin-control-' + this.orientation_); this.textbox_ = this.getElement().getElementsByClassName( MarginControl.Classes_.TEXTBOX)[0]; this.textbox_.setAttribute( 'aria-label', loadTimeData.getString(this.orientation_)); this.marginLineEl_ = this.getElement().getElementsByClassName( MarginControl.Classes_.LINE)[0]; }, /** @override */ enterDocument: function() { print_preview.Component.prototype.enterDocument.call(this); this.tracker.add( this.getElement(), 'mousedown', this.onMouseDown_.bind(this)); this.tracker.add( this.getElement(), 'webkitTransitionEnd', this.onWebkitTransitionEnd_.bind(this)); this.tracker.add( this.textbox_, 'input', this.onTextboxInput_.bind(this)); this.tracker.add( this.textbox_, 'keydown', this.onTextboxKeyDown_.bind(this)); this.tracker.add( this.textbox_, 'focus', this.setIsFocused_.bind(this, true)); this.tracker.add(this.textbox_, 'blur', this.onTexboxBlur_.bind(this)); }, /** @override */ exitDocument: function() { print_preview.Component.prototype.exitDocument.call(this); this.textbox_ = null; this.marginLineEl_ = null; }, /** * @param {boolean} isFocused Whether the margin control is in focus. * @private */ setIsFocused_: function(isFocused) { this.isFocused_ = isFocused; }, /** * Called whenever a mousedown event occurs on the component. * @param {MouseEvent} event The event that occured. * @private */ onMouseDown_: function(event) { if (!this.textbox_.disabled && event.button == 0 && (event.target == this.getElement() || event.target == this.marginLineEl_)) { this.mouseStartPositionInPixels_ = new print_preview.Coordinate2d(event.x, event.y); this.marginStartPositionInPixels_ = new print_preview.Coordinate2d( this.getElement().offsetLeft, this.getElement().offsetTop); this.setIsInError(false); cr.dispatchSimpleEvent(this, MarginControl.EventType.DRAG_START); } }, /** * Called when opacity CSS transition ends. * @private */ onWebkitTransitionEnd_: function(event) { if (event.propertyName != 'opacity') return; var elStyle = window.getComputedStyle(this.getElement()); var opacity = parseInt(elStyle.getPropertyValue('opacity'), 10); this.textbox_.setAttribute('aria-hidden', opacity == 0); }, /** * Called when textbox content changes. Starts text change timeout. * @private */ onTextboxInput_: function(event) { if (this.textTimeout_) { clearTimeout(this.textTimeout_); this.textTimeout_ = null; } this.textTimeout_ = setTimeout( this.onTextboxTimeout_.bind(this), MarginControl.TEXTBOX_TIMEOUT_); }, /** * Called when a key down event occurs on the textbox. Dispatches a * TEXT_CHANGE event if the "Enter" key was pressed. * @param {Event} event Contains the key that was pressed. * @private */ onTextboxKeyDown_: function(event) { if (this.textTimeout_) { clearTimeout(this.textTimeout_); this.textTimeout_ = null; } if (event.keyCode == 13 /*enter*/) { cr.dispatchSimpleEvent(this, MarginControl.EventType.TEXT_CHANGE); } }, /** * Called after a timeout after the text in the textbox has changed. Saves * the textbox's value to the print ticket. * @private */ onTextboxTimeout_: function() { this.textTimeout_ = null; cr.dispatchSimpleEvent(this, MarginControl.EventType.TEXT_CHANGE); }, /** * Called when the textbox loses focus. Dispatches a TEXT_CHANGE event. */ onTexboxBlur_: function() { if (this.textTimeout_) { clearTimeout(this.textTimeout_); this.textTimeout_ = null; } this.setIsFocused_(false); cr.dispatchSimpleEvent(this, MarginControl.EventType.TEXT_CHANGE); } }; // Export return { MarginControl: MarginControl }; });