Updated drag and drop thumbnails.
[chromium-blink-merge.git] / chrome / browser / resources / print_preview / previewarea / margin_control_container.js
blob52fd782ee22f9fa4b3317b35b4d3b7994c1cc282
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.PrintTicketStore} printTicketStore Used to read and
11 * write custom margin values.
12 * @constructor
13 * @extends {print_preview.Component}
15 function MarginControlContainer(printTicketStore) {
16 print_preview.Component.call(this);
18 /**
19 * Used to read and write custom margin values.
20 * @type {!print_preview.PrintTicketStore}
21 * @private
23 this.printTicketStore_ = printTicketStore;
25 /**
26 * Used to convert between the system's local units and points.
27 * @type {!print_preview.MeasurementSystem}
28 * @private
30 this.measurementSystem_ = printTicketStore.measurementSystem;
32 /**
33 * Convenience array that contains all of the margin controls.
34 * @type {!Object.<
35 * !print_preview.ticket_items.CustomMargins.Orientation,
36 * !print_preview.MarginControl>}
37 * @private
39 this.controls_ = {};
40 for (var key in print_preview.ticket_items.CustomMargins.Orientation) {
41 var orientation = print_preview.ticket_items.CustomMargins.Orientation[
42 key];
43 var control = new print_preview.MarginControl(orientation);
44 this.controls_[orientation] = control;
45 this.addChild(control);
48 /**
49 * Margin control currently being dragged. Null if no control is being
50 * dragged.
51 * @type {print_preview.MarginControl}
52 * @private
54 this.draggedControl_ = null;
56 /**
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
59 * preview page.
60 * @type {!print_preview.Coordinate2d}
61 * @private
63 this.translateTransform_ = new print_preview.Coordinate2d(0, 0);
65 /**
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.
69 * @type {number}
70 * @private
72 this.scaleTransform_ = 1;
74 /**
75 * Clipping size for clipping the margin controls.
76 * @type {print_preview.Size}
77 * @private
79 this.clippingSize_ = null;
82 /**
83 * CSS classes used by the custom margins component.
84 * @enum {string}
85 * @private
87 MarginControlContainer.Classes_ = {
88 DRAGGING_HORIZONTAL: 'margin-control-container-dragging-horizontal',
89 DRAGGING_VERTICAL: 'margin-control-container-dragging-vertical'
92 /**
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.
96 * @private
98 MarginControlContainer.isTopOrBottom_ = function(orientation) {
99 return orientation ==
100 print_preview.ticket_items.CustomMargins.Orientation.TOP ||
101 orientation ==
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) {
141 if (!clipSize) {
142 return;
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);
163 /** @override */
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
168 // element.
169 this.tracker.add(window, 'mouseup', this.onMouseUp_.bind(this));
170 this.tracker.add(window, 'mousemove', this.onMouseMove_.bind(this));
171 this.tracker.add(
172 this.getElement(), 'mouseover', this.onMouseOver_.bind(this));
173 this.tracker.add(
174 this.getElement(), 'mouseout', this.onMouseOut_.bind(this));
176 this.tracker.add(
177 this.printTicketStore_,
178 print_preview.PrintTicketStore.EventType.INITIALIZE,
179 this.onTicketChange_.bind(this));
180 this.tracker.add(
181 this.printTicketStore_,
182 print_preview.PrintTicketStore.EventType.TICKET_CHANGE,
183 this.onTicketChange_.bind(this));
184 this.tracker.add(
185 this.printTicketStore_,
186 print_preview.PrintTicketStore.EventType.DOCUMENT_CHANGE,
187 this.onTicketChange_.bind(this));
188 this.tracker.add(
189 this.printTicketStore_,
190 print_preview.PrintTicketStore.EventType.CAPABILITIES_CHANGE,
191 this.onTicketChange_.bind(this));
193 for (var orientation in this.controls_) {
194 this.tracker.add(
195 this.controls_[orientation],
196 print_preview.MarginControl.EventType.DRAG_START,
197 this.onControlDragStart_.bind(this, this.controls_[orientation]));
198 this.tracker.add(
199 this.controls_[orientation],
200 print_preview.MarginControl.EventType.TEXT_CHANGE,
201 this.onControlTextChange_.bind(this, this.controls_[orientation]));
205 /** @override */
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.
214 * @private
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
227 * to in pixels.
228 * @private
230 moveControlWithConstraints_: function(control, posInPixels) {
231 var newPosInPts;
232 if (MarginControlContainer.isTopOrBottom_(control.getOrientation())) {
233 newPosInPts = control.convertPixelsToPts(posInPixels.y);
234 } else {
235 newPosInPts = control.convertPixelsToPts(posInPixels.x);
237 newPosInPts = Math.min(
238 this.printTicketStore_.getCustomMarginMax(control.getOrientation()),
239 newPosInPts);
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.
249 * @private
251 parseValueToPts_: function(value) {
252 // Removing whitespace anywhere in the string.
253 value = value.replace(/\s*/g, '');
254 if (value.length == 0) {
255 return null;
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));
269 return null;
273 * @param {number} value Value in points to serialize.
274 * @return {string} String representation of the value in the system's local
275 * units.
276 * @private
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
287 * drag.
288 * @private
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.
302 * @private
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.
318 * @private
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);
326 if (event) {
327 var posInPixels =
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
342 * controls.
343 * @private
345 onMouseOver_: function() {
346 var fromElement = event.fromElement;
347 while (fromElement != null) {
348 if (fromElement == this.getElement()) {
349 return;
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
362 * controls.
363 * @private
365 onMouseOut_: function(event) {
366 var toElement = event.toElement;
367 while (toElement != null) {
368 if (toElement == this.getElement()) {
369 return;
371 toElement = toElement.parentElement;
373 if (this.draggedControl_ != null) {
374 return;
376 for (var orientation in this.controls_) {
377 if (this.controls_[orientation].getIsFocused() ||
378 this.controls_[orientation].getIsInError()) {
379 return;
382 this.setIsMarginControlsVisible_(false);
386 * Called when the print ticket changes. Updates the position of the margin
387 * controls.
388 * @private
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.
413 * @private
415 onControlTextChange_: function(control) {
416 var marginValue = this.parseValueToPts_(control.getTextboxValue());
417 if (marginValue != null) {
418 this.printTicketStore_.updateCustomMargin(
419 control.getOrientation(), marginValue);
420 } else {
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;
428 } else {
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);
442 // Export
443 return {
444 MarginControlContainer: MarginControlContainer