Move Webstore URL concepts to //extensions and out
[chromium-blink-merge.git] / chrome / browser / resources / print_preview / previewarea / margin_control_container.js
blob4e7b7319e226458294294a248bdbd035ac6bcd9e
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() {
6 'use strict';
8 /**
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.
17 * @constructor
18 * @extends {print_preview.Component}
20 function MarginControlContainer(documentInfo, marginsTypeTicketItem,
21 customMarginsTicketItem, measurementSystem) {
22 print_preview.Component.call(this);
24 /**
25 * Document data model.
26 * @type {!print_preview.DocumentInfo}
27 * @private
29 this.documentInfo_ = documentInfo;
31 /**
32 * Margins type ticket item used to read predefined margins type.
34 this.marginsTypeTicketItem_ = marginsTypeTicketItem;
36 /**
37 * Custom margins ticket item used to read/write custom margin values.
38 * @type {!print_preview.ticket_items.CustomMargins}
39 * @private
41 this.customMarginsTicketItem_ = customMarginsTicketItem;
43 /**
44 * Used to convert between the system's local units and points.
45 * @type {!print_preview.MeasurementSystem}
46 * @private
48 this.measurementSystem_ = measurementSystem;
50 /**
51 * Convenience array that contains all of the margin controls.
52 * @type {!Object.<
53 * !print_preview.ticket_items.CustomMargins.Orientation,
54 * !print_preview.MarginControl>}
55 * @private
57 this.controls_ = {};
58 for (var key in print_preview.ticket_items.CustomMargins.Orientation) {
59 var orientation = print_preview.ticket_items.CustomMargins.Orientation[
60 key];
61 var control = new print_preview.MarginControl(orientation);
62 this.controls_[orientation] = control;
63 this.addChild(control);
66 /**
67 * Margin control currently being dragged. Null if no control is being
68 * dragged.
69 * @type {print_preview.MarginControl}
70 * @private
72 this.draggedControl_ = null;
74 /**
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
77 * preview page.
78 * @type {!print_preview.Coordinate2d}
79 * @private
81 this.translateTransform_ = new print_preview.Coordinate2d(0, 0);
83 /**
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.
87 * @type {number}
88 * @private
90 this.scaleTransform_ = 1;
92 /**
93 * Clipping size for clipping the margin controls.
94 * @type {print_preview.Size}
95 * @private
97 this.clippingSize_ = null;
101 * CSS classes used by the custom margins component.
102 * @enum {string}
103 * @private
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.
114 * @private
116 MarginControlContainer.isTopOrBottom_ = function(orientation) {
117 return orientation ==
118 print_preview.ticket_items.CustomMargins.Orientation.TOP ||
119 orientation ==
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) {
159 if (!clipSize) {
160 return;
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);
181 /** @override */
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
186 // element.
187 this.tracker.add(window, 'mouseup', this.onMouseUp_.bind(this));
188 this.tracker.add(window, 'mousemove', this.onMouseMove_.bind(this));
189 this.tracker.add(
190 this.getElement(), 'mouseover', this.onMouseOver_.bind(this));
191 this.tracker.add(
192 this.getElement(), 'mouseout', this.onMouseOut_.bind(this));
194 this.tracker.add(
195 this.documentInfo_,
196 print_preview.DocumentInfo.EventType.CHANGE,
197 this.onTicketChange_.bind(this));
198 this.tracker.add(
199 this.marginsTypeTicketItem_,
200 print_preview.ticket_items.TicketItem.EventType.CHANGE,
201 this.onTicketChange_.bind(this));
202 this.tracker.add(
203 this.customMarginsTicketItem_,
204 print_preview.ticket_items.TicketItem.EventType.CHANGE,
205 this.onTicketChange_.bind(this));
207 for (var orientation in this.controls_) {
208 this.tracker.add(
209 this.controls_[orientation],
210 print_preview.MarginControl.EventType.DRAG_START,
211 this.onControlDragStart_.bind(this, this.controls_[orientation]));
212 this.tracker.add(
213 this.controls_[orientation],
214 print_preview.MarginControl.EventType.TEXT_CHANGE,
215 this.onControlTextChange_.bind(this, this.controls_[orientation]));
219 /** @override */
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.
228 * @private
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
241 * to in pixels.
242 * @private
244 moveControlWithConstraints_: function(control, posInPixels) {
245 var newPosInPts;
246 if (MarginControlContainer.isTopOrBottom_(control.getOrientation())) {
247 newPosInPts = control.convertPixelsToPts(posInPixels.y);
248 } else {
249 newPosInPts = control.convertPixelsToPts(posInPixels.x);
251 newPosInPts = Math.min(this.customMarginsTicketItem_.getMarginMax(
252 control.getOrientation()),
253 newPosInPts);
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.
263 * @private
265 parseValueToPts_: function(value) {
266 // Removing whitespace anywhere in the string.
267 value = value.replace(/\s*/g, '');
268 if (value.length == 0) {
269 return null;
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));
283 return null;
287 * @param {number} value Value in points to serialize.
288 * @return {string} String representation of the value in the system's local
289 * units.
290 * @private
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
301 * drag.
302 * @private
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.
316 * @private
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.
332 * @private
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);
340 if (event) {
341 var posInPixels =
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
356 * controls.
357 * @private
359 onMouseOver_: function() {
360 var fromElement = event.fromElement;
361 while (fromElement != null) {
362 if (fromElement == this.getElement()) {
363 return;
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
376 * controls.
377 * @private
379 onMouseOut_: function(event) {
380 var toElement = event.toElement;
381 while (toElement != null) {
382 if (toElement == this.getElement()) {
383 return;
385 toElement = toElement.parentElement;
387 if (this.draggedControl_ != null) {
388 return;
390 for (var orientation in this.controls_) {
391 if (this.controls_[orientation].getIsFocused() ||
392 this.controls_[orientation].getIsInError()) {
393 return;
396 this.setIsMarginControlsVisible_(false);
400 * Called when the print ticket changes. Updates the position of the margin
401 * controls.
402 * @private
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.
427 * @private
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);
439 } else {
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;
447 } else {
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);
461 // Export
462 return {
463 MarginControlContainer: MarginControlContainer