]> git-server-git.apps.pok.os.sepia.ceph.com Git - ceph.git/commitdiff
mgr/dashboard: replace cdCopy2ClipboardButton Directive with a component 38269/head
authorCourtney Caldwell <ccaldwel@redhat.com>
Tue, 15 Dec 2020 16:41:18 +0000 (11:41 -0500)
committerCourtney Caldwell <ccaldwel@redhat.com>
Tue, 15 Dec 2020 16:41:18 +0000 (11:41 -0500)
Replaces Directive-Based copy2ClipboardButton with a Component-based version.
Further replaces all instances of Directive use with Component version.
Similar toastr logic used.
Fixes: https://tracker.ceph.com/issues/44960
Signed-off-by: Courtney Caldwell <ccaldwel@redhat.com>
18 files changed:
src/pybind/mgr/dashboard/frontend/src/app/ceph/block/iscsi-target-discovery-modal/iscsi-target-discovery-modal.component.html
src/pybind/mgr/dashboard/frontend/src/app/ceph/block/iscsi-target-form/iscsi-target-form.component.html
src/pybind/mgr/dashboard/frontend/src/app/ceph/block/mirroring/bootstrap-create-modal/bootstrap-create-modal.component.html
src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/logs/logs.component.html
src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/services/service-form/service-form.component.html
src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/telemetry/telemetry.component.html
src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-user-form/rgw-user-form.component.html
src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-user-s3-key-modal/rgw-user-s3-key-modal.component.html
src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-user-subuser-modal/rgw-user-subuser-modal.component.html
src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-user-swift-key-modal/rgw-user-swift-key-modal.component.html
src/pybind/mgr/dashboard/frontend/src/app/shared/components/components.module.ts
src/pybind/mgr/dashboard/frontend/src/app/shared/components/copy2clipboard-button/copy2clipboard-button.component.html [new file with mode: 0644]
src/pybind/mgr/dashboard/frontend/src/app/shared/components/copy2clipboard-button/copy2clipboard-button.component.scss [new file with mode: 0644]
src/pybind/mgr/dashboard/frontend/src/app/shared/components/copy2clipboard-button/copy2clipboard-button.component.spec.ts [new file with mode: 0644]
src/pybind/mgr/dashboard/frontend/src/app/shared/components/copy2clipboard-button/copy2clipboard-button.component.ts [new file with mode: 0644]
src/pybind/mgr/dashboard/frontend/src/app/shared/directives/copy2clipboard-button.directive.spec.ts [deleted file]
src/pybind/mgr/dashboard/frontend/src/app/shared/directives/copy2clipboard-button.directive.ts [deleted file]
src/pybind/mgr/dashboard/frontend/src/app/shared/directives/directives.module.ts

index c7724e9ec9a955910eb58634fd015062bebeec9c..d84ea787f13963cbb27063630154b33c39175cb3 100644 (file)
                         class="btn btn-light"
                         cdPasswordButton="password">
                 </button>
-                <button type="button"
-                        class="btn btn-light"
-                        cdCopy2ClipboardButton="password">
-                </button>
+                <cd-copy-2-clipboard-button source="password">
+                </cd-copy-2-clipboard-button>
               </span>
             </div>
             <span class="invalid-feedback"
                         class="btn btn-light"
                         cdPasswordButton="mutual_password">
                 </button>
-                <button type="button"
-                        class="btn btn-light"
-                        cdCopy2ClipboardButton="mutual_password">
-                </button>
+                <cd-copy-2-clipboard-button source="mutual_password">
+                </cd-copy-2-clipboard-button>
               </span>
             </div>
             <span class="invalid-feedback"
index ed719e41612140e73d8ed8297f0661c03da4ea5d..491e84903a38bf19d013cb6d62dd8e972204f4a1 100644 (file)
                           class="btn btn-light"
                           cdPasswordButton="target_password">
                   </button>
-                  <button type="button"
-                          class="btn btn-light"
-                          cdCopy2ClipboardButton="target_password">
-                  </button>
+                  <cd-copy-2-clipboard-button source="target_password">
+                  </cd-copy-2-clipboard-button>
                 </span>
               </div>
 
                           class="btn btn-light"
                           cdPasswordButton="target_mutual_password">
                   </button>
-                  <button type="button"
-                          class="btn btn-light"
-                          cdCopy2ClipboardButton="target_mutual_password">
-                  </button>
+                  <cd-copy-2-clipboard-button source="target_mutual_password">
+                  </cd-copy-2-clipboard-button>
                 </span>
               </div>
 
                                   class="btn btn-light"
                                   [cdPasswordButton]="'password' + ii">
                           </button>
-                          <button type="button"
-                                  class="btn btn-light"
-                                  [cdCopy2ClipboardButton]="'password' + ii">
-                          </button>
+                          <cd-copy-2-clipboard-button [source]="'password' + ii">
+                          </cd-copy-2-clipboard-button>
                         </span>
                       </div>
                       <span class="invalid-feedback"
                                   class="btn btn-light"
                                   [cdPasswordButton]="'mutual_password' + ii">
                           </button>
-                          <button type="button"
-                                  class="btn btn-light"
-                                  [cdCopy2ClipboardButton]="'mutual_password' + ii">
-                          </button>
+                          <cd-copy-2-clipboard-button [source]="'mutual_password' + ii">
+                          </cd-copy-2-clipboard-button>
                         </span>
                       </div>
                       <span class="invalid-feedback"
index 6ab22cc70f7e4f5701026a8270c5b13c80e75d34..a31ab933c7525d7be708ffec87ab65617616f991 100755 (executable)
                     readonly>
           </textarea>
         </div>
-        <button class="btn btn-primary mb-3 float-right"
-                aria-label="Copy to Clipboard"
-                i18n-aria-label
-                title="Copy to Clipboard"
-                i18n-title
-                cdCopy2ClipboardButton="token">
-          <ng-container i18n>Copy to Clipboard</ng-container>
-        </button>
+        <cd-copy-2-clipboard-button class="float-right"
+                                    source="token">
+        </cd-copy-2-clipboard-button>
       </div>
 
       <div class="modal-footer">
index 3b9caabbe733036f51e032218aba74e608ed3170..b255854619b6b63ec3ce876f7c9bec3ac65c8952 100644 (file)
                                 [textItem]="logText"
                                 fileName="cluster_log">
             </cd-download-button>
-            <button type="button"
-                    class="btn btn-light"
-                    [cdCopy2ClipboardButton]="logText"
-                    [byId]="false"></button>
+            <cd-copy-2-clipboard-button
+                    [source]="logText"
+                    [byId]="false">
+            </cd-copy-2-clipboard-button>
           </div>
           <div class="card-body">
             <p *ngFor="let line of clog">
                                 [textItem]="logText"
                                 fileName="audit_log">
             </cd-download-button>
-            <button type="button"
-                    class="btn btn-light"
-                    [cdCopy2ClipboardButton]="logText"
-                    [byId]="false"></button>
+            <cd-copy-2-clipboard-button
+                    [source]="logText"
+                    [byId]="false">
+            </cd-copy-2-clipboard-button>
           </div>
           <div class="card-body">
             <p *ngFor="let line of audit_log">
index 6f9d47ce4210113032593e61280a4f85421758e7..e2d4f01140ccef8c1032f8546215de28a9c653b5 100644 (file)
                           class="btn btn-light"
                           cdPasswordButton="api_password">
                   </button>
-                  <button type="button"
-                          class="btn btn-light"
-                          cdCopy2ClipboardButton="api_password">
-                  </button>
+                  <cd-copy-2-clipboard-button source="api_password">
+                  </cd-copy-2-clipboard-button>
                 </span>
                 <span class="invalid-feedback"
                       *ngIf="serviceForm.showError('api_password', frm, 'required')"
index 877374967a44502f833e75049ba8e5a048421b00..69690668392f9891818dc49792431082692a4294 100644 (file)
               <button type="button"
                       class="btn btn-light"
                       (click)="next()">
-                <ng-container i18n>{{ actionLabels.NEXT }}</ng-container>
+                <ng-container>{{ actionLabels.NEXT }}</ng-container>
               </button>
             </div>
           </div>
                   <cd-download-button [objectItem]="report"
                                       fileName="telemetry_report">
                   </cd-download-button>
-                  <button type="button"
-                          class="btn btn-light"
-                          cdCopy2ClipboardButton="report">
-                  </button>
+                  <cd-copy-2-clipboard-button source="report">
+                  </cd-copy-2-clipboard-button>
                 </div>
               </div>
             </div>
index 25b7bce95306b81dc5a1fd901593d1feeedad8a0..360015125b5d55c5eb79f2ce4c2a870d19df349a 100644 (file)
                           class="btn btn-light"
                           cdPasswordButton="access_key">
                   </button>
-                  <button type="button"
-                          class="btn btn-light"
-                          cdCopy2ClipboardButton="access_key">
-                  </button>
+                  <cd-copy-2-clipboard-button source="access_key">
+                  </cd-copy-2-clipboard-button>
                 </span>
               </div>
               <span class="invalid-feedback"
                           class="btn btn-light"
                           cdPasswordButton="secret_key">
                   </button>
-                  <button type="button"
-                          class="btn btn-light"
-                          cdCopy2ClipboardButton="secret_key">
-                  </button>
+                  <cd-copy-2-clipboard-button source="secret_key">
+                  </cd-copy-2-clipboard-button>
                 </span>
               </div>
               <span class="invalid-feedback"
index 0f8edcaf5ddefa5fad2a8dc6bee44d7ce09e4d4d..a68534286c17cd75d79f78478824347c67c2d4ec 100644 (file)
                         class="btn btn-light"
                         cdPasswordButton="access_key">
                 </button>
-                <button type="button"
-                        class="btn btn-light"
-                        cdCopy2ClipboardButton="access_key">
-                </button>
+                <cd-copy-2-clipboard-button source="access_key">
+                </cd-copy-2-clipboard-button>
               </span>
             </div>
             <span class="invalid-feedback"
                         class="btn btn-light"
                         cdPasswordButton="secret_key">
                 </button>
-                <button type="button"
-                        class="btn btn-light"
-                        cdCopy2ClipboardButton="secret_key">
-                </button>
+                <cd-copy-2-clipboard-button source="secret_key">
+                </cd-copy-2-clipboard-button>
               </span>
             </div>
             <span class="invalid-feedback"
index 66c59cb3f2c62028393f5fd90765a181eb2b20b9..cbe5ae3976dd829d99d8a39e17552b25053b6be1 100644 (file)
                           class="btn btn-light"
                           cdPasswordButton="secret_key">
                   </button>
-                  <button type="button"
-                          class="btn btn-light"
-                          cdCopy2ClipboardButton="secret_key">
-                  </button>
+                  <cd-copy-2-clipboard-button source="secret_key">
+                  </cd-copy-2-clipboard-button>
                 </span>
               </div>
               <span class="invalid-feedback"
index 8121dbc31e377b79c7e5e907fb44fc9b4e6cd9ee..200a6c544a6fbb4ba4401a41da1ccb75ccd9fc3a 100644 (file)
                         class="btn btn-light"
                         cdPasswordButton="secret_key">
                 </button>
-                <button type="button"
-                        class="btn btn-light"
-                        cdCopy2ClipboardButton="secret_key">
-                </button>
+                <cd-copy-2-clipboard-button source="secret_key">
+                </cd-copy-2-clipboard-button>
               </span>
             </div>
           </div>
index 5defbf36a3a598ee201873d70f381f46e10ebbfd..bccbc645bab1bf0d23e4557f9cec58ad25802593 100644 (file)
@@ -22,6 +22,7 @@ import { AlertPanelComponent } from './alert-panel/alert-panel.component';
 import { BackButtonComponent } from './back-button/back-button.component';
 import { ConfigOptionComponent } from './config-option/config-option.component';
 import { ConfirmationModalComponent } from './confirmation-modal/confirmation-modal.component';
+import { Copy2ClipboardButtonComponent } from './copy2clipboard-button/copy2clipboard-button.component';
 import { CriticalConfirmationModalComponent } from './critical-confirmation-modal/critical-confirmation-modal.component';
 import { DateTimePickerComponent } from './date-time-picker/date-time-picker.component';
 import { DocComponent } from './doc/doc.component';
@@ -88,6 +89,7 @@ import { UsageBarComponent } from './usage-bar/usage-bar.component';
     OrchestratorDocPanelComponent,
     DateTimePickerComponent,
     DocComponent,
+    Copy2ClipboardButtonComponent,
     DownloadButtonComponent,
     FormButtonPanelComponent
   ],
@@ -113,6 +115,7 @@ import { UsageBarComponent } from './usage-bar/usage-bar.component';
     OrchestratorDocPanelComponent,
     DateTimePickerComponent,
     DocComponent,
+    Copy2ClipboardButtonComponent,
     DownloadButtonComponent,
     FormButtonPanelComponent
   ]
diff --git a/src/pybind/mgr/dashboard/frontend/src/app/shared/components/copy2clipboard-button/copy2clipboard-button.component.html b/src/pybind/mgr/dashboard/frontend/src/app/shared/components/copy2clipboard-button/copy2clipboard-button.component.html
new file mode 100644 (file)
index 0000000..25a3f3c
--- /dev/null
@@ -0,0 +1,7 @@
+<button (click)="onClick()"
+        type="button"
+        class="btn btn-light"
+        i18n-title
+        title="Copy to Clipboard">
+  <i [ngClass]="[icons.clipboard]"></i>
+</button>
diff --git a/src/pybind/mgr/dashboard/frontend/src/app/shared/components/copy2clipboard-button/copy2clipboard-button.component.scss b/src/pybind/mgr/dashboard/frontend/src/app/shared/components/copy2clipboard-button/copy2clipboard-button.component.scss
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/src/pybind/mgr/dashboard/frontend/src/app/shared/components/copy2clipboard-button/copy2clipboard-button.component.spec.ts b/src/pybind/mgr/dashboard/frontend/src/app/shared/components/copy2clipboard-button/copy2clipboard-button.component.spec.ts
new file mode 100644 (file)
index 0000000..2842793
--- /dev/null
@@ -0,0 +1,65 @@
+import { TestBed } from '@angular/core/testing';
+
+import * as BrowserDetect from 'detect-browser';
+import { ToastrService } from 'ngx-toastr';
+
+import { configureTestBed } from '~/testing/unit-test-helper';
+import { Copy2ClipboardButtonComponent } from './copy2clipboard-button.component';
+
+describe('Copy2ClipboardButtonComponent', () => {
+  let component: Copy2ClipboardButtonComponent;
+
+  configureTestBed({
+    providers: [
+      {
+        provide: ToastrService,
+        useValue: {
+          error: () => true,
+          success: () => true
+        }
+      }
+    ]
+  });
+
+  it('should create an instance', () => {
+    component = new Copy2ClipboardButtonComponent(null);
+    expect(component).toBeTruthy();
+  });
+
+  describe('test onClick behaviours', () => {
+    let toastrService: ToastrService;
+    let queryFn: jasmine.Spy;
+    let writeTextFn: jasmine.Spy;
+
+    beforeEach(() => {
+      toastrService = TestBed.inject(ToastrService);
+      component = new Copy2ClipboardButtonComponent(toastrService);
+      spyOn<any>(component, 'getText').and.returnValue('foo');
+      Object.assign(navigator, {
+        permissions: { query: jest.fn() },
+        clipboard: {
+          writeText: jest.fn()
+        }
+      });
+      queryFn = spyOn(navigator.permissions, 'query');
+    });
+
+    it('should not call permissions API', () => {
+      spyOn(BrowserDetect, 'detect').and.returnValue({ name: 'firefox' });
+      writeTextFn = spyOn(navigator.clipboard, 'writeText').and.returnValue(
+        new Promise<void>((resolve, _) => {
+          resolve();
+        })
+      );
+      component.onClick();
+      expect(queryFn).not.toHaveBeenCalled();
+      expect(writeTextFn).toHaveBeenCalledWith('foo');
+    });
+
+    it('should call permissions API', () => {
+      spyOn(BrowserDetect, 'detect').and.returnValue({ name: 'chrome' });
+      component.onClick();
+      expect(queryFn).toHaveBeenCalled();
+    });
+  });
+});
diff --git a/src/pybind/mgr/dashboard/frontend/src/app/shared/components/copy2clipboard-button/copy2clipboard-button.component.ts b/src/pybind/mgr/dashboard/frontend/src/app/shared/components/copy2clipboard-button/copy2clipboard-button.component.ts
new file mode 100644 (file)
index 0000000..2cc656b
--- /dev/null
@@ -0,0 +1,55 @@
+import { Component, HostListener, Input } from '@angular/core';
+
+import { detect } from 'detect-browser';
+import { ToastrService } from 'ngx-toastr';
+
+import { Icons } from '~/app/shared/enum/icons.enum';
+
+@Component({
+  selector: 'cd-copy-2-clipboard-button',
+  templateUrl: './copy2clipboard-button.component.html',
+  styleUrls: ['./copy2clipboard-button.component.scss']
+})
+export class Copy2ClipboardButtonComponent {
+  @Input()
+  private source: string;
+
+  @Input()
+  byId = true;
+
+  icons = Icons;
+
+  constructor(private toastr: ToastrService) {}
+
+  private getText(): string {
+    const element = document.getElementById(this.source) as HTMLInputElement;
+    return element.value;
+  }
+
+  @HostListener('click')
+  onClick() {
+    try {
+      const browser = detect();
+      const text = this.byId ? this.getText() : this.source;
+      const toastrFn = () => {
+        this.toastr.success('Copied text to the clipboard successfully.');
+      };
+      if (['firefox', 'ie', 'ios', 'safari'].includes(browser.name)) {
+        // Various browsers do not support the `Permissions API`.
+        // https://developer.mozilla.org/en-US/docs/Web/API/Permissions_API#Browser_compatibility
+        navigator.clipboard.writeText(text).then(() => toastrFn());
+      } else {
+        // Checking if we have the clipboard-write permission
+        navigator.permissions
+          .query({ name: 'clipboard-write' as PermissionName })
+          .then((result: any) => {
+            if (result.state === 'granted' || result.state === 'prompt') {
+              navigator.clipboard.writeText(text).then(() => toastrFn());
+            }
+          });
+      }
+    } catch (_) {
+      this.toastr.error('Failed to copy text to the clipboard.');
+    }
+  }
+}
diff --git a/src/pybind/mgr/dashboard/frontend/src/app/shared/directives/copy2clipboard-button.directive.spec.ts b/src/pybind/mgr/dashboard/frontend/src/app/shared/directives/copy2clipboard-button.directive.spec.ts
deleted file mode 100644 (file)
index b9681a9..0000000
+++ /dev/null
@@ -1,65 +0,0 @@
-import { TestBed } from '@angular/core/testing';
-
-import * as BrowserDetect from 'detect-browser';
-import { ToastrService } from 'ngx-toastr';
-
-import { configureTestBed } from '~/testing/unit-test-helper';
-import { Copy2ClipboardButtonDirective } from './copy2clipboard-button.directive';
-
-describe('Copy2clipboardButtonDirective', () => {
-  let directive: Copy2ClipboardButtonDirective;
-
-  configureTestBed({
-    providers: [
-      {
-        provide: ToastrService,
-        useValue: {
-          error: () => true,
-          success: () => true
-        }
-      }
-    ]
-  });
-
-  it('should create an instance', () => {
-    directive = new Copy2ClipboardButtonDirective(null, null, null);
-    expect(directive).toBeTruthy();
-  });
-
-  describe('test onClick behaviours', () => {
-    let toastrService: ToastrService;
-    let queryFn: jasmine.Spy;
-    let writeTextFn: jasmine.Spy;
-
-    beforeEach(() => {
-      toastrService = TestBed.inject(ToastrService);
-      directive = new Copy2ClipboardButtonDirective(null, null, toastrService);
-      spyOn<any>(directive, 'getText').and.returnValue('foo');
-      Object.assign(navigator, {
-        permissions: { query: jest.fn() },
-        clipboard: {
-          writeText: jest.fn()
-        }
-      });
-      queryFn = spyOn(navigator.permissions, 'query');
-    });
-
-    it('should not call permissions API', () => {
-      spyOn(BrowserDetect, 'detect').and.returnValue({ name: 'firefox' });
-      writeTextFn = spyOn(navigator.clipboard, 'writeText').and.returnValue(
-        new Promise<void>((resolve, _) => {
-          resolve();
-        })
-      );
-      directive.onClick();
-      expect(queryFn).not.toHaveBeenCalled();
-      expect(writeTextFn).toHaveBeenCalledWith('foo');
-    });
-
-    it('should call permissions API', () => {
-      spyOn(BrowserDetect, 'detect').and.returnValue({ name: 'chrome' });
-      directive.onClick();
-      expect(queryFn).toHaveBeenCalled();
-    });
-  });
-});
diff --git a/src/pybind/mgr/dashboard/frontend/src/app/shared/directives/copy2clipboard-button.directive.ts b/src/pybind/mgr/dashboard/frontend/src/app/shared/directives/copy2clipboard-button.directive.ts
deleted file mode 100644 (file)
index 218af07..0000000
+++ /dev/null
@@ -1,60 +0,0 @@
-import { Directive, ElementRef, HostListener, Input, OnInit, Renderer2 } from '@angular/core';
-
-import { detect } from 'detect-browser';
-import { ToastrService } from 'ngx-toastr';
-
-@Directive({
-  selector: '[cdCopy2ClipboardButton]'
-})
-export class Copy2ClipboardButtonDirective implements OnInit {
-  @Input()
-  private cdCopy2ClipboardButton: string;
-  @Input()
-  byId = true;
-
-  constructor(
-    private elementRef: ElementRef,
-    private renderer: Renderer2,
-    private toastr: ToastrService
-  ) {}
-
-  ngOnInit() {
-    const iElement = this.renderer.createElement('i');
-    this.renderer.addClass(iElement, 'fa');
-    this.renderer.addClass(iElement, 'fa-clipboard');
-    this.renderer.setAttribute(iElement, 'title', $localize`Copy to clipboard`);
-    this.renderer.appendChild(this.elementRef.nativeElement, iElement);
-  }
-
-  private getText(): string {
-    const element = document.getElementById(this.cdCopy2ClipboardButton) as HTMLInputElement;
-    return element.value;
-  }
-
-  @HostListener('click')
-  onClick() {
-    try {
-      const browser = detect();
-      const text = this.byId ? this.getText() : this.cdCopy2ClipboardButton;
-      const toastrFn = () => {
-        this.toastr.success('Copied text to the clipboard successfully.');
-      };
-      if (['firefox', 'ie', 'ios', 'safari'].includes(browser.name)) {
-        // Various browsers do not support the `Permissions API`.
-        // https://developer.mozilla.org/en-US/docs/Web/API/Permissions_API#Browser_compatibility
-        navigator.clipboard.writeText(text).then(() => toastrFn());
-      } else {
-        // Checking if we have the clipboard-write permission
-        navigator.permissions
-          .query({ name: 'clipboard-write' as PermissionName })
-          .then((result: any) => {
-            if (result.state === 'granted' || result.state === 'prompt') {
-              navigator.clipboard.writeText(text).then(() => toastrFn());
-            }
-          });
-      }
-    } catch (_) {
-      this.toastr.error('Failed to copy text to the clipboard.');
-    }
-  }
-}
index fd43d6621285b4f382db527be5eaadcc856fd207..00e5635d36b59be7057801748711b80141e6e181 100644 (file)
@@ -1,7 +1,6 @@
 import { NgModule } from '@angular/core';
 
 import { AutofocusDirective } from './autofocus.directive';
-import { Copy2ClipboardButtonDirective } from './copy2clipboard-button.directive';
 import { DimlessBinaryPerSecondDirective } from './dimless-binary-per-second.directive';
 import { DimlessBinaryDirective } from './dimless-binary.directive';
 import { FormInputDisableDirective } from './form-input-disable.directive';
@@ -20,7 +19,6 @@ import { TrimDirective } from './trim.directive';
   imports: [],
   declarations: [
     AutofocusDirective,
-    Copy2ClipboardButtonDirective,
     DimlessBinaryDirective,
     DimlessBinaryPerSecondDirective,
     PasswordButtonDirective,
@@ -37,7 +35,6 @@ import { TrimDirective } from './trim.directive';
   ],
   exports: [
     AutofocusDirective,
-    Copy2ClipboardButtonDirective,
     DimlessBinaryDirective,
     DimlessBinaryPerSecondDirective,
     PasswordButtonDirective,