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.PrintTicketStore} printTicketStore Used to read and
11 * write custom margin values.
13 * @extends {print_preview.Component}
15 function MarginControlContainer(printTicketStore
) {
16 print_preview
.Component
.call(this);
19 * Used to read and write custom margin values.
20 * @type {!print_preview.PrintTicketStore}
23 this.printTicketStore_
= printTicketStore
;
26 * Used to convert between the system's local units and points.
27 * @type {!print_preview.MeasurementSystem}
30 this.measurementSystem_
= printTicketStore
.measurementSystem
;
33 * Convenience array that contains all of the margin controls.
35 * !print_preview.ticket_items.CustomMargins.Orientation,
36 * !print_preview.MarginControl>}
40 for (var key
in print_preview
.ticket_items
.CustomMargins
.Orientation
) {
41 var orientation
= print_preview
.ticket_items
.CustomMargins
.Orientation
[
43 var control
= new print_preview
.MarginControl(orientation
);
44 this.controls_
[orientation
] = control
;
45 this.addChild(control
);
49 * Margin control currently being dragged. Null if no control is being
51 * @type {print_preview.MarginControl}
54 this.draggedControl_
= null;
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
60 * @type {!print_preview.Coordinate2d}
63 this.translateTransform_
= new print_preview
.Coordinate2d(0, 0);
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.
72 this.scaleTransform_
= 1;
75 * Clipping size for clipping the margin controls.
76 * @type {print_preview.Size}
79 this.clippingSize_
= null;
83 * CSS classes used by the custom margins component.
87 MarginControlContainer
.Classes_
= {
88 DRAGGING_HORIZONTAL
: 'margin-control-container-dragging-horizontal',
89 DRAGGING_VERTICAL
: 'margin-control-container-dragging-vertical'
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.
98 MarginControlContainer
.isTopOrBottom_ = function(orientation
) {
100 print_preview
.ticket_items
.CustomMargins
.Orientation
.TOP
||
102 print_preview
.ticket_items
.CustomMargins
.Orientation
.BOTTOM
;
105 MarginControlContainer
.prototype = {
106 __proto__
: print_preview
.Component
.prototype,
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.
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
);
124 * Updates the scaling transform that scales pixels values to point values.
125 * @param {number} scaleTransform Updated value of the scale transform.
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
);
137 * Clips margin controls to the given clip size in pixels.
138 * @param {print_preview.Size} Size to clip the margin controls to.
140 updateClippingMask: function(clipSize
) {
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)';
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);
164 enterDocument: function() {
165 print_preview
.Component
.prototype.enterDocument
.call(this);
167 // We want to respond to mouse up events even beyond the component's
169 this.tracker
.add(window
, 'mouseup', this.onMouseUp_
.bind(this));
170 this.tracker
.add(window
, 'mousemove', this.onMouseMove_
.bind(this));
172 this.getElement(), 'mouseover', this.onMouseOver_
.bind(this));
174 this.getElement(), 'mouseout', this.onMouseOut_
.bind(this));
177 this.printTicketStore_
,
178 print_preview
.PrintTicketStore
.EventType
.INITIALIZE
,
179 this.onTicketChange_
.bind(this));
181 this.printTicketStore_
,
182 print_preview
.PrintTicketStore
.EventType
.TICKET_CHANGE
,
183 this.onTicketChange_
.bind(this));
185 this.printTicketStore_
,
186 print_preview
.PrintTicketStore
.EventType
.DOCUMENT_CHANGE
,
187 this.onTicketChange_
.bind(this));
189 this.printTicketStore_
,
190 print_preview
.PrintTicketStore
.EventType
.CAPABILITIES_CHANGE
,
191 this.onTicketChange_
.bind(this));
193 for (var orientation
in this.controls_
) {
195 this.controls_
[orientation
],
196 print_preview
.MarginControl
.EventType
.DRAG_START
,
197 this.onControlDragStart_
.bind(this, this.controls_
[orientation
]));
199 this.controls_
[orientation
],
200 print_preview
.MarginControl
.EventType
.TEXT_CHANGE
,
201 this.onControlTextChange_
.bind(this, this.controls_
[orientation
]));
206 decorateInternal: function() {
207 for (var orientation
in this.controls_
) {
208 this.controls_
[orientation
].render(this.getElement());
213 * @param {boolean} isVisible Whether the margin controls are visible.
216 setIsMarginControlsVisible_: function(isVisible
) {
217 for (var orientation
in this.controls_
) {
218 this.controls_
[orientation
].setIsVisible(isVisible
);
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
230 moveControlWithConstraints_: function(control
, posInPixels
) {
232 if (MarginControlContainer
.isTopOrBottom_(control
.getOrientation())) {
233 newPosInPts
= control
.convertPixelsToPts(posInPixels
.y
);
235 newPosInPts
= control
.convertPixelsToPts(posInPixels
.x
);
237 newPosInPts
= Math
.min(
238 this.printTicketStore_
.getCustomMarginMax(control
.getOrientation()),
240 newPosInPts
= Math
.max(0, newPosInPts
);
241 newPosInPts
= Math
.round(newPosInPts
);
242 control
.setPositionInPts(newPosInPts
);
243 control
.setTextboxValue(this.serializeValueFromPts_(newPosInPts
));
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.
251 parseValueToPts_: function(value
) {
252 // Removing whitespace anywhere in the string.
253 value
= value
.replace(/\s*/g, '');
254 if (value
.length
== 0) {
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
));
273 * @param {number} value Value in points to serialize.
274 * @return {string} String representation of the value in the system's local
278 serializeValueFromPts_: function(value
) {
279 value
= this.measurementSystem_
.convertFromPoints(value
);
280 value
= this.measurementSystem_
.roundValue(value
);
281 return value
+ this.measurementSystem_
.unitSymbol
;
285 * Called when a margin control starts to drag.
286 * @param {print_preview.MarginControl} control The control which started to
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
);
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.
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_
);
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.
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
);
328 this.draggedControl_
.translateMouseToPositionInPixels(
329 new print_preview
.Coordinate2d(event
.x
, event
.y
));
330 this.moveControlWithConstraints_(this.draggedControl_
, posInPixels
);
332 this.updateClippingMask(this.clippingSize_
);
333 this.printTicketStore_
.updateCustomMargin(
334 this.draggedControl_
.getOrientation(),
335 this.draggedControl_
.getPositionInPts());
336 this.draggedControl_
= null;
341 * Called when the mouse moves onto the component. Shows the margin
345 onMouseOver_: function() {
346 var fromElement
= event
.fromElement
;
347 while (fromElement
!= null) {
348 if (fromElement
== this.getElement()) {
351 fromElement
= fromElement
.parentElement
;
353 if (this.printTicketStore_
.hasMarginsCapability() &&
354 this.printTicketStore_
.getMarginsType() ==
355 print_preview
.ticket_items
.MarginsType
.Value
.CUSTOM
) {
356 this.setIsMarginControlsVisible_(true);
361 * Called when the mouse moves off of the component. Hides the margin
365 onMouseOut_: function(event
) {
366 var toElement
= event
.toElement
;
367 while (toElement
!= null) {
368 if (toElement
== this.getElement()) {
371 toElement
= toElement
.parentElement
;
373 if (this.draggedControl_
!= null) {
376 for (var orientation
in this.controls_
) {
377 if (this.controls_
[orientation
].getIsFocused() ||
378 this.controls_
[orientation
].getIsInError()) {
382 this.setIsMarginControlsVisible_(false);
386 * Called when the print ticket changes. Updates the position of the margin
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);
401 this.updateClippingMask(this.clippingSize_
);
402 if (this.printTicketStore_
.getMarginsType() !=
403 print_preview
.ticket_items
.MarginsType
.Value
.CUSTOM
) {
404 this.setIsMarginControlsVisible_(false);
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.
415 onControlTextChange_: function(control
) {
416 var marginValue
= this.parseValueToPts_(control
.getTextboxValue());
417 if (marginValue
!= null) {
418 this.printTicketStore_
.updateCustomMargin(
419 control
.getOrientation(), marginValue
);
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;
429 control
.setIsInError(true);
430 enableOtherControls
= false;
432 // Enable other controls.
433 for (var o
in this.controls_
) {
434 if (control
.getOrientation() != o
) {
435 this.controls_
[o
].setIsEnabled(enableOtherControls
);
444 MarginControlContainer
: MarginControlContainer