Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(177)

Unified Diff: chrome/browser/resources/print_preview/data/print_ticket_store.js

Issue 10108001: Refactor print preview web ui (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Fixes broken tests Created 8 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: chrome/browser/resources/print_preview/data/print_ticket_store.js
diff --git a/chrome/browser/resources/print_preview/data/print_ticket_store.js b/chrome/browser/resources/print_preview/data/print_ticket_store.js
new file mode 100644
index 0000000000000000000000000000000000000000..137f6e3522a6ef3bb0e2ee0393859cb82a4bfe12
--- /dev/null
+++ b/chrome/browser/resources/print_preview/data/print_ticket_store.js
@@ -0,0 +1,645 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+cr.define('print_preview', function() {
+ 'use strict';
+
+ // TODO Maybe clear print ticket when destination changes. Or better yet,
+ // carry over any print ticket state that is possible. I.e. if destination
+ // changes, the new destination might not support duplex anymore, so we should
+ // clear the ticket's isDuplexEnabled state.
+
+ /**
+ * Storage of the print ticket and document statistics. Dispatches events when
+ * the contents of the print ticket or document statistics change. Also
+ * handles validation of the print ticket against destination capabilities and
+ * against the document.
+ * @constructor
+ * @extends {cr.EventTarget}
+ */
+ function PrintTicketStore() {
dpapad 2012/05/09 23:18:09 It seems that all classes under settings/ have a r
Robert Toscano 2012/05/10 00:12:05 You're right there is a singleton (it's created in
dpapad 2012/05/10 00:15:32 Sg. it does makes things cleaner. I have been usin
Robert Toscano 2012/05/10 00:23:27 We have access to the closure library from print p
dpapad 2012/05/10 02:15:03 No, we don't. I am using it outside chromium code,
Robert Toscano 2012/05/10 03:37:41 No prob.
+ cr.EventTarget.call(this);
+
+ // Create the document info with some initial settings. Actual
+ // page-related information won't be set until preview generation occurs,
+ // so we'll use some defaults until then. This way, the print ticket store
+ // will be valid even if no preview can be generated.
+ var initialPageSize = new print_preview.Size(612, 792); // 8.5"x11"
+
+ /**
+ * Information about the document to print.
+ * @type {!print_preview.DocumentInfo}
+ * @private
+ */
+ this.documentInfo_ = new print_preview.DocumentInfo(
+ true /*isModifiable*/,
+ 1 /*pageCount*/,
+ initialPageSize,
+ new print_preview.PrintableArea(
+ new print_preview.Coordinate2d(0, 0), initialPageSize));
+
+ /**
+ * Printing capabilities of Chromium and the currently selected destination.
+ * @type {!print_preview.CapabilitiesHolder}
+ * @private
+ */
+ this.capabilitiesHolder_ = new print_preview.CapabilitiesHolder(
+ new print_preview.ChromiumCapabilities(
+ true /*hasCopiesCapability*/,
+ '1' /*defaultCopiesStr*/,
+ true /*hasCollateCapability*/,
+ true /*defaultIsCollateEnabled*/,
+ true /*hasDuplexCapability*/,
+ true /*defaultIsDuplexEnabled*/,
+ true /*hasOrientationCapability*/,
+ false /*defaultIsLandscapeEnabled*/,
+ true /*hasColorCapability*/,
+ true /*defaultIsColorEnabled*/));
+
+ /**
+ * Current measurement system. Used to work with margin measurements.
+ * @type {!print_preview.MeasurementSystem}
+ * @private
+ */
+ this.measurementSystem_ = new print_preview.MeasurementSystem(
+ ',', '.', print_preview.MeasurementSystem.UnitType.IMPERIAL);
+
+ /**
+ * Collate ticket item.
+ * @type {!print_preview.ticket_items.Collate}
+ * @private
+ */
+ this.collate_ =
+ new print_preview.ticket_items.Collate(this.capabilitiesHolder_);
+
+ /**
+ * Color ticket item.
+ * @type {!print_preview.ticket_items.Color}
+ * @private
+ */
+ this.color_ =
+ new print_preview.ticket_items.Color(this.capabilitiesHolder_);
+
+ /**
+ * Copies ticket item.
+ * @type {!print_preview.ticket_items.Copies}
+ * @private
+ */
+ this.copies_ =
+ new print_preview.ticket_items.Copies(this.capabilitiesHolder_);
+
+ /**
+ * Duplex ticket item.
+ * @type {!print_preview.ticket_items.Duplex}
+ * @private
+ */
+ this.duplex_ =
+ new print_preview.ticket_items.Duplex(this.capabilitiesHolder_);
+
+ /**
+ * Landscape ticket item.
+ * @type {!print_preview.ticket_items.Landscape}
+ * @private
+ */
+ this.landscape_ = new print_preview.ticket_items.Landscape(
+ this.capabilitiesHolder_, this.documentInfo_);
+
+ /**
+ * Page range ticket item.
+ * @type {!print_preview.ticket_items.PageRange}
+ * @private
+ */
+ this.pageRange_ =
+ new print_preview.ticket_items.PageRange(this.documentInfo_);
+
+ /**
+ * Margins type ticket item.
+ * @type {!print_preview.ticket_items.MarginsType}
+ * @private
+ */
+ this.marginsType_ =
+ new print_preview.ticket_items.MarginsType(this.documentInfo_);
+
+ /**
+ * Custom margins ticket item.
+ * @type {!print_preview.ticket_items.CustomMargins}
+ * @private
+ */
+ this.customMargins_ = new print_preview.ticket_items.CustomMargins(
+ this.documentInfo_, this.measurementSystem_);
+
+ /**
+ * Header-footer ticket item.
+ * @type {!print_preview.ticket_items.HeaderFooter}
+ * @private
+ */
+ this.headerFooter_ = new print_preview.ticket_items.HeaderFooter(
+ this.documentInfo_, this.marginsType_, this.customMargins_);
+ };
+
+ /**
+ * Event types dispatched by the print ticket store.
+ * @enum {string}
+ */
+ PrintTicketStore.EventType = {
+ CAPABILITIES_CHANGE: 'print_preview.PrintTicketStore.CAPABILITIES_CHANGE',
+ DOCUMENT_CHANGE: 'print_preview.PrintTicketStore.DOCUMENT_CHANGE',
+ INITIALIZE: 'print_preview.PrintTicketStore.INITIALIZE',
+ TICKET_CHANGE: 'print_preview.PrintTicketStore.TICKET_CHANGE'
+ };
+
+ PrintTicketStore.prototype = {
+ __proto__: cr.EventTarget.prototype,
+
+ /** @return {boolean} Whether the document is modifiable. */
+ get isDocumentModifiable() {
+ return this.documentInfo_.isModifiable;
+ },
+
+ /** @return {number} Number of pages in the document. */
+ get pageCount() {
+ return this.documentInfo_.pageCount;
+ },
+
+ /**
+ * @param {number} pageCount New number of pages in the document.
+ * Dispatches a DOCUMENT_CHANGE event if the value changes.
+ */
+ updatePageCount: function(pageCount) {
+ if (this.documentInfo_.pageCount != pageCount) {
+ this.documentInfo_.pageCount = pageCount;
+ cr.dispatchSimpleEvent(
+ this, PrintTicketStore.EventType.DOCUMENT_CHANGE);
+ }
+ },
+
+ /**
+ * @return {!print_preview.PrintableArea} Printable area of the document in
+ * points.
+ */
+ get printableArea() {
+ return this.documentInfo_.printableArea;
+ },
+
+ /**
+ * @param {!print_preview.PrintableArea} printableArea New printable area of
+ * the document in points. Dispatches a DOCUMENT_CHANGE event if the
+ * value changes.
+ */
+ updatePrintableArea: function(printableArea) {
+ if (!this.documentInfo_.printableArea.equals(printableArea)) {
+ this.documentInfo_.printableArea = printableArea;
+ cr.dispatchSimpleEvent(
+ this, PrintTicketStore.EventType.DOCUMENT_CHANGE);
+ }
+ },
+
+ /** @return {!print_preview.Size} Size of the document in points. */
+ get pageSize() {
+ return this.documentInfo_.pageSize;
+ },
+
+ /**
+ * @param {!print_preview.Size} pageSize New size of the document.
+ * Dispatches a DOCUMENT_CHANGE event if the value changes.
+ */
+ updatePageSize: function(pageSize) {
+ if (!this.documentInfo_.pageSize.equals(pageSize)) {
+ this.documentInfo_.pageSize = pageSize;
+ cr.dispatchSimpleEvent(
+ this, PrintTicketStore.EventType.DOCUMENT_CHANGE);
+ }
+ },
+
+ /**
+ * @return {!print_preview.MeasurementSystem} Measurement system of the
+ * local system.
+ */
+ get measurementSystem() {
+ return this.measurementSystem_;
+ },
+
+ /**
+ * Initializes the print ticket store. Dispatches an INITIALIZE event.
+ * @param {boolean} isDocumentModifiable Whether the document to print is
+ * modifiable (i.e. can be re-flowed by Chromium).
+ * @param {?boolean} isDuplexEnabled Previous duplex setting.
+ * @param {?boolean} isHeaderFooterEnabled Previous header-footer setting.
+ * @param {?print_preview.ticket_items.MarginsType.Value} marginsType
+ * Previous margins type.
+ * @param {print_preview.Margins} customMargins Previous custom margins.
+ */
+ initialize: function(
+ isDocumentModifiable,
+ isDuplexEnabled,
+ isHeaderFooterEnabled,
+ marginsType,
+ customMargins,
+ thousandsDelimeter,
+ decimalDelimeter,
+ unitType) {
+
+ this.documentInfo_.isModifiable = isDocumentModifiable;
+ this.measurementSystem_.setSystem(
+ thousandsDelimeter, decimalDelimeter, unitType);
+
+ // Initialize ticket with user's previous values.
+ if (isDuplexEnabled != null) {
+ this.duplex_.updateValue(isDuplexEnabled);
+ }
+ if (isHeaderFooterEnabled != null) {
+ this.headerFooter_.updateValue(isHeaderFooterEnabled);
+ }
+ if (marginsType != null) {
+ this.marginsType_.updateValue(marginsType);
+ }
+ if (customMargins != null) {
+ this.customMargins_.updateValueInPts(customMargins);
+ }
+
+ cr.dispatchSimpleEvent(this, PrintTicketStore.EventType.INITIALIZE);
+ },
+
+ /**
+ * Updates the capabilities of the destination the print ticket is for.
+ * Dispatches a CAPABILITIES_CHANGE event.
+ * @param {!print_preview.ChromiumCapabilities} caps New capabilities.
+ */
+ updateDestinationCapabilities: function(caps) {
+ this.capabilitiesHolder_.set(caps);
+ cr.dispatchSimpleEvent(
+ this, PrintTicketStore.EventType.CAPABILITIES_CHANGE);
+ },
+
+
+ /** @return {boolean} Whether the ticket store has the copies capability. */
+ hasCopiesCapability: function() {
+ return this.copies_.isCapabilityAvailable();
+ },
+
+ /**
+ * @return {boolean} Whether the string representation of the copies value
+ * currently in the ticket store is valid.
+ */
+ isCopiesValid: function() {
+ return this.copies_.isValid();
+ },
+
+ isCopiesValidForValue: function(value) {
+ return this.copies_.wouldValueBeValid(value);
+ },
+
+ /** @return {number} Number of copies to print. */
+ getCopies: function() {
+ return this.copies_.getValueAsNumber();
+ },
+
+ /**
+ * @return {string} String representation of the number of copies to print.
+ */
+ getCopiesStr: function() {
+ return this.copies_.getValue();
+ },
+
+ /**
+ * Updates the string representation of the number of copies to print.
+ * Dispatches a TICKET_CHANGE event if the string value has changed.
+ * @param {string} New string representation of the number of copies to
+ * print.
+ */
+ updateCopies: function(copies) {
+ if (this.copies_.getValue() != copies) {
+ this.copies_.updateValue(copies);
+ cr.dispatchSimpleEvent(this, PrintTicketStore.EventType.TICKET_CHANGE);
+ }
+ },
+
+ /** @return {boolean} Whether the ticket store has a collate capability. */
+ hasCollateCapability: function() {
+ return this.collate_.isCapabilityAvailable();
+ },
+
+ /** @return {boolean} Whether collate is enabled. */
+ isCollateEnabled: function() {
+ return this.collate_.getValue();
+ },
+
+ /**
+ * Updates whether collate is enabled. Dispatches a TICKET_CHANGE event if
+ * collate has changed.
+ * @param {boolean} isCollateEnabled Whether collate is enabled.
+ */
+ updateCollate: function(isCollateEnabled) {
+ if (this.collate_.getValue() != isCollateEnabled) {
+ this.collate_.updateValue(isCollateEnabled);
+ cr.dispatchSimpleEvent(this, PrintTicketStore.EventType.TICKET_CHANGE);
+ }
+ },
+
+ /**
+ * @return {boolean} Whether the ticket store has color printing capability.
+ */
+ hasColorCapability: function() {
+ return this.color_.isCapabilityAvailable();
+ },
+
+ /** @return {boolean} Whether color printing is enabled. */
+ isColorEnabled: function() {
+ return this.color_.getValue();
+ },
+
+ /**
+ * Updates whether color printing is enabled. Dispatches a TICKET_CHANGE if
+ * color has changed.
+ * @param {boolean} isColorEnabled Whether the color printing is enabled.
+ */
+ updateColor: function(isColorEnabled) {
+ if (this.color_.getValue() != isColorEnabled) {
+ this.color_.updateValue(isColorEnabled);
+ cr.dispatchSimpleEvent(this, PrintTicketStore.EventType.TICKET_CHANGE);
+ }
+ },
+
+ /** @return {boolean} Whether the header-footer capability is available. */
+ hasHeaderFooterCapability: function() {
+ return this.headerFooter_.isCapabilityAvailable();
+ },
+
+ /** @return {boolean} Whether the header-footer setting is enabled. */
+ isHeaderFooterEnabled: function() {
+ return this.headerFooter_.getValue();
+ },
+
+ /**
+ * Updates the whether the header-footer setting is enabled. Dispatches a
+ * TICKET_CHANGE event if the setting changed.
+ * @param {boolean} isHeaderFooterEnabled Whether the header-footer setting
+ * is enabled.
+ */
+ updateHeaderFooter: function(isHeaderFooterEnabled) {
+ if (this.headerFooter_.getValue() != isHeaderFooterEnabled) {
+ this.headerFooter_.updateValue(isHeaderFooterEnabled);
+ cr.dispatchSimpleEvent(this, PrintTicketStore.EventType.TICKET_CHANGE);
+ }
+ },
+
+ /**
+ * @return {boolean} Whether the page orientation capability is available.
+ */
+ hasOrientationCapability: function() {
+ return this.landscape_.isCapabilityAvailable();
+ },
+
+ /**
+ * @return {boolean} Whether the document should be printed in landscape.
+ */
+ isLandscapeEnabled: function() {
+ return this.landscape_.getValue();
+ },
+
+ /**
+ * Updates whether the document should be printed in landscape. Dispatches
+ * a TICKET_CHANGE event if the setting changes.
+ * @param {boolean} isLandscapeEnabled Whether the document should be
+ * printed in landscape.
+ */
+ updateOrientation: function(isLandscapeEnabled) {
+ if (this.landscape_.getValue() != isLandscapeEnabled) {
+ this.landscape_.updateValue(isLandscapeEnabled);
+ cr.dispatchSimpleEvent(this, PrintTicketStore.EventType.TICKET_CHANGE);
+ }
+ },
+
+ /** @return {boolean} Whether the duplexing capability is available. */
+ hasDuplexCapability: function() {
+ return this.duplex_.isCapabilityAvailable();
+ },
+
+ /** @return {boolean} Whether the document should be printed in duplex. */
+ isDuplexEnabled: function() {
+ return this.duplex_.getValue();
+ },
+
+ /**
+ * Updates the duplexing setting. Dispatches a TICKET_CHANGE event if the
+ * value changes.
+ * @param {boolean} isDuplexEnabled Whether the document should be printed
+ * in duplex.
+ */
+ updateDuplex: function(isDuplexEnabled) {
+ if (this.duplex_.getValue() != isDuplexEnabled) {
+ this.duplex_.updateValue(isDuplexEnabled);
+ cr.dispatchSimpleEvent(this, PrintTicketStore.EventType.TICKET_CHANGE);
+ }
+ },
+
+ /** @return {boolean} Whether the margins capability is available. */
+ hasMarginsCapability: function() {
+ return this.marginsType_.isCapabilityAvailable();
+ },
+
+ /**
+ * @return {print_preview.ticket_items.MarginsType.Value} Type of predefined
+ * margins.
+ */
+ getMarginsType: function() {
+ return this.marginsType_.getValue();
+ },
+
+ /**
+ * Updates the type of predefined margins. Dispatches a TICKET_CHANGE event
+ * if the margins type changes.
+ * @param {print_preview.ticket_items.MarginsType.Value} marginsType Type of
+ * predefined margins.
+ */
+ updateMarginsType: function(marginsType) {
+ if (this.marginsType_.getValue() != marginsType) {
+ this.marginsType_.updateValue(marginsType);
+ cr.dispatchSimpleEvent(this, PrintTicketStore.EventType.TICKET_CHANGE);
+ }
+ },
+
+ /** @return {boolean} Whether all of the custom margins are valid. */
+ isCustomMarginsValid: function() {
+ return this.customMargins_.isValid();
+ },
+
+ /**
+ * @param {print_preview.ticket_items.CustomMargins.Orientation} orientation
+ * Specifies the margin to check if parseable.
+ * @return {boolean} Whether the specified margin is parseable.
+ */
+ isCustomMarginParseable: function(orientation) {
+ return this.customMargins_.isMarginParseable(orientation);
+ },
+
+ /** @return {!print_preview.Margins} Custom margins of the document. */
+ getCustomMarginsInPts: function() {
+ return this.customMargins_.getValueInPts();
+ },
+
+ /**
+ * @return {!print_preview.ticket_items.StringMargins} Custom margins as
+ * strings in the local measurement system.
+ */
+ getCustomMargins: function() {
+ return this.customMargins_.getValue();
+ },
+
+ /**
+ * Updates the custom margins of the document. Dispatches a TICKET_CHANGE
+ * event if the margins have changed.
+ * @param {!print_preview.Margins} marginsInPts New document page margins.
+ */
+ updateCustomMarginsInPts: function(marginsInPts) {
+ if (!this.isCustomMarginsValid() ||
+ !marginsInPts.equals(this.getCustomMarginsInPts())) {
+ this.customMargins_.updateValueInPts(marginsInPts);
+ cr.dispatchSimpleEvent(this, PrintTicketStore.EventType.TICKET_CHANGE);
+ }
+ },
+
+ /**
+ * Updates a single custom margin's value as a string in the local
+ * measurement system.
+ * @param {print_preview.ticket_items.CustomMargins.Orientation} orientation
+ * Specifies the margin to update.
+ * @param {string} value Updated string value in the local measurement
+ * system.
+ */
+ updateCustomMargin: function(orientation, value) {
+ if (this.customMargins_.getValue().get(orientation) != value) {
+ this.customMargins_.updateMargin(orientation, value);
+ cr.dispatchSimpleEvent(this, PrintTicketStore.EventType.TICKET_CHANGE);
+ }
+ },
+
+ /**
+ * Updates a single custom margin's value in points.
+ * @param {print_preview.ticket_items.CustomMargins.Orientation} orientation
+ * Specifies the margin to update.
+ * @param {number} New value in points of the specified margin.
+ */
+ updateCustomMarginInPts: function(orientation, valueInPts) {
+ if (!this.isCustomMarginsValid() ||
+ this.getCustomMarginsInPts().get(orientation) != valueInPts) {
+ this.customMargins_.updateMarginInPts(orientation, valueInPts);
+ cr.dispatchSimpleEvent(this, PrintTicketStore.EventType.TICKET_CHANGE);
+ }
+ },
+
+ /**
+ * Updates the default value of the custom margins. This value is used if
+ * the user has not edited the custom margins ticket item.
+ * @param {!print_preview.Margins} margins Updated margins value.
+ */
+ updateDefaultCustomMarginsInPts: function(margins) {
+ this.customMargins_.updateDefaultValueInPts(margins);
+ cr.dispatchSimpleEvent(this, PrintTicketStore.EventType.TICKET_CHANGE);
+ },
+
+ /**
+ * Serializes a value in points to a string in the local measurement system.
+ * @param {number} marginInPts Margin value in points.
+ * @return {string} Serialized representation of the given value in the
+ * local measurement system.
+ */
+ serializeMarginFromPts: function(marginInPts) {
+ return this.customMargins_.serializeMarginFromPts(marginInPts);
+ },
+
+ /** @return {boolean} Whether the page range capability is available. */
+ hasPageRangeCapability: function() {
+ return this.pageRange_.isCapabilityAvailable();
+ },
+
+ /**
+ * @return {boolean} Whether the current page range string is defines a
+ * valid page number set.
+ */
+ isPageRangeValid: function() {
+ return this.pageRange_.isValid();
+ },
+
+ /** @return {string} String representation of the page range. */
+ getPageRangeStr: function() {
+ return this.pageRange_.getValue();
+ },
+
+ /**
+ * @return {!print_preview.PageNumberSet} Page number set specified by the
+ * string representation of the page range string.
+ */
+ getPageNumberSet: function() {
+ return this.pageRange_.getPageNumberSet();
+ },
+
+ /**
+ * Updates the page range string. Dispatches a TICKET_CHANGE if the string
+ * changed.
+ * @param {string} pageRangeStr New page range string.
+ */
+ updatePageRange: function(pageRangeStr) {
+ if (this.pageRange_.getValue() != pageRangeStr) {
+ this.pageRange_.updateValue(pageRangeStr);
+ cr.dispatchSimpleEvent(this, PrintTicketStore.EventType.TICKET_CHANGE);
+ }
+ },
+
+ /**
+ * @return {boolean} {@code true} if the stored print ticket is valid,
+ * {@code false} otherwise.
+ */
+ isTicketValid: function() {
+ return (!this.hasCopiesCapability() || this.isCopiesValid()) &&
+ (!this.hasPageRangeCapability() || this.isPageRangeValid()) &&
+ (!this.hasMarginsCapability() ||
+ this.getMarginsType() !=
+ print_preview.ticket_items.MarginsType.Value.CUSTOM ||
+ this.isCustomMarginsValid());
+ },
+
+ /** @return {string} Serialized representation of the print ticket store. */
+ serialize: function() {
+ var state = {};
+ if (this.color_.isCapabilityAvailable() && this.color_.isUserEdited()) {
+ state['isColorEnabled'] = this.color_.getValue();
+ }
+ if (this.duplex_.isCapabilityAvailable() && this.duplex_.isUserEdited()) {
+ state['isDuplexEnabled'] = this.duplex_.getValue();
+ }
+ if (this.landscape_.isCapabilityAvailable() &&
+ this.landscape_.isUserEdited()) {
+ state['isLandscapeEnabled'] = this.landscape_.getValue();
+ }
+ if (this.headerFooter_.isCapabilityAvailable() &&
+ this.headerFooter_.isUserEdited()) {
+ state['isHeaderFooterEnabled'] = this.headerFooter_.getValue();
+ }
+ if (this.marginsType_.isCapabilityAvailable() &&
+ this.marginsType_.isUserEdited()) {
+ state['marginsType'] = this.marginsType_.getValue();
+ if (this.marginsType_.getValue() ==
+ print_preview.ticket_items.MarginsType.Value.CUSTOM &&
+ this.customMargins_.isUserEdited()) {
+ var customMarginsInPts = this.customMargins_.getValueInPts();
+ var orientationEnum =
+ print_preview.ticket_items.CustomMargins.Orientation;
+ state['customMargins'] = {
+ 'top': customMarginsInPts.get(orientationEnum.TOP),
+ 'right': customMarginsInPts.get(orientationEnum.RIGHT),
+ 'bottom': customMarginsInPts.get(orientationEnum.BOTTOM),
+ 'left': customMarginsInPts.get(orientationEnum.LEFT)
+ };
+ }
+ }
+ return JSON.stringify(state);
+ }
+ };
+
+ // Export
+ return {
+ PrintTicketStore: PrintTicketStore
+ };
+});

Powered by Google App Engine
This is Rietveld 408576698