diff --git a/src/app/button-page/button-page.component.ts b/src/app/button-page/button-page.component.ts index d9552ef..ddce78a 100644 --- a/src/app/button-page/button-page.component.ts +++ b/src/app/button-page/button-page.component.ts @@ -19,6 +19,7 @@ import { OBFBoard, Button, LoadBoardAction } from '../obfboard'; import { Subscription, Subscriber } from 'rxjs'; import { ScanningService, ScanningModel, ScannableCollectionProvider, ScannableCollection, Scannable } from '../scanning.service'; import { ConfigService } from '../config.service'; +import { CustomActionService } from '../custom-action.service'; class ScannableButton extends Scannable { static TYPE = 'OBFButton'; @@ -131,8 +132,13 @@ export class ButtonPageComponent implements OnInit, OnDestroy { ':space': this.speechbarService.space.bind(this.speechbarService) }; - constructor(private boardService: BoardService, private speechbarService: SpeechbarService, - private scanningService: ScanningService, private configService: ConfigService) { } + constructor( + private boardService: BoardService, + private speechbarService: SpeechbarService, + private scanningService: ScanningService, + private configService: ConfigService, + private customActionService: CustomActionService + ) { } ngOnInit() { this.loadBoard(); @@ -221,6 +227,8 @@ export class ButtonPageComponent implements OnInit, OnDestroy { if (action.startsWith('+')) { this.speechbarService.appendButton(button, action); + } else if (action.startsWith(':ext')) { + this.customActionService.handle(button, action); } else { const actionPerformer = this.actionPerformers[action]; diff --git a/src/app/custom-action.service.spec.ts b/src/app/custom-action.service.spec.ts new file mode 100644 index 0000000..cc97787 --- /dev/null +++ b/src/app/custom-action.service.spec.ts @@ -0,0 +1,15 @@ +import { TestBed, inject } from '@angular/core/testing'; + +import { CustomActionService } from './custom-action.service'; + +describe('CustomActionService', () => { + beforeEach(() => { + TestBed.configureTestingModule({ + providers: [CustomActionService] + }); + }); + + it('should be created', inject([CustomActionService], (service: CustomActionService) => { + expect(service).toBeTruthy(); + })); +}); diff --git a/src/app/custom-action.service.ts b/src/app/custom-action.service.ts new file mode 100644 index 0000000..4ad7a42 --- /dev/null +++ b/src/app/custom-action.service.ts @@ -0,0 +1,102 @@ +import { Injectable } from '@angular/core'; +import { Button } from './obfboard'; +import { LocalStorage } from 'ngx-store'; + +interface Scripts { + name: string; + src: string; +} +export const ScriptStore: Scripts[] = [ + { + name: 'lib1', + src: 'https://dl.dropbox.com/s/yf06gz438yz9ftb/helloLib.js?dl=1' + }, + { + name: 'iftttLib', + src: '/assets/libraries/iftttLib.js' + } +]; + +declare var document: any; + +@Injectable({ + providedIn: 'root' +}) +export class CustomActionService { + + private scripts: any = {}; + + @LocalStorage() + private _iftttLibConfig; + + constructor() { + ScriptStore.forEach((script: any) => { + this.scripts[script.name] = { + loaded: false, + src: script.src + }; + }); + } + + load(...scripts: string[]) { + const promises: any[] = []; + scripts.forEach((script) => promises.push(this.loadScript(script))); + return Promise.all(promises); + } + + // TODO: tidy this up and get config involved + loadScript(name: string) { + return new Promise((resolve, reject) => { + // resolve if already loaded + if (this.scripts[name].loaded) { + resolve({ script: name, loaded: true, status: 'Already Loaded' }); + } else { + // load script + const script = document.createElement('script'); + script.type = 'text/javascript'; + script.src = this.scripts[name].src; + if (script.readyState) { // IE + script.onreadystatechange = () => { + if (script.readyState === 'loaded' || script.readyState === 'complete') { + script.onreadystatechange = null; + this.scripts[name].loaded = true; + resolve({ script: name, loaded: true, status: 'Loaded' }); + } + }; + } else { // Others + script.onload = () => { + this.scripts[name].loaded = true; + resolve({ script: name, loaded: true, status: 'Loaded' }); + }; + } + script.onerror = (error: any) => resolve({ script: name, loaded: false, status: 'Loaded' }); + document.getElementsByTagName('head')[0].appendChild(script); + } + }); + } + + handle(button: Button, action: string) { + + if (action.startsWith(':ext_ovf_js:')) { + console.log(action); + const jsCall = action.slice(12); + // TODO: this doesn't make sense now! Load everything up front? + // Or interogate for namespace...(would still require loading!) + this.load('iftttLib').then(data => { + console.log('script loaded ', data); + let func = window; + for (const ns of jsCall.split('.')) { + // TODO: sometimes this will fail! + func = func[ns]; + } + if (typeof func === 'function') { + const context = { + 'button': button + }; + const config = this._iftttLibConfig; + (func)(context, config); + } + }).catch(error => console.log(error)); + } + } +} diff --git a/src/assets/libraries/iftttLib.js b/src/assets/libraries/iftttLib.js new file mode 100644 index 0000000..1feee32 --- /dev/null +++ b/src/assets/libraries/iftttLib.js @@ -0,0 +1,16 @@ +var iftttLib = (function () { + var self = {}; + + var sendRequest = function(trigger, key) { + var url = "https://maker.ifttt.com/trigger/" + trigger + "/with/key/" + key + var http = new XMLHttpRequest(); + http.open("GET", url); + http.send(); + } + + self.trigger = function(context, config) { + sendRequest(context.button.id, config['key']); + } + + return self; +}());