SQL Operations Studio Public Preview 1 (0.23) release source code

This commit is contained in:
Karl Burtram
2017-11-09 14:30:27 -08:00
parent b88ecb8d93
commit 3cdac41339
8829 changed files with 759707 additions and 286 deletions

View File

@@ -0,0 +1,45 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import * as electron from 'electron';
import { ISqlOAuthService } from 'sql/common/sqlOAuthService';
/**
* Implements simple electron based utilities for registering and sending IPC to the main thread to
* handle OAuth requests. This is to avoid breaking layering issues caused when certain
* electron-based services are referenced in unit testable code.
*/
export class SqlOAuthService implements ISqlOAuthService {
public _serviceBrand: any;
/**
* Sends request to main thread to handle OAuth request
* @param {string} eventId Unique ID of the request to return upon completion
* @param {string} url URL to load to do OAuth
* @param {boolean} silent Whether or not to show the OAuth window
* @return {Thenable<string>} Promise to return an authorization code
*/
performOAuthAuthorization(eventId: string, url: string, silent: boolean): void {
// Setup the args and send the IPC call
electron.ipcRenderer.send(
'oauth',
{
url: url,
silent: silent,
eventId: eventId
}
);
}
/**
* Registers a handler for the oauth-reply event on the IPC channel
* @param {(event, args) => void} handler Handler to call when the event is triggered
*/
registerOAuthCallback(handler: (event, args) => void): void {
electron.ipcRenderer.on('oauth-reply', handler);
}
}

View File

@@ -0,0 +1,15 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
// localizable strings
export const InvalidProvider = 'Provider is invalid';
export const SerializationDisabled = 'Saving results into different format disabled for this data provider.';
/**
* Feature names
*/
export const RestoreFeatureName = 'restore';
export const BackupFeatureName = 'backup';

View File

@@ -0,0 +1,45 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as path from 'path';
import * as os from 'os';
import URI from 'vs/base/common/uri';
import { UNTITLED_SCHEMA } from 'vs/workbench/services/untitled/common/untitledEditorService';
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
export const FILE_SCHEMA: string = 'file';
export function resolveCurrentDirectory(uri: string, rootPath: string): string {
let sqlUri = URI.parse(uri);
let currentDirectory: string;
// use current directory of the sql file if sql file is saved
if (sqlUri.scheme === FILE_SCHEMA) {
currentDirectory = path.dirname(sqlUri.fsPath);
} else if (sqlUri.scheme === UNTITLED_SCHEMA) {
// if sql file is unsaved/untitled but a workspace is open use workspace root
let root = rootPath;
if (root) {
currentDirectory = root;
} else {
// use temp directory
currentDirectory = os.tmpdir();
}
} else {
currentDirectory = path.dirname(sqlUri.path);
}
return currentDirectory;
}
export function resolveFilePath(uri: string, filePath: string, rootPath: string): string {
let currentDirectory = resolveCurrentDirectory(uri, rootPath);
return path.normalize(path.join(currentDirectory, filePath));
}
export function getRootPath(contextService: IWorkspaceContextService): string {
return contextService.hasWorkspace() && contextService.getWorkspace().roots[0]
? contextService.getWorkspace().roots[0].fsPath : undefined;
}

View File

@@ -0,0 +1,34 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
export const ISqlOAuthService = createDecorator<ISqlOAuthService>('sqlOAuthService');
/**
* Defines simple electron based utilities for registering and sending IPC to the main thread to
* handle OAuth requests. This is to avoid breaking layering issues caused when certain
* electron-based services are referenced in unit testable code.
*/
export interface ISqlOAuthService {
_serviceBrand: any;
/**
* Sends request to main thread to handle OAuth request
* @param {string} eventId Unique ID of the request to return upon completion
* @param {string} url URL to load to do OAuth
* @param {boolean} silent Whether or not to show the OAuth window
* @return {Thenable<string>} Promise to return an authorization code
*/
performOAuthAuthorization(eventId: string, url: string, silent: boolean): void;
/**
* Registers a handler for the oauth-reply event on the IPC channel
* @param {(event, args) => void} handler Handler to call when the event is triggered
*/
registerOAuthCallback(handler: (event, args) => void): void;
}

View File

@@ -0,0 +1,40 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
// Telemetry Event Names
export const DatabaseConnected = 'DatabaseConnected';
export const DatabaseDisconnected = 'DatabaseDisconnected';
export const DeleteConnection = 'DeleteConnection';
export const AddServerGroup = 'AddServerGroup';
export const MoveServerGroup = 'MoveServerGroup';
export const MoveServerConnection = 'MoveServerConnection';
export const DeleteServerGroup = 'DeleteServerGroup';
export const ModalDialogClosed = 'ModalDialogClosed';
export const ModalDialogOpened = 'ModalDialogOpened';
export const BackupCreated = 'BackupCreated';
export const RestoreRequested = 'RestoreRequested';
export const ChartCreated = 'ChartCreated';
export const ObjectExplorerExpand = 'ObjectExplorerExpand';
export const RunQuery = 'RunQuery';
export const RunQueryStatement = 'RunQueryStatement';
export const CancelQuery = 'CancelQuery';
export const NewQuery = 'NewQuery';
export const FirewallRuleRequested = 'FirewallRuleCreated';
// Telemetry Properties
// Modal Dialogs:
export const ErrorMessage = 'ErrorMessage';
export const ConnectionAdvancedProperties = 'ConnectionAdvancedProperties';
export const Connection = 'Connection';
export const Backup = 'Backup';
export const Restore = 'Restore';
export const Insights = 'Insights';
export const Profiler = 'Profiler';
export const ServerGroups = 'ServerGroups';
export const Accounts = 'Accounts';
export const FireWallRule = 'FirewallRule';

View File

@@ -0,0 +1,78 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import * as crypto from 'crypto';
import * as os from 'os';
import { ITelemetryService, ITelemetryData } from 'vs/platform/telemetry/common/telemetry';
import { IConnectionProfile } from 'sql/parts/connection/common/interfaces';
import { warn } from 'sql/base/common/log';
import { generateUuid } from 'vs/base/common/uuid';
// Generate a unique, deterministic ID for the current user of the extension
export function generateUserId(): Promise<string> {
return new Promise<string>(resolve => {
try {
getmac.getMac((error, macAddress) => {
if (!error) {
resolve(crypto.createHash('sha256').update(macAddress + os.homedir(), 'utf8').digest('hex'));
} else {
resolve(generateUuid()); // fallback
}
});
} catch (err) {
resolve(generateUuid()); // fallback
}
});
}
export interface IConnectionTelemetryData extends ITelemetryData {
provider?: string;
}
/**
* Call the given telemetry service to log the telemetry event.
* If the provider is not in the data, tries to get it from connection inside the data.
* The connection in the data won't be included in the telemetry data
* Note: userId is added to all telemetry events so no need to add it here
* @param telemetryService Telemetry Service
* @param telemetryEventName Telemetry event name
* @param data Telemetry data
*/
export function addTelemetry(
telemetryService: ITelemetryService,
telemetryEventName: string,
data?: IConnectionTelemetryData,
connection?: IConnectionProfile): Promise<void> {
return new Promise<void>(resolve => {
try {
let telData: ITelemetryData = data === undefined ? {} : data;
if (telData && telData.provider === undefined) {
let provider: string = '';
if (connection) {
provider = connection.providerName;
}
telData.provider = provider;
}
delete telData['connection'];
if (telemetryService) {
telemetryService.publicLog(telemetryEventName, telData).then(() => {
resolve();
}, telemetryServiceError => {
warn(`Failed to add telemetry. error: ${telemetryServiceError}`);
resolve();
});
} else {
resolve();
}
} catch (error) {
warn(`Failed to add telemetry. error: ${error}`);
resolve();
}
});
}

View File

@@ -0,0 +1,13 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { registerColor, foreground } from 'vs/platform/theme/common/colorRegistry';
import { Color, RGBA } from 'vs/base/common/color';
import * as nls from 'vs/nls';
export const tableHeaderBackground = registerColor('table.headerBackground', { dark: new Color(new RGBA(51, 51, 52)), light: new Color(new RGBA(245, 245, 245)), hc: null }, nls.localize('tableHeaderBackground', 'Table header background color'));
export const tableHeaderForeground = registerColor('table.headerForeground', { dark: new Color(new RGBA(229, 229, 229)), light: new Color(new RGBA(16, 16, 16)), hc: null }, nls.localize('tableHeaderForeground', 'Table header foreground color'));
export const disabledInputBackground = registerColor('input.disabled.background', { dark: '#444444', light: '#dcdcdc', hc: Color.black }, nls.localize('disabledInputBoxBackground', "Disabled Input box background."));
export const disabledInputForeground = registerColor('input.disabled.foreground', { dark: '#888888', light: '#888888', hc: foreground }, nls.localize('disabledInputBoxForeground', "Disabled Input box foreground."));

View File

@@ -0,0 +1,227 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import * as sqlcolors from './colors';
import { IThemeService } from 'vs/platform/theme/common/themeService';
import * as cr from 'vs/platform/theme/common/colorRegistry';
import { IThemable, attachStyler } from 'vs/platform/theme/common/styler';
import { IDisposable } from 'vs/base/common/lifecycle';
import { SIDE_BAR_BACKGROUND } from 'vs/workbench/common/theme';
export function attachModalDialogStyler(widget: IThemable, themeService: IThemeService, style?:
{
dialogForeground?: cr.ColorIdentifier,
dialogHeaderAndFooterBackground?: cr.ColorIdentifier,
dialogBodyBackground?: cr.ColorIdentifier,
}): IDisposable {
return attachStyler(themeService, {
dialogForeground: (style && style.dialogForeground) || cr.foreground,
dialogBorder: cr.contrastBorder,
dialogHeaderAndFooterBackground: (style && style.dialogHeaderAndFooterBackground) || SIDE_BAR_BACKGROUND,
dialogBodyBackground: (style && style.dialogBodyBackground) || cr.editorBackground
}, widget);
}
export function attachDropdownStyler(widget: IThemable, themeService: IThemeService, style?:
{
backgroundColor?: cr.ColorIdentifier,
foregroundColor?: cr.ColorIdentifier,
borderColor?: cr.ColorIdentifier,
}): IDisposable {
return attachStyler(themeService, {
foregroundColor: (style && style.foregroundColor) || cr.inputForeground,
borderColor: (style && style.borderColor) || cr.inputBorder,
backgroundColor: (style && style.backgroundColor) || cr.editorBackground
}, widget);
}
export function attachInputBoxStyler(widget: IThemable, themeService: IThemeService, style?:
{
inputBackground?: cr.ColorIdentifier,
inputForeground?: cr.ColorIdentifier,
disabledInputBackground?: cr.ColorIdentifier,
disabledInputForeground?: cr.ColorIdentifier,
inputBorder?: cr.ColorIdentifier,
inputValidationInfoBorder?: cr.ColorIdentifier,
inputValidationInfoBackground?: cr.ColorIdentifier,
inputValidationWarningBorder?: cr.ColorIdentifier,
inputValidationWarningBackground?: cr.ColorIdentifier,
inputValidationErrorBorder?: cr.ColorIdentifier,
inputValidationErrorBackground?: cr.ColorIdentifier
}): IDisposable {
return attachStyler(themeService, {
inputBackground: (style && style.inputBackground) || cr.inputBackground,
inputForeground: (style && style.inputForeground) || cr.inputForeground,
disabledInputBackground: (style && style.disabledInputBackground) || sqlcolors.disabledInputBackground,
disabledInputForeground: (style && style.disabledInputForeground) || sqlcolors.disabledInputForeground,
inputBorder: (style && style.inputBorder) || cr.inputBorder,
inputValidationInfoBorder: (style && style.inputValidationInfoBorder) || cr.inputValidationInfoBorder,
inputValidationInfoBackground: (style && style.inputValidationInfoBackground) || cr.inputValidationInfoBackground,
inputValidationWarningBorder: (style && style.inputValidationWarningBorder) || cr.inputValidationWarningBorder,
inputValidationWarningBackground: (style && style.inputValidationWarningBackground) || cr.inputValidationWarningBackground,
inputValidationErrorBorder: (style && style.inputValidationErrorBorder) || cr.inputValidationErrorBorder,
inputValidationErrorBackground: (style && style.inputValidationErrorBackground) || cr.inputValidationErrorBackground
}, widget);
}
export function attachSelectBoxStyler(widget: IThemable, themeService: IThemeService, style?:
{
selectBackground?: cr.ColorIdentifier,
selectForeground?: cr.ColorIdentifier,
selectBorder?: cr.ColorIdentifier,
disabledSelectBackground?: cr.ColorIdentifier,
disabledSelectForeground?: cr.ColorIdentifier,
inputValidationInfoBorder?: cr.ColorIdentifier,
inputValidationInfoBackground?: cr.ColorIdentifier,
inputValidationWarningBorder?: cr.ColorIdentifier,
inputValidationWarningBackground?: cr.ColorIdentifier,
inputValidationErrorBorder?: cr.ColorIdentifier,
inputValidationErrorBackground?: cr.ColorIdentifier
}): IDisposable {
return attachStyler(themeService, {
selectBackground: (style && style.selectBackground) || cr.selectBackground,
selectForeground: (style && style.selectForeground) || cr.selectForeground,
selectBorder: (style && style.selectBorder) || cr.selectBorder,
disabledSelectBackground: (style && style.disabledSelectBackground) || sqlcolors.disabledInputBackground,
disabledSelectForeground: (style && style.disabledSelectForeground) || sqlcolors.disabledInputForeground,
inputValidationInfoBorder: (style && style.inputValidationInfoBorder) || cr.inputValidationInfoBorder,
inputValidationInfoBackground: (style && style.inputValidationInfoBackground) || cr.inputValidationInfoBackground,
inputValidationWarningBorder: (style && style.inputValidationWarningBorder) || cr.inputValidationWarningBorder,
inputValidationWarningBackground: (style && style.inputValidationWarningBackground) || cr.inputValidationWarningBackground,
inputValidationErrorBorder: (style && style.inputValidationErrorBorder) || cr.inputValidationErrorBorder,
inputValidationErrorBackground: (style && style.inputValidationErrorBackground) || cr.inputValidationErrorBackground
}, widget);
}
export function attachListBoxStyler(widget: IThemable, themeService: IThemeService, style?:
{
selectBackground?: cr.ColorIdentifier,
selectForeground?: cr.ColorIdentifier,
selectBorder?: cr.ColorIdentifier,
inputValidationInfoBorder?: cr.ColorIdentifier,
inputValidationInfoBackground?: cr.ColorIdentifier,
inputValidationWarningBorder?: cr.ColorIdentifier,
inputValidationWarningBackground?: cr.ColorIdentifier,
inputValidationErrorBorder?: cr.ColorIdentifier,
inputValidationErrorBackground?: cr.ColorIdentifier
}): IDisposable {
return attachStyler(themeService, {
selectBackground: (style && style.selectBackground) || cr.selectBackground,
selectForeground: (style && style.selectForeground) || cr.selectForeground,
selectBorder: (style && style.selectBorder) || cr.selectBorder,
inputValidationInfoBorder: (style && style.inputValidationInfoBorder) || cr.inputValidationInfoBorder,
inputValidationInfoBackground: (style && style.inputValidationInfoBackground) || cr.inputValidationInfoBackground,
inputValidationWarningBorder: (style && style.inputValidationWarningBorder) || cr.inputValidationWarningBorder,
inputValidationWarningBackground: (style && style.inputValidationWarningBackground) || cr.inputValidationWarningBackground,
inputValidationErrorBorder: (style && style.inputValidationErrorBorder) || cr.inputValidationErrorBorder,
inputValidationErrorBackground: (style && style.inputValidationErrorBackground) || cr.inputValidationErrorBackground
}, widget);
}
export function attachTableStyler(widget: IThemable, themeService: IThemeService, style?: {
listFocusBackground?: cr.ColorIdentifier,
listFocusForeground?: cr.ColorIdentifier,
listActiveSelectionBackground?: cr.ColorIdentifier,
listActiveSelectionForeground?: cr.ColorIdentifier,
listFocusAndSelectionBackground?: cr.ColorIdentifier,
listFocusAndSelectionForeground?: cr.ColorIdentifier,
listInactiveFocusBackground?: cr.ColorIdentifier,
listInactiveSelectionBackground?: cr.ColorIdentifier,
listInactiveSelectionForeground?: cr.ColorIdentifier,
listHoverBackground?: cr.ColorIdentifier,
listHoverForeground?: cr.ColorIdentifier,
listDropBackground?: cr.ColorIdentifier,
listFocusOutline?: cr.ColorIdentifier,
listInactiveFocusOutline?: cr.ColorIdentifier,
listSelectionOutline?: cr.ColorIdentifier,
listHoverOutline?: cr.ColorIdentifier,
tableHeaderBackground?: cr.ColorIdentifier,
tableHeaderForeground?: cr.ColorIdentifier
}): IDisposable {
return attachStyler(themeService, {
listFocusBackground: (style && style.listFocusBackground) || cr.listFocusBackground,
listFocusForeground: (style && style.listFocusForeground) || cr.listFocusForeground,
listActiveSelectionBackground: (style && style.listActiveSelectionBackground) || cr.lighten(cr.listActiveSelectionBackground, 0.1),
listActiveSelectionForeground: (style && style.listActiveSelectionForeground) || cr.listActiveSelectionForeground,
listFocusAndSelectionBackground: style && style.listFocusAndSelectionBackground || cr.listActiveSelectionBackground,
listFocusAndSelectionForeground: (style && style.listFocusAndSelectionForeground) || cr.listActiveSelectionForeground,
listInactiveFocusBackground: (style && style.listInactiveFocusBackground),
listInactiveSelectionBackground: (style && style.listInactiveSelectionBackground) || cr.listInactiveSelectionBackground,
listInactiveSelectionForeground: (style && style.listInactiveSelectionForeground) || cr.listInactiveSelectionForeground,
listHoverBackground: (style && style.listHoverBackground) || cr.listHoverBackground,
listHoverForeground: (style && style.listHoverForeground) || cr.listHoverForeground,
listDropBackground: (style && style.listDropBackground) || cr.listDropBackground,
listFocusOutline: (style && style.listFocusOutline) || cr.activeContrastBorder,
listSelectionOutline: (style && style.listSelectionOutline) || cr.activeContrastBorder,
listHoverOutline: (style && style.listHoverOutline) || cr.activeContrastBorder,
listInactiveFocusOutline: style && style.listInactiveFocusOutline,
tableHeaderBackground: (style && style.tableHeaderBackground) || sqlcolors.tableHeaderBackground,
tableHeaderForeground: (style && style.tableHeaderForeground) || sqlcolors.tableHeaderForeground
}, widget);
}
export function attachEditableDropdownStyler(widget: IThemable, themeService: IThemeService, style?: {
listFocusBackground?: cr.ColorIdentifier,
listFocusForeground?: cr.ColorIdentifier,
listActiveSelectionBackground?: cr.ColorIdentifier,
listActiveSelectionForeground?: cr.ColorIdentifier,
listFocusAndSelectionBackground?: cr.ColorIdentifier,
listFocusAndSelectionForeground?: cr.ColorIdentifier,
listInactiveFocusBackground?: cr.ColorIdentifier,
listInactiveSelectionBackground?: cr.ColorIdentifier,
listInactiveSelectionForeground?: cr.ColorIdentifier,
listHoverBackground?: cr.ColorIdentifier,
listHoverForeground?: cr.ColorIdentifier,
listDropBackground?: cr.ColorIdentifier,
listFocusOutline?: cr.ColorIdentifier,
listInactiveFocusOutline?: cr.ColorIdentifier,
listSelectionOutline?: cr.ColorIdentifier,
listHoverOutline?: cr.ColorIdentifier,
inputBackground?: cr.ColorIdentifier,
inputForeground?: cr.ColorIdentifier,
inputBorder?: cr.ColorIdentifier,
inputValidationInfoBorder?: cr.ColorIdentifier,
inputValidationInfoBackground?: cr.ColorIdentifier,
inputValidationWarningBorder?: cr.ColorIdentifier,
inputValidationWarningBackground?: cr.ColorIdentifier,
inputValidationErrorBorder?: cr.ColorIdentifier,
inputValidationErrorBackground?: cr.ColorIdentifier,
contextBackground?: cr.ColorIdentifier,
contextBorder?: cr.ColorIdentifier
}): IDisposable {
return attachStyler(themeService, {
listFocusBackground: (style && style.listFocusBackground) || cr.listFocusBackground,
listFocusForeground: (style && style.listFocusForeground) || cr.listFocusForeground,
listActiveSelectionBackground: (style && style.listActiveSelectionBackground) || cr.lighten(cr.listActiveSelectionBackground, 0.1),
listActiveSelectionForeground: (style && style.listActiveSelectionForeground) || cr.listActiveSelectionForeground,
listFocusAndSelectionBackground: style && style.listFocusAndSelectionBackground || cr.listActiveSelectionBackground,
listFocusAndSelectionForeground: (style && style.listFocusAndSelectionForeground) || cr.listActiveSelectionForeground,
listInactiveFocusBackground: (style && style.listInactiveFocusBackground),
listInactiveSelectionBackground: (style && style.listInactiveSelectionBackground) || cr.listInactiveSelectionBackground,
listInactiveSelectionForeground: (style && style.listInactiveSelectionForeground) || cr.listInactiveSelectionForeground,
listHoverBackground: (style && style.listHoverBackground) || cr.listHoverBackground,
listHoverForeground: (style && style.listHoverForeground) || cr.listHoverForeground,
listDropBackground: (style && style.listDropBackground) || cr.listDropBackground,
listFocusOutline: (style && style.listFocusOutline) || cr.activeContrastBorder,
listSelectionOutline: (style && style.listSelectionOutline) || cr.activeContrastBorder,
listHoverOutline: (style && style.listHoverOutline) || cr.activeContrastBorder,
listInactiveFocusOutline: style && style.listInactiveFocusOutline,
inputBackground: (style && style.inputBackground) || cr.inputBackground,
inputForeground: (style && style.inputForeground) || cr.inputForeground,
inputBorder: (style && style.inputBorder) || cr.inputBorder,
inputValidationInfoBorder: (style && style.inputValidationInfoBorder) || cr.inputValidationInfoBorder,
inputValidationInfoBackground: (style && style.inputValidationInfoBackground) || cr.inputValidationInfoBackground,
inputValidationWarningBorder: (style && style.inputValidationWarningBorder) || cr.inputValidationWarningBorder,
inputValidationWarningBackground: (style && style.inputValidationWarningBackground) || cr.inputValidationWarningBackground,
inputValidationErrorBorder: (style && style.inputValidationErrorBorder) || cr.inputValidationErrorBorder,
inputValidationErrorBackground: (style && style.inputValidationErrorBackground) || cr.inputValidationErrorBackground,
contextBackground: (style && style.contextBackground) || cr.editorBackground,
contextBorder: (style && style.contextBorder) || cr.inputBorder
}, widget);
}

View File

@@ -0,0 +1,27 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { DefaultUrlSerializer, UrlSerializer, UrlTree } from '@angular/router';
/**
* Angular router uses parentheses for custom behavior, however for file system,
* they are valid in paths. Therefore before and after angular handles url's, we
* encode and decode the parentheses. Github issue angular/angular#10280, microsoft/carbon#1116
*/
export default class CustomUrlSerializer implements UrlSerializer {
private _defaultUrlSerializer: DefaultUrlSerializer = new DefaultUrlSerializer();
parse(url: string): UrlTree {
// Encode parentheses
url = url.replace(/\(/g, '%28').replace(/\)/g, '%29');
// Use the default serializer from here on
return this._defaultUrlSerializer.parse(url);
}
serialize(tree: UrlTree): string {
// serialize parentheses after angular router
return this._defaultUrlSerializer.serialize(tree).replace(/%28/g, '(').replace(/%29/g, ')');
}
}