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);
435 var enableOtherControls;
436 if (!control.getIsFocused()) {
437 // If control no longer in focus, revert to previous valid value.
438 control.setTextboxValue(
439 this.serializeValueFromPts_(control.getPositionInPts()));
440 control.setIsInError(false);
441 enableOtherControls = true;
443 control.setIsInError(true);
444 enableOtherControls = false;
446 // Enable other controls.
447 for (var o in this.controls_) {
448 if (control.getOrientation() != o) {
449 this.controls_[o].setIsEnabled(enableOtherControls);
458 MarginControlContainer: MarginControlContainer