Index: chrome/browser/resources/print_preview/previewarea/margin_control_container.js |
diff --git a/chrome/browser/resources/print_preview/previewarea/margin_control_container.js b/chrome/browser/resources/print_preview/previewarea/margin_control_container.js |
new file mode 100644 |
index 0000000000000000000000000000000000000000..1209da930fb08ff2f74c960c2876b3c72a97cee9 |
--- /dev/null |
+++ b/chrome/browser/resources/print_preview/previewarea/margin_control_container.js |
@@ -0,0 +1,446 @@ |
+// 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.PrintTicketStore} printTicketStore Used to read and |
+ * write custom margin values. |
+ * @constructor |
+ * @extends {print_preview.Component} |
+ */ |
+ function MarginControlContainer(printTicketStore) { |
+ print_preview.Component.call(this); |
+ |
+ /** |
+ * Used to read and write custom margin values. |
+ * @type {!print_preview.PrintTicketStore} |
+ * @private |
+ */ |
+ this.printTicketStore_ = printTicketStore; |
+ |
+ /** |
+ * Used to convert between the system's local units and points. |
+ * @type {!print_preview.MeasurementSystem} |
+ * @private |
+ */ |
+ this.measurementSystem_ = printTicketStore.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.printTicketStore_.getMarginsType() == |
+ 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.printTicketStore_, |
+ print_preview.PrintTicketStore.EventType.INITIALIZE, |
+ this.onTicketChange_.bind(this)); |
+ this.tracker.add( |
+ this.printTicketStore_, |
+ print_preview.PrintTicketStore.EventType.TICKET_CHANGE, |
+ this.onTicketChange_.bind(this)); |
+ this.tracker.add( |
+ this.printTicketStore_, |
+ print_preview.PrintTicketStore.EventType.DOCUMENT_CHANGE, |
+ this.onTicketChange_.bind(this)); |
+ this.tracker.add( |
+ this.printTicketStore_, |
+ print_preview.PrintTicketStore.EventType.CAPABILITIES_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.printTicketStore_.getCustomMarginMax(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.printTicketStore_.updateCustomMargin( |
+ 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.printTicketStore_.hasMarginsCapability() && |
+ this.printTicketStore_.getMarginsType() == |
+ 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.printTicketStore_.getCustomMargins(); |
+ for (var orientation in this.controls_) { |
+ var control = this.controls_[orientation]; |
+ control.setPageSize(this.printTicketStore_.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.printTicketStore_.getMarginsType() != |
+ 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.printTicketStore_.updateCustomMargin( |
+ 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 |
+ }; |
+}); |