UNPKG

9.82 kBJavaScriptView Raw
1import { DOCUMENT, CommonModule } from '@angular/common';
2import { ɵɵdefineInjectable, ɵɵinject, Injectable, Inject, Optional, EventEmitter, Directive, Input, Output, HostListener, ViewContainerRef, TemplateRef, NgModule } from '@angular/core';
3import { WINDOW } from 'ngx-window-token';
4import { Subject } from 'rxjs';
5
6/**
7 * The following code is heavily copied from https://github.com/zenorocha/clipboard.js
8 */
9class ClipboardService {
10 constructor(document, window) {
11 this.document = document;
12 this.window = window;
13 this.copySubject = new Subject();
14 this.copyResponse$ = this.copySubject.asObservable();
15 this.config = {};
16 }
17 configure(config) {
18 this.config = config;
19 }
20 copy(content) {
21 if (!this.isSupported || !content) {
22 return this.pushCopyResponse({ isSuccess: false, content });
23 }
24 const copyResult = this.copyFromContent(content);
25 if (copyResult) {
26 return this.pushCopyResponse({ content, isSuccess: copyResult });
27 }
28 return this.pushCopyResponse({ isSuccess: false, content });
29 }
30 get isSupported() {
31 return !!this.document.queryCommandSupported && !!this.document.queryCommandSupported('copy') && !!this.window;
32 }
33 isTargetValid(element) {
34 if (element instanceof HTMLInputElement || element instanceof HTMLTextAreaElement) {
35 if (element.hasAttribute('disabled')) {
36 throw new Error('Invalid "target" attribute. Please use "readonly" instead of "disabled" attribute');
37 }
38 return true;
39 }
40 throw new Error('Target should be input or textarea');
41 }
42 /**
43 * Attempts to copy from an input `targetElm`
44 */
45 copyFromInputElement(targetElm, isFocus = true) {
46 try {
47 this.selectTarget(targetElm);
48 const re = this.copyText();
49 this.clearSelection(isFocus ? targetElm : undefined, this.window);
50 return re && this.isCopySuccessInIE11();
51 }
52 catch (error) {
53 return false;
54 }
55 }
56 /**
57 * This is a hack for IE11 to return `true` even if copy fails.
58 */
59 isCopySuccessInIE11() {
60 const clipboardData = this.window['clipboardData'];
61 if (clipboardData && clipboardData.getData) {
62 if (!clipboardData.getData('Text')) {
63 return false;
64 }
65 }
66 return true;
67 }
68 /**
69 * Creates a fake textarea element, sets its value from `text` property,
70 * and makes a selection on it.
71 */
72 copyFromContent(content, container = this.document.body) {
73 // check if the temp textarea still belongs to the current container.
74 // In case we have multiple places using ngx-clipboard, one is in a modal using container but the other one is not.
75 if (this.tempTextArea && !container.contains(this.tempTextArea)) {
76 this.destroy(this.tempTextArea.parentElement || undefined);
77 }
78 if (!this.tempTextArea) {
79 this.tempTextArea = this.createTempTextArea(this.document, this.window);
80 try {
81 container.appendChild(this.tempTextArea);
82 }
83 catch (error) {
84 throw new Error('Container should be a Dom element');
85 }
86 }
87 this.tempTextArea.value = content;
88 const toReturn = this.copyFromInputElement(this.tempTextArea, false);
89 if (this.config.cleanUpAfterCopy) {
90 this.destroy(this.tempTextArea.parentElement || undefined);
91 }
92 return toReturn;
93 }
94 /**
95 * Remove temporary textarea if any exists.
96 */
97 destroy(container = this.document.body) {
98 if (this.tempTextArea) {
99 container.removeChild(this.tempTextArea);
100 // removeChild doesn't remove the reference from memory
101 this.tempTextArea = undefined;
102 }
103 }
104 /**
105 * Select the target html input element.
106 */
107 selectTarget(inputElement) {
108 inputElement.select();
109 inputElement.setSelectionRange(0, inputElement.value.length);
110 return inputElement.value.length;
111 }
112 copyText() {
113 return this.document.execCommand('copy');
114 }
115 /**
116 * Moves focus away from `target` and back to the trigger, removes current selection.
117 */
118 clearSelection(inputElement, window) {
119 var _a;
120 inputElement && inputElement.focus();
121 (_a = window.getSelection()) === null || _a === void 0 ? void 0 : _a.removeAllRanges();
122 }
123 /**
124 * Creates a fake textarea for copy command.
125 */
126 createTempTextArea(doc, window) {
127 const isRTL = doc.documentElement.getAttribute('dir') === 'rtl';
128 let ta;
129 ta = doc.createElement('textarea');
130 // Prevent zooming on iOS
131 ta.style.fontSize = '12pt';
132 // Reset box model
133 ta.style.border = '0';
134 ta.style.padding = '0';
135 ta.style.margin = '0';
136 // Move element out of screen horizontally
137 ta.style.position = 'absolute';
138 ta.style[isRTL ? 'right' : 'left'] = '-9999px';
139 // Move element to the same position vertically
140 const yPosition = window.pageYOffset || doc.documentElement.scrollTop;
141 ta.style.top = yPosition + 'px';
142 ta.setAttribute('readonly', '');
143 return ta;
144 }
145 /**
146 * Pushes copy operation response to copySubject, to provide global access
147 * to the response.
148 */
149 pushCopyResponse(response) {
150 this.copySubject.next(response);
151 }
152 /**
153 * @deprecated use pushCopyResponse instead.
154 */
155 pushCopyReponse(response) {
156 this.pushCopyResponse(response);
157 }
158}
159ClipboardService.ɵprov = ɵɵdefineInjectable({ factory: function ClipboardService_Factory() { return new ClipboardService(ɵɵinject(DOCUMENT), ɵɵinject(WINDOW, 8)); }, token: ClipboardService, providedIn: "root" });
160ClipboardService.decorators = [
161 { type: Injectable, args: [{ providedIn: 'root' },] }
162];
163ClipboardService.ctorParameters = () => [
164 { type: undefined, decorators: [{ type: Inject, args: [DOCUMENT,] }] },
165 { type: undefined, decorators: [{ type: Optional }, { type: Inject, args: [WINDOW,] }] }
166];
167
168class ClipboardDirective {
169 constructor(clipboardSrv) {
170 this.clipboardSrv = clipboardSrv;
171 this.cbOnSuccess = new EventEmitter();
172 this.cbOnError = new EventEmitter();
173 }
174 // tslint:disable-next-line:no-empty
175 ngOnInit() { }
176 ngOnDestroy() {
177 this.clipboardSrv.destroy(this.container);
178 }
179 onClick(event) {
180 if (!this.clipboardSrv.isSupported) {
181 this.handleResult(false, undefined, event);
182 }
183 else if (this.targetElm && this.clipboardSrv.isTargetValid(this.targetElm)) {
184 this.handleResult(this.clipboardSrv.copyFromInputElement(this.targetElm), this.targetElm.value, event);
185 }
186 else if (this.cbContent) {
187 this.handleResult(this.clipboardSrv.copyFromContent(this.cbContent, this.container), this.cbContent, event);
188 }
189 }
190 /**
191 * Fires an event based on the copy operation result.
192 * @param succeeded
193 */
194 handleResult(succeeded, copiedContent, event) {
195 let response = {
196 isSuccess: succeeded,
197 event
198 };
199 if (succeeded) {
200 response = Object.assign(response, {
201 content: copiedContent,
202 successMessage: this.cbSuccessMsg
203 });
204 this.cbOnSuccess.emit(response);
205 }
206 else {
207 this.cbOnError.emit(response);
208 }
209 this.clipboardSrv.pushCopyResponse(response);
210 }
211}
212ClipboardDirective.decorators = [
213 { type: Directive, args: [{
214 selector: '[ngxClipboard]'
215 },] }
216];
217ClipboardDirective.ctorParameters = () => [
218 { type: ClipboardService }
219];
220ClipboardDirective.propDecorators = {
221 targetElm: [{ type: Input, args: ['ngxClipboard',] }],
222 container: [{ type: Input }],
223 cbContent: [{ type: Input }],
224 cbSuccessMsg: [{ type: Input }],
225 cbOnSuccess: [{ type: Output }],
226 cbOnError: [{ type: Output }],
227 onClick: [{ type: HostListener, args: ['click', ['$event.target'],] }]
228};
229
230class ClipboardIfSupportedDirective {
231 constructor(_clipboardService, _viewContainerRef, _templateRef) {
232 this._clipboardService = _clipboardService;
233 this._viewContainerRef = _viewContainerRef;
234 this._templateRef = _templateRef;
235 }
236 ngOnInit() {
237 if (this._clipboardService.isSupported) {
238 this._viewContainerRef.createEmbeddedView(this._templateRef);
239 }
240 }
241}
242ClipboardIfSupportedDirective.decorators = [
243 { type: Directive, args: [{
244 selector: '[ngxClipboardIfSupported]'
245 },] }
246];
247ClipboardIfSupportedDirective.ctorParameters = () => [
248 { type: ClipboardService },
249 { type: ViewContainerRef },
250 { type: TemplateRef }
251];
252
253class ClipboardModule {
254}
255ClipboardModule.decorators = [
256 { type: NgModule, args: [{
257 imports: [CommonModule],
258 declarations: [ClipboardDirective, ClipboardIfSupportedDirective],
259 exports: [ClipboardDirective, ClipboardIfSupportedDirective]
260 },] }
261];
262
263/*
264 * Public API Surface of ngx-clipboard
265 */
266
267/**
268 * Generated bundle index. Do not edit.
269 */
270
271export { ClipboardDirective, ClipboardIfSupportedDirective, ClipboardModule, ClipboardService };
272//# sourceMappingURL=ngx-clipboard.js.map