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. 4 5cr.define('print_preview', function() { 6 'use strict'; 7 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); 18 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; 25 26 /** 27 * Position of the margin control in points. 28 * @type {number} 29 * @private 30 */ 31 this.positionInPts_ = 0; 32 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); 39 40 /** 41 * Amount to scale pixel values by to convert to pixel space. 42 * @type {number} 43 * @private 44 */ 45 this.scaleTransform_ = 1; 46 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); 53 54 /** 55 * Position of the margin control when dragging starts. 56 * @type {print_preview.Coordinate2d} 57 * @private 58 */ 59 this.marginStartPositionInPixels_ = null; 60 61 /** 62 * Position of the mouse when the dragging starts. 63 * @type {print_preview.Coordinate2d} 64 * @private 65 */ 66 this.mouseStartPositionInPixels_ = null; 67 68 /** 69 * Processing timeout for the textbox. 70 * @type {?number} 71 * @private 72 */ 73 this.textTimeout_ = null; 74 75 /** 76 * Textbox used to display and receive the value of the margin. 77 * @type {HTMLInputElement} 78 * @private 79 */ 80 this.textbox_ = null; 81 82 /** 83 * Element of the margin control line. 84 * @type {HTMLElement} 85 * @private 86 */ 87 this.marginLineEl_ = null; 88 89 /** 90 * Whether this margin control's textbox has keyboard focus. 91 * @type {boolean} 92 * @private 93 */ 94 this.isFocused_ = false; 95 96 /** 97 * Whether the margin control is in an error state. 98 * @type {boolean} 99 * @private 100 */ 101 this.isInError_ = false; 102 }; 103 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', 111 112 // Dispatched when the text in the margin control's textbox changes. 113 TEXT_CHANGE: 'print_preview.MarginControl.TEXT_CHANGE' 114 }; 115 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 }; 126 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; 134 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; 143 144 MarginControl.prototype = { 145 __proto__: print_preview.Component.prototype, 146 147 /** @return {boolean} Whether this margin control is in focus. */ 148 getIsFocused: function() { 149 return this.isFocused_; 150 }, 151 152 /** 153 * @return {!print_preview.ticket_items.CustomMargins.Orientation} 154 * Orientation of the margin control. 155 */ 156 getOrientation: function() { 157 return this.orientation_; 158 }, 159 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 }, 168 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 }, 178 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 }, 186 187 /** @param {boolean} isVisible Whether the margin control is visible. */ 188 setIsVisible: function(isVisible) { 189 this.getElement().classList.toggle('invisible', !isVisible); 190 }, 191 192 /** @return {boolean} Whether the margin control is in an error state. */ 193 getIsInError: function() { 194 return this.isInError_; 195 }, 196 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 }, 205 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 }, 211 212 /** @return {number} Current position of the margin control in points. */ 213 getPositionInPts: function() { 214 return this.positionInPts_; 215 }, 216 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 }, 253 254 /** @return {string} The value in the margin control's textbox. */ 255 getTextboxValue: function() { 256 return this.textbox_.value; 257 }, 258 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 }, 265 266 /** 267 * Converts a value in pixels to points. 268 * @param {number} 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 }, 292 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 }, 307 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', localStrings.getString(this.orientation_)); 317 this.marginLineEl_ = this.getElement().getElementsByClassName( 318 MarginControl.Classes_.LINE)[0]; 319 }, 320 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 }, 338 339 /** @override */ 340 exitDocument: function() { 341 print_preview.Component.prototype.exitDocument.call(this); 342 this.textbox_ = null; 343 this.marginLineEl_ = null; 344 }, 345 346 /** 347 * @param {boolean} isFocused Whether the margin control is in focus. 348 * @private 349 */ 350 setIsFocused_: function(isFocused) { 351 this.isFocused_ = isFocused; 352 }, 353 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 }, 372 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')); 382 this.textbox_.setAttribute('aria-hidden', opacity == 0); 383 }, 384 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 }, 397 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 }, 413 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 }, 423 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 }; 436 437 // Export 438 return { 439 MarginControl: MarginControl 440 }; 441}); 442