OLD | NEW |
(Empty) | |
| 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 |
| 5 cr.define('print_preview', function() { |
| 6 'use strict'; |
| 7 |
| 8 /** |
| 9 * UI component used for setting custom print margins. |
| 10 * @param {!print_preview.PrintTicketStore} printTicketStore Used to read and |
| 11 * write custom margin values. |
| 12 * @constructor |
| 13 * @extends {print_preview.Component} |
| 14 */ |
| 15 function MarginControlContainer(printTicketStore) { |
| 16 print_preview.Component.call(this); |
| 17 |
| 18 /** |
| 19 * Used to read and write custom margin values. |
| 20 * @type {!print_preview.PrintTicketStore} |
| 21 * @private |
| 22 */ |
| 23 this.printTicketStore_ = printTicketStore; |
| 24 |
| 25 /** |
| 26 * Used to convert between the system's local units and points. |
| 27 * @type {!print_preview.MeasurementSystem} |
| 28 * @private |
| 29 */ |
| 30 this.measurementSystem_ = printTicketStore.measurementSystem; |
| 31 |
| 32 /** |
| 33 * Convenience array that contains all of the margin controls. |
| 34 * @type {!Object< |
| 35 * print_preview.ticket_items.CustomMargins.Orientation, |
| 36 * !print_preview.MarginControl>} |
| 37 * @private |
| 38 */ |
| 39 this.controls_ = {}; |
| 40 for (var key in print_preview.ticket_items.CustomMargins.Orientation) { |
| 41 var orientation = print_preview.ticket_items.CustomMargins.Orientation[ |
| 42 key]; |
| 43 var control = new print_preview.MarginControl(orientation); |
| 44 this.controls_[orientation] = control; |
| 45 this.addChild(control); |
| 46 } |
| 47 |
| 48 /** |
| 49 * Margin control currently being dragged. Null if no control is being |
| 50 * dragged. |
| 51 * @type {print_preview.MarginControl} |
| 52 * @private |
| 53 */ |
| 54 this.draggedControl_ = null; |
| 55 |
| 56 /** |
| 57 * Translation transformation in pixels to translate from the origin of the |
| 58 * custom margins component to the top-left corner of the most visible |
| 59 * preview page. |
| 60 * @type {!print_preview.Coordinate2d} |
| 61 * @private |
| 62 */ |
| 63 this.translateTransform_ = new print_preview.Coordinate2d(0, 0); |
| 64 |
| 65 /** |
| 66 * Scaling transformation to scale from pixels to the units which the |
| 67 * print preview is in. The scaling factor is the same in both dimensions, |
| 68 * so this field is just a single number. |
| 69 * @type {number} |
| 70 * @private |
| 71 */ |
| 72 this.scaleTransform_ = 1; |
| 73 |
| 74 /** |
| 75 * Clipping size for clipping the margin controls. |
| 76 * @type {print_preview.Size} |
| 77 * @private |
| 78 */ |
| 79 this.clippingSize_ = null; |
| 80 }; |
| 81 |
| 82 /** |
| 83 * CSS classes used by the custom margins component. |
| 84 * @enum {string} |
| 85 * @private |
| 86 */ |
| 87 MarginControlContainer.Classes_ = { |
| 88 DRAGGING_HORIZONTAL: 'margin-control-container-dragging-horizontal', |
| 89 DRAGGING_VERTICAL: 'margin-control-container-dragging-vertical' |
| 90 }; |
| 91 |
| 92 /** |
| 93 * @param {print_preview.ticket_items.CustomMargins.Orientation} orientation |
| 94 * Orientation value to test. |
| 95 * @return {boolean} Whether the given orientation is TOP or BOTTOM. |
| 96 * @private |
| 97 */ |
| 98 MarginControlContainer.isTopOrBottom_ = function(orientation) { |
| 99 return orientation == |
| 100 print_preview.ticket_items.CustomMargins.Orientation.TOP || |
| 101 orientation == |
| 102 print_preview.ticket_items.CustomMargins.Orientation.BOTTOM; |
| 103 }; |
| 104 |
| 105 MarginControlContainer.prototype = { |
| 106 __proto__: print_preview.Component.prototype, |
| 107 |
| 108 /** |
| 109 * Updates the translation transformation that translates pixel values in |
| 110 * the space of the HTML DOM. |
| 111 * @param {print_preview.Coordinate2d} translateTransform Updated value of |
| 112 * the translation transformation. |
| 113 */ |
| 114 updateTranslationTransform: function(translateTransform) { |
| 115 if (!translateTransform.equals(this.translateTransform_)) { |
| 116 this.translateTransform_ = translateTransform; |
| 117 for (var orientation in this.controls_) { |
| 118 this.controls_[orientation].setTranslateTransform(translateTransform); |
| 119 } |
| 120 } |
| 121 }, |
| 122 |
| 123 /** |
| 124 * Updates the scaling transform that scales pixels values to point values. |
| 125 * @param {number} scaleTransform Updated value of the scale transform. |
| 126 */ |
| 127 updateScaleTransform: function(scaleTransform) { |
| 128 if (scaleTransform != this.scaleTransform_) { |
| 129 this.scaleTransform_ = scaleTransform; |
| 130 for (var orientation in this.controls_) { |
| 131 this.controls_[orientation].setScaleTransform(scaleTransform); |
| 132 } |
| 133 } |
| 134 }, |
| 135 |
| 136 /** |
| 137 * Clips margin controls to the given clip size in pixels. |
| 138 * @param {print_preview.Size} Size to clip the margin controls to. |
| 139 */ |
| 140 updateClippingMask: function(clipSize) { |
| 141 if (!clipSize) { |
| 142 return; |
| 143 } |
| 144 this.clippingSize_ = clipSize; |
| 145 for (var orientation in this.controls_) { |
| 146 var el = this.controls_[orientation].getElement(); |
| 147 el.style.clip = 'rect(' + |
| 148 (-el.offsetTop) + 'px, ' + |
| 149 (clipSize.width - el.offsetLeft) + 'px, ' + |
| 150 (clipSize.height - el.offsetTop) + 'px, ' + |
| 151 (-el.offsetLeft) + 'px)'; |
| 152 } |
| 153 }, |
| 154 |
| 155 /** Shows the margin controls if the need to be shown. */ |
| 156 showMarginControlsIfNeeded: function() { |
| 157 if (this.printTicketStore_.getMarginsType() == |
| 158 print_preview.ticket_items.MarginsType.Value.CUSTOM) { |
| 159 this.setIsMarginControlsVisible_(true); |
| 160 } |
| 161 }, |
| 162 |
| 163 /** @override */ |
| 164 enterDocument: function() { |
| 165 print_preview.Component.prototype.enterDocument.call(this); |
| 166 |
| 167 // We want to respond to mouse up events even beyond the component's |
| 168 // element. |
| 169 this.tracker.add(window, 'mouseup', this.onMouseUp_.bind(this)); |
| 170 this.tracker.add(window, 'mousemove', this.onMouseMove_.bind(this)); |
| 171 this.tracker.add( |
| 172 this.getElement(), 'mouseover', this.onMouseOver_.bind(this)); |
| 173 this.tracker.add( |
| 174 this.getElement(), 'mouseout', this.onMouseOut_.bind(this)); |
| 175 |
| 176 this.tracker.add( |
| 177 this.printTicketStore_, |
| 178 print_preview.PrintTicketStore.EventType.INITIALIZE, |
| 179 this.onTicketChange_.bind(this)); |
| 180 this.tracker.add( |
| 181 this.printTicketStore_, |
| 182 print_preview.PrintTicketStore.EventType.TICKET_CHANGE, |
| 183 this.onTicketChange_.bind(this)); |
| 184 this.tracker.add( |
| 185 this.printTicketStore_, |
| 186 print_preview.PrintTicketStore.EventType.DOCUMENT_CHANGE, |
| 187 this.onTicketChange_.bind(this)); |
| 188 this.tracker.add( |
| 189 this.printTicketStore_, |
| 190 print_preview.PrintTicketStore.EventType.CAPABILITIES_CHANGE, |
| 191 this.onTicketChange_.bind(this)); |
| 192 |
| 193 for (var orientation in this.controls_) { |
| 194 this.tracker.add( |
| 195 this.controls_[orientation], |
| 196 print_preview.MarginControl.EventType.DRAG_START, |
| 197 this.onControlDragStart_.bind(this, this.controls_[orientation])); |
| 198 this.tracker.add( |
| 199 this.controls_[orientation], |
| 200 print_preview.MarginControl.EventType.TEXT_CHANGE, |
| 201 this.onControlTextChange_.bind(this, this.controls_[orientation])); |
| 202 } |
| 203 }, |
| 204 |
| 205 /** @override */ |
| 206 decorateInternal: function() { |
| 207 for (var orientation in this.controls_) { |
| 208 this.controls_[orientation].render(this.getElement()); |
| 209 } |
| 210 }, |
| 211 |
| 212 /** |
| 213 * @param {boolean} isVisible Whether the margin controls are visible. |
| 214 * @private |
| 215 */ |
| 216 setIsMarginControlsVisible_: function(isVisible) { |
| 217 for (var orientation in this.controls_) { |
| 218 this.controls_[orientation].setIsVisible(isVisible); |
| 219 } |
| 220 }, |
| 221 |
| 222 /** |
| 223 * Moves the position of the given control to the desired position in |
| 224 * pixels within some constraint minimum and maximum. |
| 225 * @param {!print_preview.MarginControl} control Control to move. |
| 226 * @param {!print_preview.Coordinate2d} posInPixels Desired position to move |
| 227 * to in pixels. |
| 228 * @private |
| 229 */ |
| 230 moveControlWithConstraints_: function(control, posInPixels) { |
| 231 var newPosInPts; |
| 232 if (MarginControlContainer.isTopOrBottom_(control.getOrientation())) { |
| 233 newPosInPts = control.convertPixelsToPts(posInPixels.y); |
| 234 } else { |
| 235 newPosInPts = control.convertPixelsToPts(posInPixels.x); |
| 236 } |
| 237 newPosInPts = Math.min( |
| 238 this.printTicketStore_.getCustomMarginMax(control.getOrientation()), |
| 239 newPosInPts); |
| 240 newPosInPts = Math.max(0, newPosInPts); |
| 241 newPosInPts = Math.round(newPosInPts); |
| 242 control.setPositionInPts(newPosInPts); |
| 243 control.setTextboxValue(this.serializeValueFromPts_(newPosInPts)); |
| 244 }, |
| 245 |
| 246 /** |
| 247 * @param {string} value Value to parse to points. E.g. '3.40"' or '200mm'. |
| 248 * @return {number} Value in points represented by the input value. |
| 249 * @private |
| 250 */ |
| 251 parseValueToPts_: function(value) { |
| 252 // Removing whitespace anywhere in the string. |
| 253 value = value.replace(/\s*/g, ''); |
| 254 if (value.length == 0) { |
| 255 return null; |
| 256 } |
| 257 var validationRegex = new RegExp('^(^-?)(\\d)+(\\' + |
| 258 this.measurementSystem_.thousandsDelimeter + '\\d{3})*(\\' + |
| 259 this.measurementSystem_.decimalDelimeter + '\\d*)?' + |
| 260 '(' + this.measurementSystem_.unitSymbol + ')?$'); |
| 261 if (validationRegex.test(value)) { |
| 262 // Replacing decimal point with the dot symbol in order to use |
| 263 // parseFloat() properly. |
| 264 var replacementRegex = |
| 265 new RegExp('\\' + this.measurementSystem_.decimalDelimeter + '{1}'); |
| 266 value = value.replace(replacementRegex, '.'); |
| 267 return this.measurementSystem_.convertToPoints(parseFloat(value)); |
| 268 } |
| 269 return null; |
| 270 }, |
| 271 |
| 272 /** |
| 273 * @param {number} value Value in points to serialize. |
| 274 * @return {string} String representation of the value in the system's local |
| 275 * units. |
| 276 * @private |
| 277 */ |
| 278 serializeValueFromPts_: function(value) { |
| 279 value = this.measurementSystem_.convertFromPoints(value); |
| 280 value = this.measurementSystem_.roundValue(value); |
| 281 return value + this.measurementSystem_.unitSymbol; |
| 282 }, |
| 283 |
| 284 /** |
| 285 * Called when a margin control starts to drag. |
| 286 * @param {print_preview.MarginControl} control The control which started to |
| 287 * drag. |
| 288 * @private |
| 289 */ |
| 290 onControlDragStart_: function(control) { |
| 291 this.draggedControl_ = control; |
| 292 this.getElement().classList.add( |
| 293 MarginControlContainer.isTopOrBottom_(control.getOrientation()) ? |
| 294 MarginControlContainer.Classes_.DRAGGING_VERTICAL : |
| 295 MarginControlContainer.Classes_.DRAGGING_HORIZONTAL); |
| 296 }, |
| 297 |
| 298 /** |
| 299 * Called when the mouse moves in the custom margins component. Moves the |
| 300 * dragged margin control. |
| 301 * @param {MouseEvent} event Contains the position of the mouse. |
| 302 * @private |
| 303 */ |
| 304 onMouseMove_: function(event) { |
| 305 if (this.draggedControl_) { |
| 306 this.moveControlWithConstraints_( |
| 307 this.draggedControl_, |
| 308 this.draggedControl_.translateMouseToPositionInPixels( |
| 309 new print_preview.Coordinate2d(event.x, event.y))); |
| 310 this.updateClippingMask(this.clippingSize_); |
| 311 } |
| 312 }, |
| 313 |
| 314 /** |
| 315 * Called when the mouse is released in the custom margins component. |
| 316 * Releases the dragged margin control. |
| 317 * @param {MouseEvent} event Contains the position of the mouse. |
| 318 * @private |
| 319 */ |
| 320 onMouseUp_: function(event) { |
| 321 if (this.draggedControl_) { |
| 322 this.getElement().classList.remove( |
| 323 MarginControlContainer.Classes_.DRAGGING_VERTICAL); |
| 324 this.getElement().classList.remove( |
| 325 MarginControlContainer.Classes_.DRAGGING_HORIZONTAL); |
| 326 if (event) { |
| 327 var posInPixels = |
| 328 this.draggedControl_.translateMouseToPositionInPixels( |
| 329 new print_preview.Coordinate2d(event.x, event.y)); |
| 330 this.moveControlWithConstraints_(this.draggedControl_, posInPixels); |
| 331 } |
| 332 this.updateClippingMask(this.clippingSize_); |
| 333 this.printTicketStore_.updateCustomMargin( |
| 334 this.draggedControl_.getOrientation(), |
| 335 this.draggedControl_.getPositionInPts()); |
| 336 this.draggedControl_ = null; |
| 337 } |
| 338 }, |
| 339 |
| 340 /** |
| 341 * Called when the mouse moves onto the component. Shows the margin |
| 342 * controls. |
| 343 * @private |
| 344 */ |
| 345 onMouseOver_: function() { |
| 346 var fromElement = event.fromElement; |
| 347 while (fromElement != null) { |
| 348 if (fromElement == this.getElement()) { |
| 349 return; |
| 350 } |
| 351 fromElement = fromElement.parentElement; |
| 352 } |
| 353 if (this.printTicketStore_.hasMarginsCapability() && |
| 354 this.printTicketStore_.getMarginsType() == |
| 355 print_preview.ticket_items.MarginsType.Value.CUSTOM) { |
| 356 this.setIsMarginControlsVisible_(true); |
| 357 } |
| 358 }, |
| 359 |
| 360 /** |
| 361 * Called when the mouse moves off of the component. Hides the margin |
| 362 * controls. |
| 363 * @private |
| 364 */ |
| 365 onMouseOut_: function(event) { |
| 366 var toElement = event.toElement; |
| 367 while (toElement != null) { |
| 368 if (toElement == this.getElement()) { |
| 369 return; |
| 370 } |
| 371 toElement = toElement.parentElement; |
| 372 } |
| 373 if (this.draggedControl_ != null) { |
| 374 return; |
| 375 } |
| 376 for (var orientation in this.controls_) { |
| 377 if (this.controls_[orientation].getIsFocused() || |
| 378 this.controls_[orientation].getIsInError()) { |
| 379 return; |
| 380 } |
| 381 } |
| 382 this.setIsMarginControlsVisible_(false); |
| 383 }, |
| 384 |
| 385 /** |
| 386 * Called when the print ticket changes. Updates the position of the margin |
| 387 * controls. |
| 388 * @private |
| 389 */ |
| 390 onTicketChange_: function() { |
| 391 var margins = this.printTicketStore_.getCustomMargins(); |
| 392 for (var orientation in this.controls_) { |
| 393 var control = this.controls_[orientation]; |
| 394 control.setPageSize(this.printTicketStore_.pageSize); |
| 395 control.setTextboxValue( |
| 396 this.serializeValueFromPts_(margins.get(orientation))); |
| 397 control.setPositionInPts(margins.get(orientation)); |
| 398 control.setIsInError(false); |
| 399 control.setIsEnabled(true); |
| 400 } |
| 401 this.updateClippingMask(this.clippingSize_); |
| 402 if (this.printTicketStore_.getMarginsType() != |
| 403 print_preview.ticket_items.MarginsType.Value.CUSTOM) { |
| 404 this.setIsMarginControlsVisible_(false); |
| 405 } |
| 406 }, |
| 407 |
| 408 /** |
| 409 * Called when the text in a textbox of a margin control changes or the |
| 410 * textbox loses focus. |
| 411 * Updates the print ticket store. |
| 412 * @param {!print_preview.MarginControl} control Updated control. |
| 413 * @private |
| 414 */ |
| 415 onControlTextChange_: function(control) { |
| 416 var marginValue = this.parseValueToPts_(control.getTextboxValue()); |
| 417 if (marginValue != null) { |
| 418 this.printTicketStore_.updateCustomMargin( |
| 419 control.getOrientation(), marginValue); |
| 420 } else { |
| 421 var enableOtherControls; |
| 422 if (!control.getIsFocused()) { |
| 423 // If control no longer in focus, revert to previous valid value. |
| 424 control.setTextboxValue( |
| 425 this.serializeValueFromPts_(control.getPositionInPts())); |
| 426 control.setIsInError(false); |
| 427 enableOtherControls = true; |
| 428 } else { |
| 429 control.setIsInError(true); |
| 430 enableOtherControls = false; |
| 431 } |
| 432 // Enable other controls. |
| 433 for (var o in this.controls_) { |
| 434 if (control.getOrientation() != o) { |
| 435 this.controls_[o].setIsEnabled(enableOtherControls); |
| 436 } |
| 437 } |
| 438 } |
| 439 } |
| 440 }; |
| 441 |
| 442 // Export |
| 443 return { |
| 444 MarginControlContainer: MarginControlContainer |
| 445 }; |
| 446 }); |
OLD | NEW |