| OLD | NEW |
| 1 /* | 1 /* |
| 2 * Copyright (C) 2012 Google Inc. All rights reserved. | 2 * Copyright (C) 2012 Google Inc. All rights reserved. |
| 3 * | 3 * |
| 4 * Redistribution and use in source and binary forms, with or without | 4 * Redistribution and use in source and binary forms, with or without |
| 5 * modification, are permitted provided that the following conditions are | 5 * modification, are permitted provided that the following conditions are |
| 6 * met: | 6 * met: |
| 7 * | 7 * |
| 8 * * Redistributions of source code must retain the above copyright | 8 * * Redistributions of source code must retain the above copyright |
| 9 * notice, this list of conditions and the following disclaimer. | 9 * notice, this list of conditions and the following disclaimer. |
| 10 * * Redistributions in binary form must reproduce the above | 10 * * Redistributions in binary form must reproduce the above |
| (...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 43 apiPrivate.panels.SearchAction = { | 43 apiPrivate.panels.SearchAction = { |
| 44 CancelSearch: "cancelSearch", | 44 CancelSearch: "cancelSearch", |
| 45 PerformSearch: "performSearch", | 45 PerformSearch: "performSearch", |
| 46 NextSearchResult: "nextSearchResult", | 46 NextSearchResult: "nextSearchResult", |
| 47 PreviousSearchResult: "previousSearchResult" | 47 PreviousSearchResult: "previousSearchResult" |
| 48 }; | 48 }; |
| 49 | 49 |
| 50 apiPrivate.Events = { | 50 apiPrivate.Events = { |
| 51 AuditStarted: "audit-started-", | 51 AuditStarted: "audit-started-", |
| 52 ButtonClicked: "button-clicked-", | 52 ButtonClicked: "button-clicked-", |
| 53 ContextMenuClicked: "context-menu-clicked-", |
| 53 PanelObjectSelected: "panel-objectSelected-", | 54 PanelObjectSelected: "panel-objectSelected-", |
| 54 NetworkRequestFinished: "network-request-finished", | 55 NetworkRequestFinished: "network-request-finished", |
| 55 OpenResource: "open-resource", | 56 OpenResource: "open-resource", |
| 56 PanelSearch: "panel-search-", | 57 PanelSearch: "panel-search-", |
| 57 ResourceAdded: "resource-added", | 58 ResourceAdded: "resource-added", |
| 59 ResourceContentEdited: "resource-content-edited", |
| 58 ResourceContentCommitted: "resource-content-committed", | 60 ResourceContentCommitted: "resource-content-committed", |
| 59 ViewShown: "view-shown-", | 61 ViewShown: "view-shown-", |
| 60 ViewHidden: "view-hidden-" | 62 ViewHidden: "view-hidden-" |
| 61 }; | 63 }; |
| 62 | 64 |
| 63 apiPrivate.Commands = { | 65 apiPrivate.Commands = { |
| 64 AddAuditCategory: "addAuditCategory", | 66 AddAuditCategory: "addAuditCategory", |
| 65 AddAuditResult: "addAuditResult", | 67 AddAuditResult: "addAuditResult", |
| 66 AddRequestHeaders: "addRequestHeaders", | 68 AddRequestHeaders: "addRequestHeaders", |
| 67 ApplyStyleSheet: "applyStyleSheet", | 69 ApplyStyleSheet: "applyStyleSheet", |
| 68 CreatePanel: "createPanel", | 70 CreatePanel: "createPanel", |
| 69 CreateSidebarPane: "createSidebarPane", | 71 CreateSidebarPane: "createSidebarPane", |
| 70 CreateToolbarButton: "createToolbarButton", | 72 CreateToolbarButton: "createToolbarButton", |
| 73 DisplaySearchResults: "displaySearchResults", |
| 71 EvaluateOnInspectedPage: "evaluateOnInspectedPage", | 74 EvaluateOnInspectedPage: "evaluateOnInspectedPage", |
| 72 ForwardKeyboardEvent: "_forwardKeyboardEvent", | 75 ForwardKeyboardEvent: "_forwardKeyboardEvent", |
| 73 GetHAR: "getHAR", | 76 GetHAR: "getHAR", |
| 74 GetPageResources: "getPageResources", | 77 GetPageResources: "getPageResources", |
| 75 GetRequestContent: "getRequestContent", | 78 GetRequestContent: "getRequestContent", |
| 76 GetResourceContent: "getResourceContent", | 79 GetResourceContent: "getResourceContent", |
| 77 InspectedURLChanged: "inspectedURLChanged", | 80 InspectedURLChanged: "inspectedURLChanged", |
| 81 NewMimeType: "newMimeType", |
| 78 OpenResource: "openResource", | 82 OpenResource: "openResource", |
| 83 RegisterLanguageService: "registerLanguageService", |
| 79 Reload: "Reload", | 84 Reload: "Reload", |
| 80 Subscribe: "subscribe", | 85 Subscribe: "subscribe", |
| 81 SetOpenResourceHandler: "setOpenResourceHandler", | 86 SetOpenResourceHandler: "setOpenResourceHandler", |
| 82 SetResourceContent: "setResourceContent", | 87 SetResourceContent: "setResourceContent", |
| 88 SetResourceLineMessages: "setResourceLineMessages", |
| 83 SetSidebarContent: "setSidebarContent", | 89 SetSidebarContent: "setSidebarContent", |
| 84 SetSidebarHeight: "setSidebarHeight", | 90 SetSidebarHeight: "setSidebarHeight", |
| 85 SetSidebarPage: "setSidebarPage", | 91 SetSidebarPage: "setSidebarPage", |
| 86 ShowPanel: "showPanel", | 92 ShowPanel: "showPanel", |
| 87 StopAuditCategoryRun: "stopAuditCategoryRun", | 93 StopAuditCategoryRun: "stopAuditCategoryRun", |
| 88 Unsubscribe: "unsubscribe", | 94 Unsubscribe: "unsubscribe", |
| 89 UpdateAuditProgress: "updateAuditProgress", | 95 UpdateAuditProgress: "updateAuditProgress", |
| 90 UpdateButton: "updateButton" | 96 UpdateButton: "updateButton" |
| 91 }; | 97 }; |
| 92 } | 98 } |
| (...skipping 76 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 169 | 175 |
| 170 /** | 176 /** |
| 171 * @constructor | 177 * @constructor |
| 172 */ | 178 */ |
| 173 function InspectorExtensionAPI() | 179 function InspectorExtensionAPI() |
| 174 { | 180 { |
| 175 this.audits = new Audits(); | 181 this.audits = new Audits(); |
| 176 this.inspectedWindow = new InspectedWindow(); | 182 this.inspectedWindow = new InspectedWindow(); |
| 177 this.panels = new Panels(); | 183 this.panels = new Panels(); |
| 178 this.network = new Network(); | 184 this.network = new Network(); |
| 185 this.language = new Language(); |
| 179 defineDeprecatedProperty(this, "webInspector", "resources", "network"); | 186 defineDeprecatedProperty(this, "webInspector", "resources", "network"); |
| 180 } | 187 } |
| 181 | 188 |
| 182 /** | 189 /** |
| 190 * @interface |
| 191 */ |
| 192 function ServiceProvider(){} |
| 193 ServiceProvider.prototype = { |
| 194 /** |
| 195 * @param {string} text |
| 196 * @param {{source: string, line: number, column: number}=} location |
| 197 * @return {!Promise<string>} |
| 198 */ |
| 199 transpile: function(text, location){ return Promise.reject(); }, |
| 200 /** |
| 201 * @param {{source: string, line: number, column: number}} location |
| 202 * @return {!Promise<!Array<{text: string, callback: function(), category: (
string|undefined)}>>} |
| 203 */ |
| 204 populateContextMenu: function(location){ return Promise.reject(); }, |
| 205 |
| 206 /** |
| 207 * @param {{source: string, line: number, column: number}} location |
| 208 * @param {string} prefix |
| 209 * @return {!Promise<!Array<{text: string, icon: (string|undefined), details
: function(): !Promise<{detail: string, description: string}>}>>} |
| 210 */ |
| 211 completions: function(location, prefix) { return Promise.reject(); }, |
| 212 |
| 213 /** |
| 214 * @param {string} content |
| 215 * @param {{line: number, column: number}} cursor |
| 216 * @param {string=} prefix |
| 217 * @param {{source: string, line: number, column: number}=} context |
| 218 * @return {!Promise<!Array<{text: string, icon: (string|undefined), details
: function(): !Promise<{detail: string, description: string}>}>>} |
| 219 */ |
| 220 debuggerCompletions: function(content, cursor, prefix, context) { return Pro
mise.reject(); } |
| 221 } |
| 222 |
| 223 /** |
| 224 * @constructor |
| 225 */ |
| 226 function Language() { |
| 227 } |
| 228 Language.prototype = { |
| 229 /** |
| 230 * @param {string} extensionOrName |
| 231 * @param {string} mime |
| 232 * @param {boolean=} byName |
| 233 */ |
| 234 newMimeType: function(extensionOrName, mime, byName) { |
| 235 extensionServer.sendRequest({command: commands.NewMimeType, extensionOrN
ame: extensionOrName, mime: mime, byName: byName}); |
| 236 }, |
| 237 |
| 238 /** |
| 239 * @param {string} name |
| 240 * @param {!Array<string>} mimes |
| 241 * @param {!ServiceProvider} service |
| 242 * @param {?=} simplemode |
| 243 */ |
| 244 register: function(name, mimes, service, simplemode) { |
| 245 var capabilities = Object.keys(service); |
| 246 if (Object.getPrototypeOf(service) !== Object.getPrototypeOf({})) { //ha
ndle classes |
| 247 var serviceWalker = service; |
| 248 while ((serviceWalker = Object.getPrototypeOf(serviceWalker)) && ser
viceWalker !== Object.getPrototypeOf({})) { |
| 249 capabilities = capabilities.concat(Object.getOwnPropertyNames(se
rviceWalker)); |
| 250 } |
| 251 } |
| 252 capabilities = Array.from(/** @type {!Iterator<string>} */(new Set(capab
ilities.filter(f => (f !== "constructor") && (typeof service[f] === "function"))
))); |
| 253 |
| 254 extensionServer.sendRequest({ |
| 255 command: commands.RegisterLanguageService, |
| 256 name: name, |
| 257 mimes: mimes, |
| 258 capabilities: capabilities, |
| 259 simplemode: simplemode |
| 260 }); |
| 261 |
| 262 extensionServer.registerHandler("transpile-"+name, function(request) { |
| 263 if (!service.transpile) { |
| 264 return extensionServer.sendRequest({command: "callback", request
Id: request.requestId, result: request.input}); |
| 265 } |
| 266 service.transpile(request.input, request.location).then(function(tra
nspiled) { |
| 267 extensionServer.sendRequest({command: "callback", requestId: req
uest.requestId, result: transpiled}); |
| 268 }) |
| 269 .catch(function(err) { |
| 270 extensionServer.sendRequest({command: "callback", requestId: req
uest.requestId}); |
| 271 console.error(err); |
| 272 }); |
| 273 }); |
| 274 |
| 275 extensionServer.registerHandler("populateContextMenu-"+name, function(re
quest) { |
| 276 if (!service.populateContextMenu) { |
| 277 return extensionServer.sendRequest({command: "callback", request
Id: request.requestId}); |
| 278 } |
| 279 var eventName ="notify-"+apiPrivate.Events.ContextMenuClicked; |
| 280 service.populateContextMenu(request.location).then(function(options)
{ |
| 281 var opts = options.map(function(item, i) { return {text: item.te
xt, id: i, category: item.category} }); |
| 282 extensionServer.sendRequest({command: "callback", requestId: req
uest.requestId, result: opts}); |
| 283 |
| 284 if (extensionServer.hasHandler(eventName)) { |
| 285 extensionServer.unregisterHandler(eventName); |
| 286 } |
| 287 extensionServer.registerHandler(eventName, handleClick); |
| 288 |
| 289 function handleClick(event) { |
| 290 extensionServer.unregisterHandler(eventName); |
| 291 if (options[event.id] && options[event.id].callback) { |
| 292 options[event.id].callback(); |
| 293 } |
| 294 } |
| 295 }) |
| 296 .catch(function(err) { |
| 297 extensionServer.sendRequest({command: "callback", requestId: req
uest.requestId}); |
| 298 console.error(err); |
| 299 }); |
| 300 }); |
| 301 |
| 302 function handleCompletionsResponse(request) { |
| 303 var commandName = "completionDetails-"+name; |
| 304 return function(completions) { |
| 305 var comps = completions.map(function(item, i) { return {text: it
em.text, icon: item.icon, id: i} }); |
| 306 |
| 307 extensionServer.sendRequest({command: "callback", requestId: req
uest.requestId, result: comps}); |
| 308 |
| 309 if (extensionServer.hasHandler(commandName)) { |
| 310 extensionServer.unregisterHandler(commandName); |
| 311 } |
| 312 extensionServer.registerHandler(commandName, handlerHover); |
| 313 |
| 314 function handlerHover(event) { |
| 315 if (completions[event.id] && completions[event.id].details)
{ |
| 316 completions[event.id].details().then(function(details) { |
| 317 extensionServer.sendRequest({command: "callback", re
questId: event.requestId, result: details}); |
| 318 }).catch(function(err) { |
| 319 extensionServer.sendRequest({command: "callback", re
questId: event.requestId}); |
| 320 console.error(err); |
| 321 }); |
| 322 } else { |
| 323 extensionServer.sendRequest({command: "callback", reques
tId: event.requestId}); |
| 324 } |
| 325 } |
| 326 } |
| 327 } |
| 328 |
| 329 extensionServer.registerHandler("completions-"+name, function(request) { |
| 330 if (!service.completions) { |
| 331 return extensionServer.sendRequest({command: "callback", request
Id: request.requestId}); |
| 332 } |
| 333 service.completions(request.location, request.prefix) |
| 334 .then(handleCompletionsResponse(request)) |
| 335 .catch(function(err) { |
| 336 extensionServer.sendRequest({command: "callback", requestId: req
uest.requestId}); |
| 337 console.error(err); |
| 338 }); |
| 339 }); |
| 340 |
| 341 extensionServer.registerHandler("debuggerCompletions-"+name, function(re
quest) { |
| 342 if (!service.debuggerCompletions) { |
| 343 return extensionServer.sendRequest({command: "callback", request
Id: request.requestId}); |
| 344 } |
| 345 service.debuggerCompletions(request.content, request.cursor, request
.prefix, request.context) |
| 346 .then(handleCompletionsResponse(request)) |
| 347 .catch(function(err) { |
| 348 extensionServer.sendRequest({command: "callback", requestId: req
uest.requestId}); |
| 349 console.error(err); |
| 350 }); |
| 351 }); |
| 352 } |
| 353 } |
| 354 |
| 355 /** |
| 183 * @constructor | 356 * @constructor |
| 184 */ | 357 */ |
| 185 function Network() | 358 function Network() |
| 186 { | 359 { |
| 187 /** | 360 /** |
| 188 * @this {EventSinkImpl} | 361 * @this {EventSinkImpl} |
| 189 */ | 362 */ |
| 190 function dispatchRequestEvent(message) | 363 function dispatchRequestEvent(message) |
| 191 { | 364 { |
| 192 var request = message.arguments[1]; | 365 var request = message.arguments[1]; |
| (...skipping 227 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 420 /** | 593 /** |
| 421 * @constructor | 594 * @constructor |
| 422 * @extends {PanelWithSidebar} | 595 * @extends {PanelWithSidebar} |
| 423 */ | 596 */ |
| 424 function SourcesPanel() | 597 function SourcesPanel() |
| 425 { | 598 { |
| 426 PanelWithSidebar.call(this, "sources"); | 599 PanelWithSidebar.call(this, "sources"); |
| 427 } | 600 } |
| 428 | 601 |
| 429 SourcesPanel.prototype = { | 602 SourcesPanel.prototype = { |
| 603 displaySearchResults: function(results) { |
| 604 extensionServer.sendRequest({command: commands.DisplaySearchResults, res
ults: results}); |
| 605 }, |
| 430 __proto__: PanelWithSidebar.prototype | 606 __proto__: PanelWithSidebar.prototype |
| 431 } | 607 } |
| 432 | 608 |
| 433 /** | 609 /** |
| 434 * @constructor | 610 * @constructor |
| 435 * @extends {ExtensionViewImpl} | 611 * @extends {ExtensionViewImpl} |
| 436 */ | 612 */ |
| 437 function ExtensionPanelImpl(id) | 613 function ExtensionPanelImpl(id) |
| 438 { | 614 { |
| 439 ExtensionViewImpl.call(this, id); | 615 ExtensionViewImpl.call(this, id); |
| (...skipping 259 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 699 } | 875 } |
| 700 | 876 |
| 701 /** | 877 /** |
| 702 * @this {EventSinkImpl} | 878 * @this {EventSinkImpl} |
| 703 */ | 879 */ |
| 704 function dispatchResourceContentEvent(message) | 880 function dispatchResourceContentEvent(message) |
| 705 { | 881 { |
| 706 this._fire(new Resource(message.arguments[0]), message.arguments[1]); | 882 this._fire(new Resource(message.arguments[0]), message.arguments[1]); |
| 707 } | 883 } |
| 708 | 884 |
| 885 /** |
| 886 * @this {EventSinkImpl} |
| 887 */ |
| 888 function dispatchResourceEditEvent(message) |
| 889 { |
| 890 this._fire(new Resource(message.arguments[0]), message.arguments[1], mes
sage.arguments[2]); |
| 891 } |
| 892 |
| 709 this.onResourceAdded = new EventSink(events.ResourceAdded, dispatchResourceE
vent); | 893 this.onResourceAdded = new EventSink(events.ResourceAdded, dispatchResourceE
vent); |
| 710 this.onResourceContentCommitted = new EventSink(events.ResourceContentCommit
ted, dispatchResourceContentEvent); | 894 this.onResourceContentCommitted = new EventSink(events.ResourceContentCommit
ted, dispatchResourceContentEvent); |
| 895 this.onResourceContentEdited = new EventSink(events.ResourceContentEdited, d
ispatchResourceEditEvent); |
| 711 } | 896 } |
| 712 | 897 |
| 713 InspectedWindow.prototype = { | 898 InspectedWindow.prototype = { |
| 714 reload: function(optionsOrUserAgent) | 899 reload: function(optionsOrUserAgent) |
| 715 { | 900 { |
| 716 var options = null; | 901 var options = null; |
| 717 if (typeof optionsOrUserAgent === "object") { | 902 if (typeof optionsOrUserAgent === "object") { |
| 718 options = optionsOrUserAgent; | 903 options = optionsOrUserAgent; |
| 719 } else if (typeof optionsOrUserAgent === "string") { | 904 } else if (typeof optionsOrUserAgent === "string") { |
| 720 options = { userAgent: optionsOrUserAgent }; | 905 options = { userAgent: optionsOrUserAgent }; |
| (...skipping 66 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 787 { | 972 { |
| 788 callback(response.content, response.encoding); | 973 callback(response.content, response.encoding); |
| 789 } | 974 } |
| 790 | 975 |
| 791 extensionServer.sendRequest({ command: commands.GetResourceContent, url:
this._url }, callback && callbackWrapper); | 976 extensionServer.sendRequest({ command: commands.GetResourceContent, url:
this._url }, callback && callbackWrapper); |
| 792 }, | 977 }, |
| 793 | 978 |
| 794 setContent: function(content, commit, callback) | 979 setContent: function(content, commit, callback) |
| 795 { | 980 { |
| 796 extensionServer.sendRequest({ command: commands.SetResourceContent, url:
this._url, content: content, commit: commit }, callback); | 981 extensionServer.sendRequest({ command: commands.SetResourceContent, url:
this._url, content: content, commit: commit }, callback); |
| 982 }, |
| 983 |
| 984 setLineMessages: function(messages, callback) { |
| 985 extensionServer.sendRequest({ command: commands.SetResourceLineMessages,
url: this._url, messages: messages }, callback); |
| 797 } | 986 } |
| 798 } | 987 } |
| 799 | 988 |
| 800 var keyboardEventRequestQueue = []; | 989 var keyboardEventRequestQueue = []; |
| 801 var forwardTimer = null; | 990 var forwardTimer = null; |
| 802 | 991 |
| 803 function forwardKeyboardEvent(event) | 992 function forwardKeyboardEvent(event) |
| 804 { | 993 { |
| 805 const Esc = "U+001B"; | 994 const Esc = "U+001B"; |
| 806 // We only care about global hotkeys, not about random text | 995 // We only care about global hotkeys, not about random text |
| (...skipping 152 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 959 // extensions) | 1148 // extensions) |
| 960 var devtools_descriptor = Object.getOwnPropertyDescriptor(chrome, "devtools"
); | 1149 var devtools_descriptor = Object.getOwnPropertyDescriptor(chrome, "devtools"
); |
| 961 if (!devtools_descriptor || devtools_descriptor.get) | 1150 if (!devtools_descriptor || devtools_descriptor.get) |
| 962 Object.defineProperty(chrome, "devtools", { value: {}, enumerable: true
}); | 1151 Object.defineProperty(chrome, "devtools", { value: {}, enumerable: true
}); |
| 963 // Only expose tabId on chrome.devtools.inspectedWindow, not webInspector.in
spectedWindow. | 1152 // Only expose tabId on chrome.devtools.inspectedWindow, not webInspector.in
spectedWindow. |
| 964 chrome.devtools.inspectedWindow = {}; | 1153 chrome.devtools.inspectedWindow = {}; |
| 965 chrome.devtools.inspectedWindow.__defineGetter__("tabId", getTabId); | 1154 chrome.devtools.inspectedWindow.__defineGetter__("tabId", getTabId); |
| 966 chrome.devtools.inspectedWindow.__proto__ = coreAPI.inspectedWindow; | 1155 chrome.devtools.inspectedWindow.__proto__ = coreAPI.inspectedWindow; |
| 967 chrome.devtools.network = coreAPI.network; | 1156 chrome.devtools.network = coreAPI.network; |
| 968 chrome.devtools.panels = coreAPI.panels; | 1157 chrome.devtools.panels = coreAPI.panels; |
| 1158 chrome.devtools.language = coreAPI.language; |
| 969 | 1159 |
| 970 // default to expose experimental APIs for now. | 1160 // default to expose experimental APIs for now. |
| 971 if (extensionInfo.exposeExperimentalAPIs !== false) { | 1161 if (extensionInfo.exposeExperimentalAPIs !== false) { |
| 972 chrome.experimental = chrome.experimental || {}; | 1162 chrome.experimental = chrome.experimental || {}; |
| 973 chrome.experimental.devtools = chrome.experimental.devtools || {}; | 1163 chrome.experimental.devtools = chrome.experimental.devtools || {}; |
| 974 | 1164 |
| 975 var properties = Object.getOwnPropertyNames(coreAPI); | 1165 var properties = Object.getOwnPropertyNames(coreAPI); |
| 976 for (var i = 0; i < properties.length; ++i) { | 1166 for (var i = 0; i < properties.length; ++i) { |
| 977 var descriptor = Object.getOwnPropertyDescriptor(coreAPI, properties
[i]); | 1167 var descriptor = Object.getOwnPropertyDescriptor(coreAPI, properties
[i]); |
| 978 Object.defineProperty(chrome.experimental.devtools, properties[i], d
escriptor); | 1168 Object.defineProperty(chrome.experimental.devtools, properties[i], d
escriptor); |
| (...skipping 25 matching lines...) Expand all Loading... |
| 1004 { | 1194 { |
| 1005 return "(function(injectedScriptId){ " + | 1195 return "(function(injectedScriptId){ " + |
| 1006 "var extensionServer;" + | 1196 "var extensionServer;" + |
| 1007 defineCommonExtensionSymbols.toString() + ";" + | 1197 defineCommonExtensionSymbols.toString() + ";" + |
| 1008 injectedExtensionAPI.toString() + ";" + | 1198 injectedExtensionAPI.toString() + ";" + |
| 1009 buildPlatformExtensionAPI(extensionInfo, inspectedTabId) + ";" + | 1199 buildPlatformExtensionAPI(extensionInfo, inspectedTabId) + ";" + |
| 1010 "platformExtensionAPI(injectedExtensionAPI(injectedScriptId));" + | 1200 "platformExtensionAPI(injectedExtensionAPI(injectedScriptId));" + |
| 1011 "return {};" + | 1201 "return {};" + |
| 1012 "})"; | 1202 "})"; |
| 1013 } | 1203 } |
| OLD | NEW |