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.
5 cr
.define('print_preview', function() {
9 * UI component used for setting custom print margins.
10 * @param {!print_preview.DocumentInfo} documentInfo Document data model.
11 * @param {!print_preview.ticket_items.MarginsType} marginsTypeTicketItem
12 * Used to read margins type.
13 * @param {!print_preview.ticket_items.CustomMargins} customMarginsTicketItem
14 * Used to read and write custom margin values.
15 * @param {!print_preview.MeasurementSystem} measurementSystem Used to convert
16 * between the system's local units and points.
18 * @extends {print_preview.Component}
20 function MarginControlContainer(documentInfo
, marginsTypeTicketItem
,
21 customMarginsTicketItem
, measurementSystem
) {
22 print_preview
.Component
.call(this);
25 * Document data model.
26 * @type {!print_preview.DocumentInfo}
29 this.documentInfo_
= documentInfo
;
32 * Margins type ticket item used to read predefined margins type.
34 this.marginsTypeTicketItem_
= marginsTypeTicketItem
;
37 * Custom margins ticket item used to read/write custom margin values.
38 * @type {!print_preview.ticket_items.CustomMargins}
41 this.customMarginsTicketItem_
= customMarginsTicketItem
;
44 * Used to convert between the system's local units and points.
45 * @type {!print_preview.MeasurementSystem}
48 this.measurementSystem_
= measurementSystem
;
51 * Convenience array that contains all of the margin controls.
53 * !print_preview.ticket_items.CustomMargins.Orientation,
54 * !print_preview.MarginControl>}
58 for (var key
in print_preview
.ticket_items
.CustomMargins
.Orientation
) {
59 var orientation
= print_preview
.ticket_items
.CustomMargins
.Orientation
[
61 var control
= new print_preview
.MarginControl(orientation
);
62 this.controls_
[orientation
] = control
;
63 this.addChild(control
);
67 * Margin control currently being dragged. Null if no control is being
69 * @type {print_preview.MarginControl}
72 this.draggedControl_
= null;
75 * Translation transformation in pixels to translate from the origin of the
76 * custom margins component to the top-left corner of the most visible
78 * @type {!print_preview.Coordinate2d}
81 this.translateTransform_
= new print_preview
.Coordinate2d(0, 0);
84 * Scaling transformation to scale from pixels to the units which the
85 * print preview is in. The scaling factor is the same in both dimensions,
86 * so this field is just a single number.
90 this.scaleTransform_
= 1;
93 * Clipping size for clipping the margin controls.
94 * @type {print_preview.Size}
97 this.clippingSize_
= null;
101 * CSS classes used by the custom margins component.
105 MarginControlContainer
.Classes_
= {
106 DRAGGING_HORIZONTAL
: 'margin-control-container-dragging-horizontal',
107 DRAGGING_VERTICAL
: 'margin-control-container-dragging-vertical'
111 * @param {!print_preview.ticket_items.CustomMargins.Orientation} orientation
112 * Orientation value to test.
113 * @return {boolean} Whether the given orientation is TOP or BOTTOM.
116 MarginControlContainer
.isTopOrBottom_ = function(orientation
) {
117 return orientation
==
118 print_preview
.ticket_items
.CustomMargins
.Orientation
.TOP
||
120 print_preview
.ticket_items
.CustomMargins
.Orientation
.BOTTOM
;
123 MarginControlContainer
.prototype = {
124 __proto__
: print_preview
.Component
.prototype,
127 * Updates the translation transformation that translates pixel values in
128 * the space of the HTML DOM.
129 * @param {print_preview.Coordinate2d} translateTransform Updated value of
130 * the translation transformation.
132 updateTranslationTransform: function(translateTransform
) {
133 if (!translateTransform
.equals(this.translateTransform_
)) {
134 this.translateTransform_
= translateTransform
;
135 for (var orientation
in this.controls_
) {
136 this.controls_
[orientation
].setTranslateTransform(translateTransform
);
142 * Updates the scaling transform that scales pixels values to point values.
143 * @param {number} scaleTransform Updated value of the scale transform.
145 updateScaleTransform: function(scaleTransform
) {
146 if (scaleTransform
!= this.scaleTransform_
) {
147 this.scaleTransform_
= scaleTransform
;
148 for (var orientation
in this.controls_
) {
149 this.controls_
[orientation
].setScaleTransform(scaleTransform
);
155 * Clips margin controls to the given clip size in pixels.
156 * @param {print_preview.Size} Size to clip the margin controls to.
158 updateClippingMask: function(clipSize
) {
162 this.clippingSize_
= clipSize
;
163 for (var orientation
in this.controls_
) {
164 var el
= this.controls_
[orientation
].getElement();
165 el
.style
.clip
= 'rect(' +
166 (-el
.offsetTop
) + 'px, ' +
167 (clipSize
.width
- el
.offsetLeft
) + 'px, ' +
168 (clipSize
.height
- el
.offsetTop
) + 'px, ' +
169 (-el
.offsetLeft
) + 'px)';
173 /** Shows the margin controls if the need to be shown. */
174 showMarginControlsIfNeeded: function() {
175 if (this.marginsTypeTicketItem_
.getValue() ==
176 print_preview
.ticket_items
.MarginsType
.Value
.CUSTOM
) {
177 this.setIsMarginControlsVisible_(true);
182 enterDocument: function() {
183 print_preview
.Component
.prototype.enterDocument
.call(this);
185 // We want to respond to mouse up events even beyond the component's
187 this.tracker
.add(window
, 'mouseup', this.onMouseUp_
.bind(this));
188 this.tracker
.add(window
, 'mousemove', this.onMouseMove_
.bind(this));
190 this.getElement(), 'mouseover', this.onMouseOver_
.bind(this));
192 this.getElement(), 'mouseout', this.onMouseOut_
.bind(this));
196 print_preview
.DocumentInfo
.EventType
.CHANGE
,
197 this.onTicketChange_
.bind(this));
199 this.marginsTypeTicketItem_
,
200 print_preview
.ticket_items
.TicketItem
.EventType
.CHANGE
,
201 this.onTicketChange_
.bind(this));
203 this.customMarginsTicketItem_
,
204 print_preview
.ticket_items
.TicketItem
.EventType
.CHANGE
,
205 this.onTicketChange_
.bind(this));
207 for (var orientation
in this.controls_
) {
209 this.controls_
[orientation
],
210 print_preview
.MarginControl
.EventType
.DRAG_START
,
211 this.onControlDragStart_
.bind(this, this.controls_
[orientation
]));
213 this.controls_
[orientation
],
214 print_preview
.MarginControl
.EventType
.TEXT_CHANGE
,
215 this.onControlTextChange_
.bind(this, this.controls_
[orientation
]));
220 decorateInternal: function() {
221 for (var orientation
in this.controls_
) {
222 this.controls_
[orientation
].render(this.getElement());
227 * @param {boolean} isVisible Whether the margin controls are visible.
230 setIsMarginControlsVisible_: function(isVisible
) {
231 for (var orientation
in this.controls_
) {
232 this.controls_
[orientation
].setIsVisible(isVisible
);
237 * Moves the position of the given control to the desired position in
238 * pixels within some constraint minimum and maximum.
239 * @param {!print_preview.MarginControl} control Control to move.
240 * @param {!print_preview.Coordinate2d} posInPixels Desired position to move
244 moveControlWithConstraints_: function(control
, posInPixels
) {
246 if (MarginControlContainer
.isTopOrBottom_(control
.getOrientation())) {
247 newPosInPts
= control
.convertPixelsToPts(posInPixels
.y
);
249 newPosInPts
= control
.convertPixelsToPts(posInPixels
.x
);
251 newPosInPts
= Math
.min(this.customMarginsTicketItem_
.getMarginMax(
252 control
.getOrientation()),
254 newPosInPts
= Math
.max(0, newPosInPts
);
255 newPosInPts
= Math
.round(newPosInPts
);
256 control
.setPositionInPts(newPosInPts
);
257 control
.setTextboxValue(this.serializeValueFromPts_(newPosInPts
));
261 * @param {string} value Value to parse to points. E.g. '3.40"' or '200mm'.
262 * @return {number} Value in points represented by the input value.
265 parseValueToPts_: function(value
) {
266 // Removing whitespace anywhere in the string.
267 value
= value
.replace(/\s*/g, '');
268 if (value
.length
== 0) {
271 var validationRegex
= new RegExp('^(^-?)(\\d)+(\\' +
272 this.measurementSystem_
.thousandsDelimeter
+ '\\d{3})*(\\' +
273 this.measurementSystem_
.decimalDelimeter
+ '\\d*)?' +
274 '(' + this.measurementSystem_
.unitSymbol
+ ')?$');
275 if (validationRegex
.test(value
)) {
276 // Replacing decimal point with the dot symbol in order to use
277 // parseFloat() properly.
278 var replacementRegex
=
279 new RegExp('\\' + this.measurementSystem_
.decimalDelimeter
+ '{1}');
280 value
= value
.replace(replacementRegex
, '.');
281 return this.measurementSystem_
.convertToPoints(parseFloat(value
));
287 * @param {number} value Value in points to serialize.
288 * @return {string} String representation of the value in the system's local
292 serializeValueFromPts_: function(value
) {
293 value
= this.measurementSystem_
.convertFromPoints(value
);
294 value
= this.measurementSystem_
.roundValue(value
);
295 return value
+ this.measurementSystem_
.unitSymbol
;
299 * Called when a margin control starts to drag.
300 * @param {print_preview.MarginControl} control The control which started to
304 onControlDragStart_: function(control
) {
305 this.draggedControl_
= control
;
306 this.getElement().classList
.add(
307 MarginControlContainer
.isTopOrBottom_(control
.getOrientation()) ?
308 MarginControlContainer
.Classes_
.DRAGGING_VERTICAL
:
309 MarginControlContainer
.Classes_
.DRAGGING_HORIZONTAL
);
313 * Called when the mouse moves in the custom margins component. Moves the
314 * dragged margin control.
315 * @param {MouseEvent} event Contains the position of the mouse.
318 onMouseMove_: function(event
) {
319 if (this.draggedControl_
) {
320 this.moveControlWithConstraints_(
321 this.draggedControl_
,
322 this.draggedControl_
.translateMouseToPositionInPixels(
323 new print_preview
.Coordinate2d(event
.x
, event
.y
)));
324 this.updateClippingMask(this.clippingSize_
);
329 * Called when the mouse is released in the custom margins component.
330 * Releases the dragged margin control.
331 * @param {MouseEvent} event Contains the position of the mouse.
334 onMouseUp_: function(event
) {
335 if (this.draggedControl_
) {
336 this.getElement().classList
.remove(
337 MarginControlContainer
.Classes_
.DRAGGING_VERTICAL
);
338 this.getElement().classList
.remove(
339 MarginControlContainer
.Classes_
.DRAGGING_HORIZONTAL
);
342 this.draggedControl_
.translateMouseToPositionInPixels(
343 new print_preview
.Coordinate2d(event
.x
, event
.y
));
344 this.moveControlWithConstraints_(this.draggedControl_
, posInPixels
);
346 this.updateClippingMask(this.clippingSize_
);
347 this.customMarginsTicketItem_
.updateMargin(
348 this.draggedControl_
.getOrientation(),
349 this.draggedControl_
.getPositionInPts());
350 this.draggedControl_
= null;
355 * Called when the mouse moves onto the component. Shows the margin
359 onMouseOver_: function() {
360 var fromElement
= event
.fromElement
;
361 while (fromElement
!= null) {
362 if (fromElement
== this.getElement()) {
365 fromElement
= fromElement
.parentElement
;
367 if (this.marginsTypeTicketItem_
.isCapabilityAvailable() &&
368 this.marginsTypeTicketItem_
.getValue() ==
369 print_preview
.ticket_items
.MarginsType
.Value
.CUSTOM
) {
370 this.setIsMarginControlsVisible_(true);
375 * Called when the mouse moves off of the component. Hides the margin
379 onMouseOut_: function(event
) {
380 var toElement
= event
.toElement
;
381 while (toElement
!= null) {
382 if (toElement
== this.getElement()) {
385 toElement
= toElement
.parentElement
;
387 if (this.draggedControl_
!= null) {
390 for (var orientation
in this.controls_
) {
391 if (this.controls_
[orientation
].getIsFocused() ||
392 this.controls_
[orientation
].getIsInError()) {
396 this.setIsMarginControlsVisible_(false);
400 * Called when the print ticket changes. Updates the position of the margin
404 onTicketChange_: function() {
405 var margins
= this.customMarginsTicketItem_
.getValue();
406 for (var orientation
in this.controls_
) {
407 var control
= this.controls_
[orientation
];
408 control
.setPageSize(this.documentInfo_
.pageSize
);
409 control
.setTextboxValue(
410 this.serializeValueFromPts_(margins
.get(orientation
)));
411 control
.setPositionInPts(margins
.get(orientation
));
412 control
.setIsInError(false);
413 control
.setIsEnabled(true);
415 this.updateClippingMask(this.clippingSize_
);
416 if (this.marginsTypeTicketItem_
.getValue() !=
417 print_preview
.ticket_items
.MarginsType
.Value
.CUSTOM
) {
418 this.setIsMarginControlsVisible_(false);
423 * Called when the text in a textbox of a margin control changes or the
424 * textbox loses focus.
425 * Updates the print ticket store.
426 * @param {!print_preview.MarginControl} control Updated control.
429 onControlTextChange_: function(control
) {
430 var marginValue
= this.parseValueToPts_(control
.getTextboxValue());
431 if (marginValue
!= null) {
432 this.customMarginsTicketItem_
.updateMargin(
433 control
.getOrientation(), marginValue
);
434 // Enable all controls.
435 for (var o
in this.controls_
) {
436 this.controls_
[o
].setIsEnabled(true);
438 control
.setIsInError(false);
440 var enableOtherControls
;
441 if (!control
.getIsFocused()) {
442 // If control no longer in focus, revert to previous valid value.
443 control
.setTextboxValue(
444 this.serializeValueFromPts_(control
.getPositionInPts()));
445 control
.setIsInError(false);
446 enableOtherControls
= true;
448 control
.setIsInError(true);
449 enableOtherControls
= false;
451 // Enable other controls.
452 for (var o
in this.controls_
) {
453 if (control
.getOrientation() != o
) {
454 this.controls_
[o
].setIsEnabled(enableOtherControls
);
463 MarginControlContainer
: MarginControlContainer