| // 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'; |
| |
| /** |
| * UI component used for setting custom print margins. |
| * @param {!print_preview.DocumentInfo} documentInfo Document data model. |
| * @param {!print_preview.ticket_items.MarginsType} marginsTypeTicketItem |
| * Used to read margins type. |
| * @param {!print_preview.ticket_items.CustomMargins} customMarginsTicketItem |
| * Used to read and write custom margin values. |
| * @param {!print_preview.MeasurementSystem} measurementSystem Used to convert |
| * between the system's local units and points. |
| * @constructor |
| * @extends {print_preview.Component} |
| */ |
| function MarginControlContainer(documentInfo, marginsTypeTicketItem, |
| customMarginsTicketItem, measurementSystem) { |
| print_preview.Component.call(this); |
| |
| /** |
| * Document data model. |
| * @type {!print_preview.DocumentInfo} |
| * @private |
| */ |
| this.documentInfo_ = documentInfo; |
| |
| /** |
| * Margins type ticket item used to read predefined margins type. |
| */ |
| this.marginsTypeTicketItem_ = marginsTypeTicketItem; |
| |
| /** |
| * Custom margins ticket item used to read/write custom margin values. |
| * @type {!print_preview.ticket_items.CustomMargins} |
| * @private |
| */ |
| this.customMarginsTicketItem_ = customMarginsTicketItem; |
| |
| /** |
| * Used to convert between the system's local units and points. |
| * @type {!print_preview.MeasurementSystem} |
| * @private |
| */ |
| this.measurementSystem_ = measurementSystem; |
| |
| /** |
| * Convenience array that contains all of the margin controls. |
| * @type {!Object.< |
| * !print_preview.ticket_items.CustomMargins.Orientation, |
| * !print_preview.MarginControl>} |
| * @private |
| */ |
| this.controls_ = {}; |
| for (var key in print_preview.ticket_items.CustomMargins.Orientation) { |
| var orientation = print_preview.ticket_items.CustomMargins.Orientation[ |
| key]; |
| var control = new print_preview.MarginControl(orientation); |
| this.controls_[orientation] = control; |
| this.addChild(control); |
| } |
| |
| /** |
| * Margin control currently being dragged. Null if no control is being |
| * dragged. |
| * @type {print_preview.MarginControl} |
| * @private |
| */ |
| this.draggedControl_ = null; |
| |
| /** |
| * Translation transformation in pixels to translate from the origin of the |
| * custom margins component to the top-left corner of the most visible |
| * preview page. |
| * @type {!print_preview.Coordinate2d} |
| * @private |
| */ |
| this.translateTransform_ = new print_preview.Coordinate2d(0, 0); |
| |
| /** |
| * Scaling transformation to scale from pixels to the units which the |
| * print preview is in. The scaling factor is the same in both dimensions, |
| * so this field is just a single number. |
| * @type {number} |
| * @private |
| */ |
| this.scaleTransform_ = 1; |
| |
| /** |
| * Clipping size for clipping the margin controls. |
| * @type {print_preview.Size} |
| * @private |
| */ |
| this.clippingSize_ = null; |
| }; |
| |
| /** |
| * CSS classes used by the custom margins component. |
| * @enum {string} |
| * @private |
| */ |
| MarginControlContainer.Classes_ = { |
| DRAGGING_HORIZONTAL: 'margin-control-container-dragging-horizontal', |
| DRAGGING_VERTICAL: 'margin-control-container-dragging-vertical' |
| }; |
| |
| /** |
| * @param {!print_preview.ticket_items.CustomMargins.Orientation} orientation |
| * Orientation value to test. |
| * @return {boolean} Whether the given orientation is TOP or BOTTOM. |
| * @private |
| */ |
| MarginControlContainer.isTopOrBottom_ = function(orientation) { |
| return orientation == |
| print_preview.ticket_items.CustomMargins.Orientation.TOP || |
| orientation == |
| print_preview.ticket_items.CustomMargins.Orientation.BOTTOM; |
| }; |
| |
| MarginControlContainer.prototype = { |
| __proto__: print_preview.Component.prototype, |
| |
| /** |
| * Updates the translation transformation that translates pixel values in |
| * the space of the HTML DOM. |
| * @param {print_preview.Coordinate2d} translateTransform Updated value of |
| * the translation transformation. |
| */ |
| updateTranslationTransform: function(translateTransform) { |
| if (!translateTransform.equals(this.translateTransform_)) { |
| this.translateTransform_ = translateTransform; |
| for (var orientation in this.controls_) { |
| this.controls_[orientation].setTranslateTransform(translateTransform); |
| } |
| } |
| }, |
| |
| /** |
| * Updates the scaling transform that scales pixels values to point values. |
| * @param {number} scaleTransform Updated value of the scale transform. |
| */ |
| updateScaleTransform: function(scaleTransform) { |
| if (scaleTransform != this.scaleTransform_) { |
| this.scaleTransform_ = scaleTransform; |
| for (var orientation in this.controls_) { |
| this.controls_[orientation].setScaleTransform(scaleTransform); |
| } |
| } |
| }, |
| |
| /** |
| * Clips margin controls to the given clip size in pixels. |
| * @param {print_preview.Size} Size to clip the margin controls to. |
| */ |
| updateClippingMask: function(clipSize) { |
| if (!clipSize) { |
| return; |
| } |
| this.clippingSize_ = clipSize; |
| for (var orientation in this.controls_) { |
| var el = this.controls_[orientation].getElement(); |
| el.style.clip = 'rect(' + |
| (-el.offsetTop) + 'px, ' + |
| (clipSize.width - el.offsetLeft) + 'px, ' + |
| (clipSize.height - el.offsetTop) + 'px, ' + |
| (-el.offsetLeft) + 'px)'; |
| } |
| }, |
| |
| /** Shows the margin controls if the need to be shown. */ |
| showMarginControlsIfNeeded: function() { |
| if (this.marginsTypeTicketItem_.getValue() == |
| print_preview.ticket_items.MarginsType.Value.CUSTOM) { |
| this.setIsMarginControlsVisible_(true); |
| } |
| }, |
| |
| /** @override */ |
| enterDocument: function() { |
| print_preview.Component.prototype.enterDocument.call(this); |
| |
| // We want to respond to mouse up events even beyond the component's |
| // element. |
| this.tracker.add(window, 'mouseup', this.onMouseUp_.bind(this)); |
| this.tracker.add(window, 'mousemove', this.onMouseMove_.bind(this)); |
| this.tracker.add( |
| this.getElement(), 'mouseover', this.onMouseOver_.bind(this)); |
| this.tracker.add( |
| this.getElement(), 'mouseout', this.onMouseOut_.bind(this)); |
| |
| this.tracker.add( |
| this.documentInfo_, |
| print_preview.DocumentInfo.EventType.CHANGE, |
| this.onTicketChange_.bind(this)); |
| this.tracker.add( |
| this.marginsTypeTicketItem_, |
| print_preview.ticket_items.TicketItem.EventType.CHANGE, |
| this.onTicketChange_.bind(this)); |
| this.tracker.add( |
| this.customMarginsTicketItem_, |
| print_preview.ticket_items.TicketItem.EventType.CHANGE, |
| this.onTicketChange_.bind(this)); |
| |
| for (var orientation in this.controls_) { |
| this.tracker.add( |
| this.controls_[orientation], |
| print_preview.MarginControl.EventType.DRAG_START, |
| this.onControlDragStart_.bind(this, this.controls_[orientation])); |
| this.tracker.add( |
| this.controls_[orientation], |
| print_preview.MarginControl.EventType.TEXT_CHANGE, |
| this.onControlTextChange_.bind(this, this.controls_[orientation])); |
| } |
| }, |
| |
| /** @override */ |
| decorateInternal: function() { |
| for (var orientation in this.controls_) { |
| this.controls_[orientation].render(this.getElement()); |
| } |
| }, |
| |
| /** |
| * @param {boolean} isVisible Whether the margin controls are visible. |
| * @private |
| */ |
| setIsMarginControlsVisible_: function(isVisible) { |
| for (var orientation in this.controls_) { |
| this.controls_[orientation].setIsVisible(isVisible); |
| } |
| }, |
| |
| /** |
| * Moves the position of the given control to the desired position in |
| * pixels within some constraint minimum and maximum. |
| * @param {!print_preview.MarginControl} control Control to move. |
| * @param {!print_preview.Coordinate2d} posInPixels Desired position to move |
| * to in pixels. |
| * @private |
| */ |
| moveControlWithConstraints_: function(control, posInPixels) { |
| var newPosInPts; |
| if (MarginControlContainer.isTopOrBottom_(control.getOrientation())) { |
| newPosInPts = control.convertPixelsToPts(posInPixels.y); |
| } else { |
| newPosInPts = control.convertPixelsToPts(posInPixels.x); |
| } |
| newPosInPts = Math.min(this.customMarginsTicketItem_.getMarginMax( |
| control.getOrientation()), |
| newPosInPts); |
| newPosInPts = Math.max(0, newPosInPts); |
| newPosInPts = Math.round(newPosInPts); |
| control.setPositionInPts(newPosInPts); |
| control.setTextboxValue(this.serializeValueFromPts_(newPosInPts)); |
| }, |
| |
| /** |
| * @param {string} value Value to parse to points. E.g. '3.40"' or '200mm'. |
| * @return {number} Value in points represented by the input value. |
| * @private |
| */ |
| parseValueToPts_: function(value) { |
| // Removing whitespace anywhere in the string. |
| value = value.replace(/\s*/g, ''); |
| if (value.length == 0) { |
| return null; |
| } |
| var validationRegex = new RegExp('^(^-?)(\\d)+(\\' + |
| this.measurementSystem_.thousandsDelimeter + '\\d{3})*(\\' + |
| this.measurementSystem_.decimalDelimeter + '\\d*)?' + |
| '(' + this.measurementSystem_.unitSymbol + ')?$'); |
| if (validationRegex.test(value)) { |
| // Replacing decimal point with the dot symbol in order to use |
| // parseFloat() properly. |
| var replacementRegex = |
| new RegExp('\\' + this.measurementSystem_.decimalDelimeter + '{1}'); |
| value = value.replace(replacementRegex, '.'); |
| return this.measurementSystem_.convertToPoints(parseFloat(value)); |
| } |
| return null; |
| }, |
| |
| /** |
| * @param {number} value Value in points to serialize. |
| * @return {string} String representation of the value in the system's local |
| * units. |
| * @private |
| */ |
| serializeValueFromPts_: function(value) { |
| value = this.measurementSystem_.convertFromPoints(value); |
| value = this.measurementSystem_.roundValue(value); |
| return value + this.measurementSystem_.unitSymbol; |
| }, |
| |
| /** |
| * Called when a margin control starts to drag. |
| * @param {print_preview.MarginControl} control The control which started to |
| * drag. |
| * @private |
| */ |
| onControlDragStart_: function(control) { |
| this.draggedControl_ = control; |
| this.getElement().classList.add( |
| MarginControlContainer.isTopOrBottom_(control.getOrientation()) ? |
| MarginControlContainer.Classes_.DRAGGING_VERTICAL : |
| MarginControlContainer.Classes_.DRAGGING_HORIZONTAL); |
| }, |
| |
| /** |
| * Called when the mouse moves in the custom margins component. Moves the |
| * dragged margin control. |
| * @param {MouseEvent} event Contains the position of the mouse. |
| * @private |
| */ |
| onMouseMove_: function(event) { |
| if (this.draggedControl_) { |
| this.moveControlWithConstraints_( |
| this.draggedControl_, |
| this.draggedControl_.translateMouseToPositionInPixels( |
| new print_preview.Coordinate2d(event.x, event.y))); |
| this.updateClippingMask(this.clippingSize_); |
| } |
| }, |
| |
| /** |
| * Called when the mouse is released in the custom margins component. |
| * Releases the dragged margin control. |
| * @param {MouseEvent} event Contains the position of the mouse. |
| * @private |
| */ |
| onMouseUp_: function(event) { |
| if (this.draggedControl_) { |
| this.getElement().classList.remove( |
| MarginControlContainer.Classes_.DRAGGING_VERTICAL); |
| this.getElement().classList.remove( |
| MarginControlContainer.Classes_.DRAGGING_HORIZONTAL); |
| if (event) { |
| var posInPixels = |
| this.draggedControl_.translateMouseToPositionInPixels( |
| new print_preview.Coordinate2d(event.x, event.y)); |
| this.moveControlWithConstraints_(this.draggedControl_, posInPixels); |
| } |
| this.updateClippingMask(this.clippingSize_); |
| this.customMarginsTicketItem_.updateMargin( |
| this.draggedControl_.getOrientation(), |
| this.draggedControl_.getPositionInPts()); |
| this.draggedControl_ = null; |
| } |
| }, |
| |
| /** |
| * Called when the mouse moves onto the component. Shows the margin |
| * controls. |
| * @private |
| */ |
| onMouseOver_: function() { |
| var fromElement = event.fromElement; |
| while (fromElement != null) { |
| if (fromElement == this.getElement()) { |
| return; |
| } |
| fromElement = fromElement.parentElement; |
| } |
| if (this.marginsTypeTicketItem_.isCapabilityAvailable() && |
| this.marginsTypeTicketItem_.getValue() == |
| print_preview.ticket_items.MarginsType.Value.CUSTOM) { |
| this.setIsMarginControlsVisible_(true); |
| } |
| }, |
| |
| /** |
| * Called when the mouse moves off of the component. Hides the margin |
| * controls. |
| * @private |
| */ |
| onMouseOut_: function(event) { |
| var toElement = event.toElement; |
| while (toElement != null) { |
| if (toElement == this.getElement()) { |
| return; |
| } |
| toElement = toElement.parentElement; |
| } |
| if (this.draggedControl_ != null) { |
| return; |
| } |
| for (var orientation in this.controls_) { |
| if (this.controls_[orientation].getIsFocused() || |
| this.controls_[orientation].getIsInError()) { |
| return; |
| } |
| } |
| this.setIsMarginControlsVisible_(false); |
| }, |
| |
| /** |
| * Called when the print ticket changes. Updates the position of the margin |
| * controls. |
| * @private |
| */ |
| onTicketChange_: function() { |
| var margins = this.customMarginsTicketItem_.getValue(); |
| for (var orientation in this.controls_) { |
| var control = this.controls_[orientation]; |
| control.setPageSize(this.documentInfo_.pageSize); |
| control.setTextboxValue( |
| this.serializeValueFromPts_(margins.get(orientation))); |
| control.setPositionInPts(margins.get(orientation)); |
| control.setIsInError(false); |
| control.setIsEnabled(true); |
| } |
| this.updateClippingMask(this.clippingSize_); |
| if (this.marginsTypeTicketItem_.getValue() != |
| print_preview.ticket_items.MarginsType.Value.CUSTOM) { |
| this.setIsMarginControlsVisible_(false); |
| } |
| }, |
| |
| /** |
| * Called when the text in a textbox of a margin control changes or the |
| * textbox loses focus. |
| * Updates the print ticket store. |
| * @param {!print_preview.MarginControl} control Updated control. |
| * @private |
| */ |
| onControlTextChange_: function(control) { |
| var marginValue = this.parseValueToPts_(control.getTextboxValue()); |
| if (marginValue != null) { |
| this.customMarginsTicketItem_.updateMargin( |
| control.getOrientation(), marginValue); |
| } else { |
| var enableOtherControls; |
| if (!control.getIsFocused()) { |
| // If control no longer in focus, revert to previous valid value. |
| control.setTextboxValue( |
| this.serializeValueFromPts_(control.getPositionInPts())); |
| control.setIsInError(false); |
| enableOtherControls = true; |
| } else { |
| control.setIsInError(true); |
| enableOtherControls = false; |
| } |
| // Enable other controls. |
| for (var o in this.controls_) { |
| if (control.getOrientation() != o) { |
| this.controls_[o].setIsEnabled(enableOtherControls); |
| } |
| } |
| } |
| } |
| }; |
| |
| // Export |
| return { |
| MarginControlContainer: MarginControlContainer |
| }; |
| }); |