@salesforcedevs/dx-components 0.59.0-alpha.1 → 0.59.0-avatar-button

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@salesforcedevs/dx-components",
3
- "version": "0.59.0-alpha.1",
3
+ "version": "0.59.0-avatar-button",
4
4
  "description": "DX Lightning web components",
5
5
  "license": "MIT",
6
6
  "engines": {
@@ -17,7 +17,8 @@
17
17
  "classnames": "^2.2.6",
18
18
  "debounce": "^1.2.0",
19
19
  "lodash.get": "^4.4.2",
20
- "microtip": "0.2.2"
20
+ "microtip": "0.2.2",
21
+ "salesforce-oauth2": "^0.2.0"
21
22
  },
22
23
  "devDependencies": {
23
24
  "@types/classnames": "^2.2.10",
@@ -0,0 +1,133 @@
1
+ :host {
2
+ --dx-c-button-custom-color: var(--dx-g-blue-vibrant-50);
3
+ --dx-c-button-custom-background: transparent;
4
+ --dx-c-button-custom-border: 1px solid transparent;
5
+ --dx-c-button-custom-color-hover: var(--dx-g-blue-vibrant-50);
6
+ --dx-c-button-custom-background-hover: var(--dx-g-cloud-blue-vibrant-90);
7
+ --dx-c-button-custom-border-hover: var(--dx-g-cloud-blue-vibrant-90);
8
+ --dx-c-slot-empty-width: max-content;
9
+ }
10
+
11
+ .avatar-small-container {
12
+ padding: 2px 0;
13
+ }
14
+
15
+ .avatar {
16
+ border-radius: 100%;
17
+ border: 2px solid #0d9dda;
18
+ }
19
+
20
+ .avatar.avatar-small {
21
+ display: block;
22
+ height: var(--dx-g-spacing-lg);
23
+ width: var(--dx-g-spacing-lg);
24
+ }
25
+
26
+ .avatar.avatar-medium {
27
+ margin-bottom: 14px;
28
+ width: var(--dx-g-spacing-2xl);
29
+ }
30
+
31
+ .login-control {
32
+ margin-left: var(--dx-g-spacing-smd);
33
+ }
34
+
35
+ .login-container {
36
+ padding: 16px;
37
+ }
38
+
39
+ .login-container.logged-in {
40
+ padding-top: 68px;
41
+ }
42
+
43
+ .header-img {
44
+ left: 0;
45
+ position: absolute;
46
+ top: 0;
47
+ width: 100%;
48
+ z-index: -1;
49
+ }
50
+
51
+ .heading {
52
+ color: var(--dx-g-blue-vibrant-20);
53
+ display: block;
54
+ font-family: var(--dx-g-font-display);
55
+ font-size: var(--dx-g-text-lg);
56
+ line-height: 28px;
57
+ }
58
+
59
+ .sub-heading {
60
+ color: var(--dx-g-blue-vibrant-20);
61
+ display: block;
62
+ font-family: var(--dx-g-font-sans);
63
+ font-size: 12px;
64
+ line-height: 18px;
65
+ padding: 8px 0;
66
+ }
67
+
68
+ ul {
69
+ list-style: none;
70
+ margin: 0;
71
+ padding: 0;
72
+ }
73
+
74
+ .login-list-item {
75
+ padding-bottom: 10px;
76
+ }
77
+
78
+ .login-section ~ .login-section {
79
+ border-top: 1px solid var(--dx-g-gray-90);
80
+ margin-top: 12px;
81
+ }
82
+
83
+ .login-section-title {
84
+ color: var(--dx-g-gray-30);
85
+ display: inline-block;
86
+ font-family: var(--dx-g-font-sans);
87
+ font-size: 14px;
88
+ line-height: 20px;
89
+ padding: 20px 0 12px;
90
+ }
91
+
92
+ .login-link {
93
+ color: var(--dx-g-blue-vibrant-20);
94
+ display: block;
95
+ }
96
+
97
+ .login-link-label {
98
+ display: inline-block;
99
+ font-family: var(--dx-g-font-display);
100
+ font-size: 16px;
101
+ line-height: 24px;
102
+ }
103
+
104
+ .login-link-img {
105
+ display: inline-block;
106
+ height: 16px;
107
+ margin-left: 6px;
108
+ vertical-align: middle;
109
+ }
110
+
111
+ .login-link-icon {
112
+ display: inline-block;
113
+ margin-left: 6px;
114
+ vertical-align: baseline;
115
+ }
116
+
117
+ .login-link-blurb {
118
+ font-family: var(--dx-g-font-sans);
119
+ font-size: 14px;
120
+ line-height: 20px;
121
+ padding-top: 4px;
122
+ }
123
+
124
+ .heading-links {
125
+ color: var(--dx-g-gray-90);
126
+ font-family: var(--dx-g-font-sans);
127
+ font-size: 14px;
128
+ line-height: 20px;
129
+ }
130
+
131
+ a.inline-link {
132
+ color: var(--dx-g-blue-vibrant-50);
133
+ }
@@ -0,0 +1,151 @@
1
+ <template>
2
+ <div if:true={isLoggedIn}>
3
+ <dx-popover offset="small" width="320px" open-on-hover>
4
+ <div slot="control" class="avatar-small-container login-control">
5
+ <img src={userInfo.avatarImgSrc} class="avatar avatar-small" />
6
+ </div>
7
+ <div slot="content">
8
+ <div class="login-container logged-in">
9
+ <img
10
+ src="/assets/svg/login-widget-bg.png"
11
+ class="header-img"
12
+ />
13
+ <img
14
+ src={userInfo.avatarImgSrc}
15
+ class="avatar avatar-medium"
16
+ />
17
+ <span class="heading">{userInfo.fullName}</span>
18
+ <span class="sub-heading">{userInfo.username}</span>
19
+ <div class="heading-links">
20
+ <!--
21
+ <a href="#" class="inline-link">Switch account</a>
22
+ &nbsp;&nbsp;|&nbsp;&nbsp;
23
+ -->
24
+ <a href="#" onclick={handleLogout} class="inline-link">
25
+ Logout
26
+ </a>
27
+ </div>
28
+ <div class="login-section">
29
+ <span class="login-section-title">Trailblazer.me</span>
30
+ <ul>
31
+ <li class="login-list-item">
32
+ <a href={settingsUrl} class="login-link">
33
+ <span class="login-link-label">
34
+ Settings
35
+ </span>
36
+ <dx-icon
37
+ class="login-link-icon"
38
+ symbol="new_window"
39
+ size="xsmall"
40
+ ></dx-icon>
41
+ </a>
42
+ </li>
43
+ <li class="login-list-item">
44
+ <a href={profileUrl} class="login-link">
45
+ <span class="login-link-label">
46
+ Profile
47
+ </span>
48
+ <dx-icon
49
+ class="login-link-icon"
50
+ symbol="new_window"
51
+ size="xsmall"
52
+ ></dx-icon>
53
+ </a>
54
+ </li>
55
+ <li class="login-list-item">
56
+ <a href="#" class="login-link">
57
+ <span class="login-link-label">
58
+ Developer Forums
59
+ </span>
60
+ <dx-icon
61
+ class="login-link-icon"
62
+ symbol="new_window"
63
+ size="xsmall"
64
+ ></dx-icon>
65
+ </a>
66
+ <div class="login-link-blurb">
67
+ Our Developer Forums have a new home!&nbsp;
68
+ <a href="#" class="inline-link">
69
+ Learn more
70
+ </a>
71
+ </div>
72
+ </li>
73
+ </ul>
74
+ </div>
75
+ </div>
76
+ </div>
77
+ </dx-popover>
78
+ </div>
79
+ <div if:false={isLoggedIn}>
80
+ <dx-popover offset="small" width="320px" open-on-hover>
81
+ <dx-button
82
+ slot="control"
83
+ class="login-control"
84
+ icon-symbol="user"
85
+ icon-position="left"
86
+ loading={isLoading}
87
+ variant="custom"
88
+ >
89
+ Login
90
+ </dx-button>
91
+ <div slot="content">
92
+ <div class="login-container">
93
+ <span class="heading">Login</span>
94
+ <div class="login-section">
95
+ <span class="login-section-title">Products</span>
96
+ <ul>
97
+ <li class="login-list-item">
98
+ <a href="#" class="login-link">
99
+ <span class="login-link-label">
100
+ Salesforce
101
+ </span>
102
+ <img
103
+ class="login-link-img"
104
+ src="/assets/svg/salesforce-cloud.svg"
105
+ alt="Salesforce logo"
106
+ />
107
+ <dx-icon
108
+ class="login-link-icon"
109
+ symbol="new_window"
110
+ size="xsmall"
111
+ ></dx-icon>
112
+ </a>
113
+ </li>
114
+ <li class="login-list-item">
115
+ <a href="#" class="login-link">
116
+ <span class="login-link-label">
117
+ Marketing Cloud
118
+ </span>
119
+ <dx-icon
120
+ class="login-link-icon"
121
+ symbol="new_window"
122
+ size="xsmall"
123
+ ></dx-icon>
124
+ </a>
125
+ </li>
126
+ </ul>
127
+ </div>
128
+ <div class="login-section">
129
+ <span class="login-section-title">
130
+ Community & Resources
131
+ </span>
132
+ <ul>
133
+ <li class="login-list-item">
134
+ <a href={loginUrl} class="login-link">
135
+ <span class="login-link-label">
136
+ Trailblazer.me
137
+ </span>
138
+ </a>
139
+ <div class="login-link-blurb">
140
+ Login into access a rich community of
141
+ Salesforce Developers, get ideas, and find
142
+ solutions.
143
+ </div>
144
+ </li>
145
+ </ul>
146
+ </div>
147
+ </div>
148
+ </div>
149
+ </dx-popover>
150
+ </div>
151
+ </template>
@@ -0,0 +1,105 @@
1
+ import { api, LightningElement } from "lwc";
2
+
3
+ const TBID_BASE_URL = "https://dev1-trailblazer-identity.cs192.force.com";
4
+ const TBID_SETTINGS_URL = `${TBID_BASE_URL}/settings`;
5
+ const TBID_PROFILE_URL = `${TBID_BASE_URL}/id`;
6
+ const TBID_API_BASE_URL = "https://development.developer.salesforce.com/tbid";
7
+ const TBID_API_LOGOUT_URL = `${TBID_API_BASE_URL}/logout`;
8
+ const TBID_API_USERINFO_URL = `https://development.developer.salesforce.com/tbid/userinfo`;
9
+ const TBID_API_LOGIN_URL = `${TBID_API_BASE_URL}/dologin`;
10
+ // const TBID_LOGOUT_URL = `${TBID_BASE_URL}/services/auth/idp/oidc/logout`;
11
+
12
+ export interface UserInfo {
13
+ avatarImgSrc?: string;
14
+ firstName?: string;
15
+ lastName?: string;
16
+ username?: string;
17
+ readonly fullName: string;
18
+ }
19
+
20
+ export default class AvatarButton extends LightningElement {
21
+ @api size: "small" | "medium" | "large" = "medium";
22
+
23
+ private userInfo: UserInfo = {
24
+ get fullName() {
25
+ if (this.firstName && this.lastName) {
26
+ return `${this.firstName} ${this.lastName}`;
27
+ }
28
+ return this.firstName || this.lastName || "";
29
+ }
30
+ };
31
+ private isLoading = false;
32
+ private settingsUrl = TBID_SETTINGS_URL;
33
+ private profileUrl = TBID_PROFILE_URL;
34
+ private loginUrl = TBID_API_LOGIN_URL;
35
+ private _didReceiveUserInfo = false;
36
+
37
+ private get isLoggedIn() {
38
+ return this._didReceiveUserInfo;
39
+ }
40
+
41
+ connectedCallback() {
42
+ window.addEventListener("tbid-login", this.handleSsoLogin);
43
+ window.addEventListener("tbid-logout", this.handleLogout);
44
+
45
+ // Whenever this component is added to the DOM, immediately request user info; if the user is logged in
46
+ // and we receive the info, this component will display the avatar UI; otherwise, it will display the login
47
+ // button.
48
+ this.requestUserInfo();
49
+ }
50
+
51
+ disconnectedCallback() {
52
+ window.removeEventListener("tbid-login", this.handleSsoLogin);
53
+ window.removeEventListener("tbid-logout", this.handleLogout);
54
+ }
55
+
56
+ // This will only be called for "seamless SSO" login by the embedded login widget (SFWidget) on the website, if it exists.
57
+ private handleSsoLogin(event: Event) {
58
+ const userInfo = (event as CustomEvent).detail;
59
+ if (userInfo) {
60
+ this.updateAvatarWithUserInfo(userInfo);
61
+ this._didReceiveUserInfo = true;
62
+ }
63
+ }
64
+
65
+ // This can be called *either* by the embedded login widget (SFWidget) on the website, if it exists,
66
+ // in the event of "seamless SSO" logout, *or* by a user clicking logout on this component.
67
+ private async handleLogout() {
68
+ this._didReceiveUserInfo = false;
69
+ this.isLoading = false;
70
+ this.updateAvatarWithUserInfo({}); // clear old info
71
+
72
+ fetch(TBID_API_LOGOUT_URL); // no need to await this
73
+ }
74
+
75
+ private updateAvatarWithUserInfo(userInfo: any) {
76
+ if (!userInfo) {
77
+ return;
78
+ }
79
+
80
+ this.userInfo.avatarImgSrc = userInfo.photos?.thumbnail;
81
+ this.userInfo.firstName = userInfo.given_name;
82
+ this.userInfo.lastName = userInfo.family_name;
83
+ this.userInfo.username = userInfo.preferred_username;
84
+ // TODO: Consider displaying initials if no photo. Is there always a photo?
85
+ }
86
+
87
+ private async requestUserInfo() {
88
+ this.isLoading = true;
89
+
90
+ try {
91
+ const userInfoRes = await fetch(TBID_API_USERINFO_URL);
92
+
93
+ if (userInfoRes.ok) {
94
+ const userInfo = await userInfoRes.json();
95
+ this.updateAvatarWithUserInfo(userInfo);
96
+ this._didReceiveUserInfo = true;
97
+ }
98
+ } catch (ex) {
99
+ // TODO: Something not directly related to auth went wrong. Unsure what to do here.
100
+ console.error("Invalid response received from TBID API, aborting.");
101
+ }
102
+
103
+ this.isLoading = false;
104
+ }
105
+ }
@@ -1,7 +1,6 @@
1
1
  import { LightningElement, api, wire, track } from "lwc";
2
2
  import { CodeBlockTheme, CodeBlockLanguage } from "typings/custom";
3
3
  import cx from "classnames";
4
- import { detect } from "dxUtils/codeLanguageDetector";
5
4
  import Prism from "dxUtils/prismjs";
6
5
  import {
7
6
  getLocalStorageData,
@@ -213,18 +212,6 @@ export default class CodeBlock extends LightningElement {
213
212
  ) || this.allLanguages[0];
214
213
  this.selectedLanguageLabel = this.selectedLanguage.label;
215
214
  this.selectedLanguageId = this.selectedLanguage.id;
216
-
217
- // Auto detect code language if it isn't specified
218
- if (!this.language) {
219
- const detectedLanguage = detect(this.codeBlock);
220
- this.selectedLanguage = {
221
- label: detectedLanguage,
222
- id: detectedLanguage.toLowerCase()
223
- };
224
- this.selectedLanguageLabel = detectedLanguage;
225
- this.selectedLanguageId = detectedLanguage.toLowerCase();
226
- }
227
-
228
215
  this.formatCodeBlock();
229
216
  this._codeBlockRendered = true;
230
217
  }
@@ -19,6 +19,7 @@
19
19
  onstatechange={handleStateChange}
20
20
  ></dx-header-search>
21
21
  </div>
22
+ <dx-avatar-button></dx-avatar-button>
22
23
  <div if:true={showSignup} class="header-login-signup">
23
24
  <dx-button
24
25
  aria-label="Sign Up For Salesforce Developer Edition"
@@ -3,6 +3,6 @@ import { LightningElement, api } from "lwc";
3
3
  export default class Logo extends LightningElement {
4
4
  @api href: string = "/";
5
5
  @api imgSrc: string = "/assets/svg/salesforce-cloud.svg";
6
- @api imgAlt: string = "Salesforce log";
6
+ @api imgAlt: string = "Salesforce logo";
7
7
  @api label!: string;
8
8
  }
@@ -1,439 +0,0 @@
1
- const LANGUAGES = {
2
- JavaScript: [
3
- // undefined keyword
4
- { pattern: /undefined/g, points: 2 },
5
- // console.log('ayy lmao')
6
- { pattern: /console\.log( )*\(/, points: 2 },
7
- // Variable declaration
8
- { pattern: /(var|const|let)( )+\w+( )*=?/, points: 2 },
9
- // Array/Object declaration
10
- { pattern: /(('|").+('|")( )*|\w+):( )*[{[]/, points: 2 },
11
- // === operator
12
- { pattern: /===/g, points: 1 },
13
- // !== operator
14
- { pattern: /!==/g, points: 1 },
15
- // Function definition
16
- {
17
- pattern: /function\*?(( )+[$\w]+( )*\(.*\)|( )*\(.*\))/g,
18
- points: 1
19
- },
20
- // null keyword
21
- { pattern: /null/g, points: 1 },
22
- // lambda expression
23
- { pattern: /\(.*\)( )*=>( )*.+/, points: 1 },
24
- // (else )if statement
25
- { pattern: /(else )?if( )+\(.+\)/, points: 1 },
26
- // while loop
27
- { pattern: /while( )+\(.+\)/, points: 1 },
28
- // C style variable declaration.
29
- {
30
- pattern: /(^|\s)(char|long|int|float|double)( )+\w+( )*=?/,
31
- points: -1
32
- },
33
- // pointer
34
- { pattern: /(\w+)( )*\*( )*\w+/, points: -1 },
35
- // HTML <script> tag
36
- {
37
- pattern: /<(\/)?script( type=('|")text\/javascript('|"))?>/,
38
- points: -50
39
- },
40
- // ES6 import / export
41
- {
42
- pattern:
43
- /(import|export(\s+)default)\s+({\s+[\w\s,]+\s+}|\w+)\s+from\s/,
44
- points: 2
45
- },
46
- // ES6 arrow function
47
- { pattern: /\([^()]{0,}\)\s+=>(\s+{)?/, points: 3 }
48
- // () => {}
49
- // (a) => {}
50
- // (a, b) => {}
51
- // ({ a, b}) => {}
52
- // ([ a, b ]) => {}
53
- ],
54
-
55
- C: [
56
- // Primitive variable declaration.
57
- { pattern: /(char|long|int|float|double)( )+\w+( )*=?/, points: 2 },
58
- // malloc function call
59
- { pattern: /malloc\(.+\)/, points: 2 },
60
- // #include <whatever.h>
61
- { pattern: /#include (<|")\w+\.h(>|")/, points: 2, nearTop: true },
62
- // pointer
63
- { pattern: /(\w+)( )*\*( )*\w+/, points: 2 },
64
- // Variable declaration and/or initialisation.
65
- { pattern: /(\w+)( )+\w+(;|( )*=)/, points: 1 },
66
- // Array declaration.
67
- { pattern: /(\w+)( )+\w+\[.+\]/, points: 1 },
68
- // #define macro
69
- { pattern: /#define( )+.+/, points: 1 },
70
- // NULL constant
71
- { pattern: /NULL/, points: 1 },
72
- // void keyword
73
- { pattern: /void/g, points: 1 },
74
- // (else )if statement
75
- { pattern: /(else )?if( )*\(.+\)/, points: 1 },
76
- // while loop
77
- { pattern: /while( )+\(.+\)/, points: 1 },
78
- // printf function
79
- { pattern: /(printf|puts)( )*\(.+\)/, points: 1 },
80
- // new Keyword from C++
81
- { pattern: /new \w+/, points: -1 },
82
- // Single quote multicharacter string
83
- { pattern: /'.{2,}'/, points: -1 },
84
- // JS variable declaration
85
- { pattern: /var( )+\w+( )*=?/, points: -1 }
86
- ],
87
-
88
- "C++": [
89
- // Primitive variable declaration.
90
- { pattern: /(char|long|int|float|double)( )+\w+( )*=?/, points: 2 },
91
- // #include <whatever.h>
92
- {
93
- pattern: /#include( )*(<|")\w+(\.h)?(>|")/,
94
- points: 2,
95
- nearTop: true
96
- },
97
- // using namespace something
98
- { pattern: /using( )+namespace( )+.+( )*;/, points: 2 },
99
- // template declaration
100
- { pattern: /template( )*<.*>/, points: 2 },
101
- // std
102
- { pattern: /std::\w+/g, points: 2 },
103
- // cout/cin/endl
104
- { pattern: /(cout|cin|endl)/g, points: 2 },
105
- // Visibility specifiers
106
- { pattern: /(public|protected|private):/, points: 2 },
107
- // nullptr
108
- { pattern: /nullptr/, points: 2 },
109
- // new Keyword
110
- { pattern: /new \w+(\(.*\))?/, points: 1 },
111
- // #define macro
112
- { pattern: /#define( )+.+/, points: 1 },
113
- // template usage
114
- { pattern: /\w+<\w+>/, points: 1 },
115
- // class keyword
116
- { pattern: /class( )+\w+/, points: 1 },
117
- // void keyword
118
- { pattern: /void/g, points: 1 },
119
- // (else )if statement
120
- { pattern: /(else )?if( )*\(.+\)/, points: 1 },
121
- // while loop
122
- { pattern: /while( )+\(.+\)/, points: 1 },
123
- // Scope operator
124
- { pattern: /\w*::\w+/, points: 1 },
125
- // Single quote multicharacter string
126
- { pattern: /'.{2,}'/, points: -1 },
127
- // Java List/ArrayList
128
- {
129
- pattern: /(List<\w+>|ArrayList<\w*>( )*\(.*\))(( )+[\w]+|;)/,
130
- points: -1
131
- }
132
- ],
133
-
134
- Python: [
135
- // Function definition
136
- { pattern: /def( )+\w+\(.*\)( )*:/, points: 2 },
137
- // while loop
138
- { pattern: /while (.+):/, points: 2 },
139
- // from library import something
140
- { pattern: /from [\w.]+ import (\w+|\*)/, points: 2 },
141
- // class keyword
142
- { pattern: /class( )*\w+(\(( )*\w+( )*\))?( )*:/, points: 2 },
143
- // if keyword
144
- { pattern: /if( )+(.+)( )*:/, points: 2 },
145
- // elif keyword
146
- { pattern: /elif( )+(.+)( )*:/, points: 2 },
147
- // else keyword
148
- { pattern: /else:/, points: 2 },
149
- // for loop
150
- { pattern: /for (\w+|\(?\w+,( )*\w+\)?) in (.+):/, points: 2 },
151
- // Python variable declaration.
152
- { pattern: /\w+( )*=( )*\w+(?!;)(\n|$)/, points: 1 },
153
- // import something
154
- { pattern: /import ([[^.]\w])+/, points: 1, nearTop: true },
155
- // print statement/function
156
- { pattern: /print((( )*\(.+\))|( )+.+)/, points: 1 },
157
- // &&/|| operators
158
- { pattern: /(&{2}|\|{2})/, points: -1 }
159
- ],
160
-
161
- Java: [
162
- // System.out.println() etc.
163
- { pattern: /System\.(in|out)\.\w+/, points: 2 },
164
- // Class variable declarations
165
- {
166
- pattern: /(private|protected|public)( )*\w+( )*\w+(( )*=( )*[\w])?/,
167
- points: 2
168
- },
169
- // Method
170
- {
171
- pattern: /(private|protected|public)( )*\w+( )*[\w]+\(.+\)/,
172
- points: 2
173
- },
174
- // String class
175
- { pattern: /(^|\s)(String)( )+[\w]+( )*=?/, points: 2 },
176
- // List/ArrayList
177
- {
178
- pattern: /(List<\w+>|ArrayList<\w*>( )*\(.*\))(( )+[\w]+|;)/,
179
- points: 2
180
- },
181
- // class keyword
182
- { pattern: /(public( )*)?class( )*\w+/, points: 2 },
183
- // Array declaration.
184
- { pattern: /(\w+)(\[( )*\])+( )+\w+/, points: 2 },
185
- // final keyword
186
- { pattern: /final( )*\w+/, points: 2 },
187
- // getter & setter
188
- { pattern: /\w+\.(get|set)\(.+\)/, points: 2 },
189
- // new Keyword (Java)
190
- { pattern: /new [A-Z]\w*( )*\(.+\)/, points: 2 },
191
- // C style variable declaration.
192
- {
193
- pattern: /(^|\s)(char|long|int|float|double)( )+[\w]+( )*=?/,
194
- points: 1
195
- },
196
- // extends/implements keywords
197
- { pattern: /(extends|implements)/, points: 2, nearTop: true },
198
- // null keyword
199
- { pattern: /null/g, points: 1 },
200
- // (else )if statement
201
- { pattern: /(else )?if( )*\(.+\)/, points: 1 },
202
- // while loop
203
- { pattern: /while( )+\(.+\)/, points: 1 },
204
- // void keyword
205
- { pattern: /void/g, points: 1 },
206
- // const
207
- { pattern: /const( )*\w+/, points: -1 },
208
- // pointer
209
- { pattern: /(\w+)( )*\*( )*\w+/, points: -1 },
210
- // Single quote multicharacter string
211
- { pattern: /'.{2,}'/, points: -1 },
212
- // C style include
213
- {
214
- pattern: /#include( )*(<|")\w+(\.h)?(>|")/,
215
- points: -1,
216
- nearTop: true
217
- }
218
- ],
219
-
220
- HTML: [
221
- {
222
- pattern: /<!DOCTYPE (html|HTML PUBLIC .+)>/,
223
- points: 2,
224
- nearTop: true
225
- },
226
- // Tags
227
- {
228
- pattern: /<[a-z0-9]+(( )*[\w]+=('|").+('|")( )*)?>.*<\/[a-z0-9]+>/g,
229
- points: 2
230
- },
231
- // Properties
232
- { pattern: /[a-z-]+=("|').+("|')/g, points: 2 },
233
- // PHP tag
234
- { pattern: /<\?php/, points: -50 }
235
- ],
236
-
237
- CSS: [
238
- // Properties
239
- { pattern: /[a-z-]+:(?!:).+;/, points: 2 },
240
- // <style> tag from HTML
241
- { pattern: /<(\/)?style>/, points: -50 }
242
- ],
243
-
244
- Ruby: [
245
- // require/include
246
- {
247
- pattern: /(require|include)( )+'\w+(\.rb)?'/,
248
- points: 2,
249
- nearTop: true
250
- },
251
- // Function definition
252
- { pattern: /def( )+\w+( )*(\(.+\))?( )*\n/, points: 2 },
253
- // Instance variables
254
- { pattern: /@\w+/, points: 2 },
255
- // Boolean property
256
- { pattern: /\.\w+\?/, points: 2 },
257
- // puts (Ruby print)
258
- { pattern: /puts( )+("|').+("|')/, points: 2 },
259
- // Inheriting class
260
- { pattern: /class [A-Z]\w*( )*<( )*([A-Z]\w*(::)?)+/, points: 2 },
261
- // attr_accessor
262
- { pattern: /attr_accessor( )+(:\w+(,( )*)?)+/, points: 2 },
263
- // new
264
- { pattern: /\w+\.new( )+/, points: 2 },
265
- // elsif keyword
266
- { pattern: /elsif/, points: 2 },
267
- // do
268
- { pattern: /do( )*\|(\w+(,( )*\w+)?)+\|/, points: 2 },
269
- // for loop
270
- { pattern: /for (\w+|\(?\w+,( )*\w+\)?) in (.+)/, points: 1 },
271
- // nil keyword
272
- { pattern: /nil/, points: 1 },
273
- // Scope operator
274
- { pattern: /[A-Z]\w*::[A-Z]\w*/, points: 1 }
275
- ],
276
-
277
- Go: [
278
- // package something
279
- { pattern: /package( )+[a-z]+\n/, points: 2, nearTop: true },
280
- // import
281
- {
282
- pattern: /(import( )*\(( )*\n)|(import( )+"[a-z0-9/.]+")/,
283
- points: 2,
284
- nearTop: true
285
- },
286
- // error check
287
- { pattern: /if.+err( )*!=( )*nil.+{/, points: 2 },
288
- // Go print
289
- { pattern: /fmt\.Print(f|ln)?\(.*\)/, points: 2 },
290
- // function
291
- { pattern: /func(( )+\w+( )*)?\(.*\).*{/, points: 2 },
292
- // variable initialisation
293
- { pattern: /\w+( )*:=( )*.+[^;\n]/, points: 2 },
294
- // if/else if
295
- { pattern: /(}( )*else( )*)?if[^()]+{/, points: 2 },
296
- // var/const declaration
297
- { pattern: /(var|const)( )+\w+( )+[\w*]+(\n|( )*=|$)/, points: 2 },
298
- // public access on package
299
- { pattern: /[a-z]+\.[A-Z]\w*/, points: 1 },
300
- // nil keyword
301
- { pattern: /nil/, points: 1 },
302
- // Single quote multicharacter string
303
- { pattern: /'.{2,}'/, points: -1 }
304
- ],
305
-
306
- PHP: [
307
- // PHP tag
308
- { pattern: /<\?php/, points: 2 },
309
- // PHP style variables.
310
- { pattern: /\$\w+/, points: 2 },
311
- // use Something\Something;
312
- { pattern: /use( )+\w+(\\\w+)+( )*;/, points: 2, nearTop: true },
313
- // arrow
314
- { pattern: /\$\w+->\w+/, points: 2 },
315
- // require/include
316
- {
317
- pattern:
318
- /(require|include)(_once)?( )*\(?( )*('|").+\.php('|")( )*\)?( )*;/,
319
- points: 2
320
- },
321
- // echo 'something';
322
- { pattern: /echo( )+('|").+('|")( )*;/, points: 1 },
323
- // NULL constant
324
- { pattern: /NULL/, points: 1 },
325
- // new keyword
326
- { pattern: /new( )+((\\\w+)+|\w+)(\(.*\))?/, points: 1 },
327
- // Function definition
328
- { pattern: /function(( )+[$\w]+\(.*\)|( )*\(.*\))/g, points: 1 },
329
- // (else)if statement
330
- { pattern: /(else)?if( )+\(.+\)/, points: 1 },
331
- // scope operator
332
- { pattern: /\w+::\w+/, points: 1 },
333
- // === operator
334
- { pattern: /===/g, points: 1 },
335
- // !== operator
336
- { pattern: /!==/g, points: 1 },
337
- // C/JS style variable declaration.
338
- {
339
- pattern: /(^|\s)(var|char|long|int|float|double)( )+\w+( )*=?/,
340
- points: -1
341
- }
342
- ],
343
-
344
- Unknown: []
345
- };
346
-
347
- function getPoints(language, lineOfCode, checkers) {
348
- return checkers
349
- .map((checker) => {
350
- if (checker.pattern.test(lineOfCode)) {
351
- return checker.points;
352
- }
353
- return 0;
354
- })
355
- .reduce((memo, num) => memo + num, 0);
356
- }
357
-
358
- function detect(snippet, options) {
359
- const opts = Object.assign(
360
- {
361
- heuristic: true,
362
- statistics: false
363
- },
364
- options || {}
365
- );
366
-
367
- let linesOfCode = snippet
368
- .replace(/\r\n?/g, "\n")
369
- .replace(/\n{2,}/g, "\n")
370
- .split("\n");
371
-
372
- function nearTop(index) {
373
- if (linesOfCode.length <= 10) {
374
- return true;
375
- }
376
- return index < linesOfCode.length / 10;
377
- }
378
-
379
- if (opts.heuristic && linesOfCode.length > 500) {
380
- linesOfCode = linesOfCode.filter((lineOfCode, index) => {
381
- if (nearTop(index)) {
382
- return true;
383
- }
384
- return index % Math.ceil(linesOfCode.length / 500) === 0;
385
- });
386
- }
387
-
388
- const pairs = Object.keys(LANGUAGES).map((key) => {
389
- return { language: key, checkers: LANGUAGES[key] };
390
- });
391
-
392
- const results = pairs.map((pair) => {
393
- const language = pair.language;
394
- const checkers = pair.checkers;
395
-
396
- if (language === "Unknown") {
397
- return { language: "Unknown", points: 1 };
398
- }
399
-
400
- const pointsList = linesOfCode.map((lineOfCode, index) => {
401
- if (!nearTop(index)) {
402
- return getPoints(
403
- language,
404
- lineOfCode,
405
- checkers.filter((checker) => !checker.nearTop)
406
- );
407
- }
408
- return getPoints(language, lineOfCode, checkers);
409
- });
410
-
411
- const points = pointsList.reduce((memo, num) => memo + num);
412
-
413
- return { language, points };
414
- });
415
-
416
- const sortedResult = results.sort(
417
- (prev, next) => prev.points - next.points
418
- );
419
- const bestResult = sortedResult[sortedResult.length - 1];
420
-
421
- if (opts.statistics) {
422
- const statistics = {};
423
- results.forEach((result) => {
424
- statistics[result.language] = result.points;
425
- });
426
- return { detected: bestResult.language, statistics };
427
- }
428
-
429
- return bestResult.language;
430
- }
431
-
432
- const languages = Object.keys(LANGUAGES);
433
- const LANG = {};
434
-
435
- languages.forEach((language) => {
436
- LANG[language] = language;
437
- });
438
-
439
- export { detect, languages, LANG };