margin_control.js revision a1401311d1ab56c4ed0a474bd38c108f75cb0cd9
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 * Value of the textbox when the timeout was started. 77 * @type {?string} 78 * @private 79 */ 80 this.preTimeoutValue_ = null; 81 82 /** 83 * Textbox used to display and receive the value of the margin. 84 * @type {HTMLInputElement} 85 * @private 86 */ 87 this.textbox_ = null; 88 89 /** 90 * Element of the margin control line. 91 * @type {HTMLElement} 92 * @private 93 */ 94 this.marginLineEl_ = null; 95 96 /** 97 * Whether this margin control's textbox has keyboard focus. 98 * @type {boolean} 99 * @private 100 */ 101 this.isFocused_ = false; 102 103 /** 104 * Whether the margin control is in an error state. 105 * @type {boolean} 106 * @private 107 */ 108 this.isInError_ = false; 109 }; 110 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', 118 119 // Dispatched when the text in the margin control's textbox changes. 120 TEXT_CHANGE: 'print_preview.MarginControl.TEXT_CHANGE' 121 }; 122 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 }; 140 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; 161 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; 169 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; 178 179 MarginControl.prototype = { 180 __proto__: print_preview.Component.prototype, 181 182 /** @return {boolean} Whether this margin control is in focus. */ 183 getIsFocused: function() { 184 return this.isFocused_; 185 }, 186 187 /** 188 * @return {!print_preview.ticket_items.CustomMargins.Orientation} 189 * Orientation of the margin control. 190 */ 191 getOrientation: function() { 192 return this.orientation_; 193 }, 194 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 }, 203 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 }, 213 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 }, 221 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 }, 230 231 /** @return {boolean} Whether the margin control is in an error state. */ 232 getIsInError: function() { 233 return this.isInError_; 234 }, 235 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 }, 248 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 }, 258 259 /** @return {number} Current position of the margin control in points. */ 260 getPositionInPts: function() { 261 return this.positionInPts_; 262 }, 263 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 }, 300 301 /** @return {string} The value in the margin control's textbox. */ 302 getTextboxValue: function() { 303 return this.textbox_.value; 304 }, 305 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 }, 312 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 }, 339 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 }, 354 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 }, 366 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 }, 378 379 /** @override */ 380 exitDocument: function() { 381 print_preview.Component.prototype.exitDocument.call(this); 382 this.textbox_ = null; 383 this.marginLineEl_ = null; 384 }, 385 386 /** 387 * @param {boolean} isFocused Whether the margin control is in focus. 388 * @private 389 */ 390 setIsFocused_: function(isFocused) { 391 this.isFocused_ = isFocused; 392 }, 393 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 }, 412 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.keyCode == 13 /*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 }, 435 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 }, 448 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 }; 462 463 // Export 464 return { 465 MarginControl: MarginControl 466 }; 467}); 468