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

Side by Side Diff: third_party/WebKit/Source/devtools/front_end/devtools.js

Issue 2449343003: [Devtools] Cleanup devtools.js and typecast for jsdoc (Closed)
Patch Set: changes Created 4 years, 1 month 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 unified diff | Download patch
« no previous file with comments | « no previous file | third_party/WebKit/Source/devtools/front_end/externs.js » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright 2014 The Chromium Authors. All rights reserved. 1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 /* eslint-disable indent */ 5 /* eslint-disable indent */
6 (function(window) { 6 (function(window) {
7 7
8 // DevToolsAPI ---------------------------------------------------------------- 8 // DevToolsAPI ----------------------------------------------------------------
9 9
10 /** 10 /**
(...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after
47 if (callback) 47 if (callback)
48 this._callbacks[callId] = callback; 48 this._callbacks[callId] = callback;
49 var message = { "id": callId, "method": method }; 49 var message = { "id": callId, "method": method };
50 if (args.length) 50 if (args.length)
51 message.params = args; 51 message.params = args;
52 DevToolsHost.sendMessageToEmbedder(JSON.stringify(message)); 52 DevToolsHost.sendMessageToEmbedder(JSON.stringify(message));
53 }, 53 },
54 54
55 /** 55 /**
56 * @param {string} method 56 * @param {string} method
57 * @param {!Array.<*>} args 57 * @param {!Array<*>} args
58 */ 58 */
59 _dispatchOnInspectorFrontendAPI: function(method, args) 59 _dispatchOnInspectorFrontendAPI: function(method, args)
60 { 60 {
61 var api = window["InspectorFrontendAPI"]; 61 const inspectorFrontendAPI = /** @type {!Object<string, function()>} */ (window["InspectorFrontendAPI"]);
62 api[method].apply(api, args); 62 inspectorFrontendAPI[method].apply(inspectorFrontendAPI, args);
63 }, 63 },
64 64
65 // API methods below this line -------------------------------------------- 65 // API methods below this line --------------------------------------------
66 66
67 /** 67 /**
68 * @param {!Array.<!ExtensionDescriptor>} extensions 68 * @param {!Array.<!ExtensionDescriptor>} extensions
69 */ 69 */
70 addExtensions: function(extensions) 70 addExtensions: function(extensions)
71 { 71 {
72 // Support for legacy front-ends (<M41). 72 // Support for legacy front-ends (<M41).
73 if (window["WebInspector"].addExtensions) 73 if (window["WebInspector"]["addExtensions"])
74 window["WebInspector"].addExtensions(extensions); 74 window["WebInspector"]["addExtensions"](extensions);
75 else 75 else
76 this._dispatchOnInspectorFrontendAPI("addExtensions", [extensions]); 76 this._dispatchOnInspectorFrontendAPI("addExtensions", [extensions]);
77 }, 77 },
78 78
79 /** 79 /**
80 * @param {string} url 80 * @param {string} url
81 */ 81 */
82 appendedToURL: function(url) 82 appendedToURL: function(url)
83 { 83 {
84 this._dispatchOnInspectorFrontendAPI("appendedToURL", [url]); 84 this._dispatchOnInspectorFrontendAPI("appendedToURL", [url]);
(...skipping 190 matching lines...) Expand 10 before | Expand all | Expand 10 after
275 { 275 {
276 this._dispatchOnInspectorFrontendAPI("searchCompleted", [requestId, file SystemPath, files]); 276 this._dispatchOnInspectorFrontendAPI("searchCompleted", [requestId, file SystemPath, files]);
277 }, 277 },
278 278
279 /** 279 /**
280 * @param {string} tabId 280 * @param {string} tabId
281 */ 281 */
282 setInspectedTabId: function(tabId) 282 setInspectedTabId: function(tabId)
283 { 283 {
284 // Support for legacy front-ends (<M41). 284 // Support for legacy front-ends (<M41).
285 if (window["WebInspector"].setInspectedTabId) 285 if (window["WebInspector"]["setInspectedTabId"])
286 window["WebInspector"].setInspectedTabId(tabId); 286 window["WebInspector"]["setInspectedTabId"](tabId);
287 else 287 else
288 this._dispatchOnInspectorFrontendAPI("setInspectedTabId", [tabId]); 288 this._dispatchOnInspectorFrontendAPI("setInspectedTabId", [tabId]);
289 }, 289 },
290 290
291 /** 291 /**
292 * @param {boolean} useSoftMenu 292 * @param {boolean} useSoftMenu
293 */ 293 */
294 setUseSoftMenu: function(useSoftMenu) 294 setUseSoftMenu: function(useSoftMenu)
295 { 295 {
296 this._dispatchOnInspectorFrontendAPI("setUseSoftMenu", [useSoftMenu]); 296 this._dispatchOnInspectorFrontendAPI("setUseSoftMenu", [useSoftMenu]);
(...skipping 600 matching lines...) Expand 10 before | Expand all | Expand 10 after
897 this.recordEnumeratedHistogram("DevTools.PanelShown", panelCode, 20); 897 this.recordEnumeratedHistogram("DevTools.PanelShown", panelCode, 20);
898 } 898 }
899 }; 899 };
900 900
901 window.InspectorFrontendHost = new InspectorFrontendHostImpl(); 901 window.InspectorFrontendHost = new InspectorFrontendHostImpl();
902 902
903 // DevToolsApp --------------------------------------------------------------- 903 // DevToolsApp ---------------------------------------------------------------
904 904
905 function installObjectObserve() 905 function installObjectObserve()
906 { 906 {
907 /** @type {!Array<string>} */
907 var properties = [ 908 var properties = [
908 "advancedSearchConfig", "auditsPanelSplitViewState", "auditsSidebarWidth ", "blockedURLs", "breakpoints", "cacheDisabled", "colorFormat", "consoleHistory ", 909 "advancedSearchConfig", "auditsPanelSplitViewState", "auditsSidebarWidth ", "blockedURLs", "breakpoints", "cacheDisabled", "colorFormat", "consoleHistory ",
909 "consoleTimestampsEnabled", "cpuProfilerView", "cssSourceMapsEnabled", " currentDockState", "customColorPalette", "customDevicePresets", "customEmulatedD eviceList", 910 "consoleTimestampsEnabled", "cpuProfilerView", "cssSourceMapsEnabled", " currentDockState", "customColorPalette", "customDevicePresets", "customEmulatedD eviceList",
910 "customFormatters", "customUserAgent", "databaseTableViewVisibleColumns" , "dataGrid-cookiesTable", "dataGrid-DOMStorageItemsView", "debuggerSidebarHidde n", "disableDataSaverInfobar", 911 "customFormatters", "customUserAgent", "databaseTableViewVisibleColumns" , "dataGrid-cookiesTable", "dataGrid-DOMStorageItemsView", "debuggerSidebarHidde n", "disableDataSaverInfobar",
911 "disablePausedStateOverlay", "domBreakpoints", "domWordWrap", "elementsP anelSplitViewState", "elementsSidebarWidth", "emulation.deviceHeight", "emulatio n.deviceModeValue", 912 "disablePausedStateOverlay", "domBreakpoints", "domWordWrap", "elementsP anelSplitViewState", "elementsSidebarWidth", "emulation.deviceHeight", "emulatio n.deviceModeValue",
912 "emulation.deviceOrientationOverride", "emulation.deviceScale", "emulati on.deviceScaleFactor", "emulation.deviceUA", "emulation.deviceWidth", "emulation .geolocationOverride", 913 "emulation.deviceOrientationOverride", "emulation.deviceScale", "emulati on.deviceScaleFactor", "emulation.deviceUA", "emulation.deviceWidth", "emulation .geolocationOverride",
913 "emulation.showDeviceMode", "emulation.showRulers", "enableAsyncStackTra ces", "eventListenerBreakpoints", "fileMappingEntries", "fileSystemMapping", "Fi leSystemViewSidebarWidth", 914 "emulation.showDeviceMode", "emulation.showRulers", "enableAsyncStackTra ces", "eventListenerBreakpoints", "fileMappingEntries", "fileSystemMapping", "Fi leSystemViewSidebarWidth",
914 "fileSystemViewSplitViewState", "filterBar-consoleView", "filterBar-netw orkPanel", "filterBar-promisePane", "filterBar-timelinePanel", "frameViewerHideC hromeWindow", 915 "fileSystemViewSplitViewState", "filterBar-consoleView", "filterBar-netw orkPanel", "filterBar-promisePane", "filterBar-timelinePanel", "frameViewerHideC hromeWindow",
915 "heapSnapshotRetainersViewSize", "heapSnapshotSplitViewState", "hideColl ectedPromises", "hideNetworkMessages", "highlightNodeOnHoverInOverlay", "highRes olutionCpuProfiling", 916 "heapSnapshotRetainersViewSize", "heapSnapshotSplitViewState", "hideColl ectedPromises", "hideNetworkMessages", "highlightNodeOnHoverInOverlay", "highRes olutionCpuProfiling",
916 "inlineVariableValues", "Inspector.drawerSplitView", "Inspector.drawerSp litViewState", "InspectorView.panelOrder", "InspectorView.screencastSplitView", 917 "inlineVariableValues", "Inspector.drawerSplitView", "Inspector.drawerSp litViewState", "InspectorView.panelOrder", "InspectorView.screencastSplitView",
(...skipping 18 matching lines...) Expand all
935 "workspaceExcludedFolders", "workspaceFolderExcludePattern", "workspaceI nfobarDisabled", "workspaceMappingInfobarDisabled", "xhrBreakpoints"]; 936 "workspaceExcludedFolders", "workspaceFolderExcludePattern", "workspaceI nfobarDisabled", "workspaceMappingInfobarDisabled", "xhrBreakpoints"];
936 937
937 /** 938 /**
938 * @this {!{_storage: Object, _name: string}} 939 * @this {!{_storage: Object, _name: string}}
939 */ 940 */
940 function settingRemove() 941 function settingRemove()
941 { 942 {
942 this._storage[this._name] = undefined; 943 this._storage[this._name] = undefined;
943 } 944 }
944 945
946 /**
947 * @param {!Object} object
948 * @param {function(!Array<!{name: string}>)} observer
949 */
945 function objectObserve(object, observer) 950 function objectObserve(object, observer)
946 { 951 {
947 if (window["WebInspector"]) { 952 if (window["WebInspector"]) {
948 var settingPrototype = window["WebInspector"]["Setting"]["prototype" ]; 953 var settingPrototype = /** @type {!Object} */ (window["WebInspector" ]["Setting"]["prototype"]);
949 if (typeof settingPrototype["remove"] === "function") 954 if (typeof settingPrototype["remove"] === "function")
950 settingPrototype["remove"] = settingRemove; 955 settingPrototype["remove"] = settingRemove;
951 } 956 }
952 957 /** @type {!Set<string>} */
953 var changedProperties = new Set(); 958 var changedProperties = new Set();
954 var scheduled = false; 959 var scheduled = false;
955 960
956 function scheduleObserver() 961 function scheduleObserver()
957 { 962 {
958 if (!scheduled) { 963 if (scheduled)
959 scheduled = true; 964 return;
960 setImmediate(callObserver); 965 scheduled = true;
961 } 966 setImmediate(callObserver);
962 } 967 }
963 968
964 function callObserver() 969 function callObserver()
965 { 970 {
966 scheduled = false; 971 scheduled = false;
967 var changes = []; 972 var changes = /** @type {!Array<!{name: string}>} */ ([]);
968 changedProperties.forEach(function(name) { changes.push({name: name} ); }); 973 changedProperties.forEach(function(name) { changes.push({name: name} ); });
969 changedProperties.clear(); 974 changedProperties.clear();
970 observer.call(null, changes); 975 observer.call(null, changes);
971 } 976 }
972 977
978 /** @type {!Map<string, *>} */
973 var storage = new Map(); 979 var storage = new Map();
974 980
981 /**
982 * @param {string} property
983 */
975 function defineProperty(property) 984 function defineProperty(property)
976 { 985 {
977 if (property in object) { 986 if (property in object) {
978 storage.set(property, object[property]); 987 storage.set(property, object[property]);
979 delete object[property]; 988 delete object[property];
980 } 989 }
981 990
982 Object.defineProperty(object, property, { 991 Object.defineProperty(object, property, {
992 /**
993 * @return {*}
994 */
983 get: function() 995 get: function()
984 { 996 {
985 return storage.get(property); 997 return storage.get(property);
986 }, 998 },
987 999
1000 /**
1001 * @param {*} value
1002 */
988 set: function(value) 1003 set: function(value)
989 { 1004 {
990 storage.set(property, value); 1005 storage.set(property, value);
991 changedProperties.add(property); 1006 changedProperties.add(property);
992 scheduleObserver(); 1007 scheduleObserver();
993 } 1008 }
994 }); 1009 });
995 } 1010 }
996 1011
997 for (var i = 0; i < properties.length; ++i) 1012 for (var i = 0; i < properties.length; ++i)
998 defineProperty(properties[i]); 1013 defineProperty(properties[i]);
999 } 1014 }
1000 1015
1001 window.Object.observe = objectObserve; 1016 window.Object.observe = objectObserve;
1002 } 1017 }
1003 1018
1019 /** @type {!Map<number, string>} */
1004 var staticKeyIdentifiers = new Map([ 1020 var staticKeyIdentifiers = new Map([
1005 [0x12, "Alt"], 1021 [0x12, "Alt"],
1006 [0x11, "Control"], 1022 [0x11, "Control"],
1007 [0x10, "Shift"], 1023 [0x10, "Shift"],
1008 [0x14, "CapsLock"], 1024 [0x14, "CapsLock"],
1009 [0x5b, "Win"], 1025 [0x5b, "Win"],
1010 [0x5c, "Win"], 1026 [0x5c, "Win"],
1011 [0x0c, "Clear"], 1027 [0x0c, "Clear"],
1012 [0x28, "Down"], 1028 [0x28, "Down"],
1013 [0x23, "End"], 1029 [0x23, "End"],
(...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after
1053 [0x2e, "U+007F"], // Standard says that DEL becomes U+007F. 1069 [0x2e, "U+007F"], // Standard says that DEL becomes U+007F.
1054 [0xb0, "MediaNextTrack"], 1070 [0xb0, "MediaNextTrack"],
1055 [0xb1, "MediaPreviousTrack"], 1071 [0xb1, "MediaPreviousTrack"],
1056 [0xb2, "MediaStop"], 1072 [0xb2, "MediaStop"],
1057 [0xb3, "MediaPlayPause"], 1073 [0xb3, "MediaPlayPause"],
1058 [0xad, "VolumeMute"], 1074 [0xad, "VolumeMute"],
1059 [0xae, "VolumeDown"], 1075 [0xae, "VolumeDown"],
1060 [0xaf, "VolumeUp"], 1076 [0xaf, "VolumeUp"],
1061 ]); 1077 ]);
1062 1078
1079 /**
1080 * @param {number} keyCode
1081 * @return {string}
1082 */
1063 function keyCodeToKeyIdentifier(keyCode) 1083 function keyCodeToKeyIdentifier(keyCode)
1064 { 1084 {
1065 var result = staticKeyIdentifiers.get(keyCode); 1085 var result = staticKeyIdentifiers.get(keyCode);
1066 if (result !== undefined) 1086 if (result !== undefined)
1067 return result; 1087 return result;
1068 result = "U+"; 1088 result = "U+";
1069 var hexString = keyCode.toString(16).toUpperCase(); 1089 var hexString = keyCode.toString(16).toUpperCase();
1070 for (var i = hexString.length; i < 4; ++i) 1090 for (var i = hexString.length; i < 4; ++i)
1071 result += "0"; 1091 result += "0";
1072 result += hexString; 1092 result += hexString;
1073 return result; 1093 return result;
1074 } 1094 }
1075 1095
1076 /**
1077 * @suppressGlobalPropertiesCheck
1078 * @suppress {checkTypes}
1079 */
1080 function installBackwardsCompatibility() 1096 function installBackwardsCompatibility()
1081 { 1097 {
1082 if (window.location.search.indexOf("remoteFrontend") === -1) 1098 if (window.location.search.indexOf("remoteFrontend") === -1)
1083 return; 1099 return;
1084 1100
1085 // Support for legacy (<M53) frontends. 1101 // Support for legacy (<M53) frontends.
1086 if (!window.KeyboardEvent.prototype.hasOwnProperty("keyIdentifier")) { 1102 if (!window.KeyboardEvent.prototype.hasOwnProperty("keyIdentifier")) {
1087 Object.defineProperty(window.KeyboardEvent.prototype, "keyIdentifier", { 1103 Object.defineProperty(window.KeyboardEvent.prototype, "keyIdentifier", {
1104 /**
1105 * @return {string}
1106 * @this {KeyboardEvent}
1107 */
1088 get: function() 1108 get: function()
1089 { 1109 {
1090 return keyCodeToKeyIdentifier(this.keyCode); 1110 return keyCodeToKeyIdentifier(this.keyCode);
1091 } 1111 }
1092 }); 1112 });
1093 } 1113 }
1094 1114
1095 // Support for legacy (<M50) frontends. 1115 // Support for legacy (<M50) frontends.
1096 installObjectObserve(); 1116 installObjectObserve();
1097 1117
1098 /** 1118 /**
1119 * @param {string} property
1120 * @return {!CSSValue|null}
1099 * @this {CSSStyleDeclaration} 1121 * @this {CSSStyleDeclaration}
1100 */ 1122 */
1101 function getValue(property) 1123 function getValue(property)
1102 { 1124 {
1103 // Note that |property| comes from another context, so we can't use === here. 1125 // Note that |property| comes from another context, so we can't use === here.
1104 // eslint-disable-next-line eqeqeq 1126 // eslint-disable-next-line eqeqeq
1105 if (property == "padding-left") { 1127 if (property == "padding-left") {
1106 return { 1128 return /** @type {!CSSValue} */ ({
1107 /** 1129 /**
1108 * @suppressReceiverCheck 1130 * @return {number}
1109 * @this {Object} 1131 * @this {!{__paddingLeft: number}}
1110 */ 1132 */
1111 getFloatValue: function() { return this.__paddingLeft; }, 1133 getFloatValue: function() { return this.__paddingLeft; },
1112 __paddingLeft: parseFloat(this.paddingLeft) 1134 __paddingLeft: parseFloat(this.paddingLeft)
1113 }; 1135 });
1114 } 1136 }
1115 throw new Error("getPropertyCSSValue is undefined"); 1137 throw new Error("getPropertyCSSValue is undefined");
1116 } 1138 }
1117 1139
1118 // Support for legacy (<M41) frontends. 1140 // Support for legacy (<M41) frontends.
1119 window.CSSStyleDeclaration.prototype.getPropertyCSSValue = getValue; 1141 window.CSSStyleDeclaration.prototype.getPropertyCSSValue = getValue;
1120 1142
1121 function CSSPrimitiveValue() 1143 function CSSPrimitiveValue()
1122 { 1144 {
1123 } 1145 }
1124 CSSPrimitiveValue.CSS_PX = 5; 1146 CSSPrimitiveValue.CSS_PX = 5;
1125 window.CSSPrimitiveValue = CSSPrimitiveValue; 1147 window.CSSPrimitiveValue = CSSPrimitiveValue;
1126 1148
1127 // Support for legacy (<M44) frontends. 1149 // Support for legacy (<M44) frontends.
1128 var styleElement = window.document.createElement("style"); 1150 var styleElement = window.document.createElement("style");
1129 styleElement.type = "text/css"; 1151 styleElement.type = "text/css";
1130 styleElement.textContent = "html /deep/ * { min-width: 0; min-height: 0; }"; 1152 styleElement.textContent = "html /deep/ * { min-width: 0; min-height: 0; }";
1131 1153
1132 // Support for quirky border-image behavior (<M51), see: 1154 // Support for quirky border-image behavior (<M51), see:
1133 // https://bugs.chromium.org/p/chromium/issues/detail?id=559258 1155 // https://bugs.chromium.org/p/chromium/issues/detail?id=559258
1134 styleElement.textContent += "\nhtml /deep/ .cm-breakpoint .CodeMirror-linenu mber { border-style: solid !important; }"; 1156 styleElement.textContent += "\nhtml /deep/ .cm-breakpoint .CodeMirror-linenu mber { border-style: solid !important; }";
1135 styleElement.textContent += "\nhtml /deep/ .cm-breakpoint.cm-breakpoint-cond itional .CodeMirror-linenumber { border-style: solid !important; }"; 1157 styleElement.textContent += "\nhtml /deep/ .cm-breakpoint.cm-breakpoint-cond itional .CodeMirror-linenumber { border-style: solid !important; }";
1136 window.document.head.appendChild(styleElement); 1158 window.document.head.appendChild(styleElement);
1137 1159
1138 // Support for legacy (<M49) frontends. 1160 // Support for legacy (<M49) frontends.
1139 Event.prototype.deepPath = undefined; 1161 Event.prototype.deepPath = undefined;
1140 1162
1141 // Support for legacy (<53) frontends. 1163 // Support for legacy (<53) frontends.
1142 window.FileError = { 1164 window.FileError = /** @type {!function (new: FileError) : ?} */ ({
1143 NOT_FOUND_ERR: DOMException.NOT_FOUND_ERR, 1165 NOT_FOUND_ERR: DOMException.NOT_FOUND_ERR,
1144 ABORT_ERR: DOMException.ABORT_ERR, 1166 ABORT_ERR: DOMException.ABORT_ERR,
1145 INVALID_MODIFICATION_ERR: DOMException.INVALID_MODIFICATION_ERR, 1167 INVALID_MODIFICATION_ERR: DOMException.INVALID_MODIFICATION_ERR,
1146 NOT_READABLE_ERR: 0 // No matching DOMException, so code will be 0. 1168 NOT_READABLE_ERR: 0 // No matching DOMException, so code will be 0.
1147 }; 1169 });
1148 } 1170 }
1149 1171
1150 function windowLoaded() 1172 function windowLoaded()
1151 { 1173 {
1152 window.removeEventListener("DOMContentLoaded", windowLoaded, false); 1174 window.removeEventListener("DOMContentLoaded", windowLoaded, false);
1153 installBackwardsCompatibility(); 1175 installBackwardsCompatibility();
1154 } 1176 }
1155 1177
1156 if (window.document.head && (window.document.readyState === "complete" || window .document.readyState === "interactive")) 1178 if (window.document.head && (window.document.readyState === "complete" || window .document.readyState === "interactive"))
1157 installBackwardsCompatibility(); 1179 installBackwardsCompatibility();
1158 else 1180 else
1159 window.addEventListener("DOMContentLoaded", windowLoaded, false); 1181 window.addEventListener("DOMContentLoaded", windowLoaded, false);
1160 1182
1183 /** @type {(!function(string, boolean=):boolean)|undefined} */
1184 DOMTokenList.prototype.__originalDOMTokenListToggle;
1185
1161 if (!DOMTokenList.prototype.__originalDOMTokenListToggle) { 1186 if (!DOMTokenList.prototype.__originalDOMTokenListToggle) {
1162 DOMTokenList.prototype.__originalDOMTokenListToggle = DOMTokenList.prototype .toggle; 1187 DOMTokenList.prototype.__originalDOMTokenListToggle = DOMTokenList.prototype .toggle;
1188 /**
1189 * @param {string} token
1190 * @param {boolean=} force
1191 * @return {boolean}
1192 */
1163 DOMTokenList.prototype.toggle = function(token, force) 1193 DOMTokenList.prototype.toggle = function(token, force)
1164 { 1194 {
1165 if (arguments.length === 1) 1195 if (arguments.length === 1)
1166 force = !this.contains(token); 1196 force = !this.contains(token);
1167 return this.__originalDOMTokenListToggle(token, !!force); 1197 return this.__originalDOMTokenListToggle(token, !!force);
1168 }; 1198 };
1169 } 1199 }
1170 1200
1171 })(window); 1201 })(window);
OLDNEW
« no previous file with comments | « no previous file | third_party/WebKit/Source/devtools/front_end/externs.js » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698