diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md
index e122304b6..0ec87ae98 100644
--- a/CONTRIBUTORS.md
+++ b/CONTRIBUTORS.md
@@ -321,6 +321,7 @@ All definitions files include a header with the author and editors, so at some p
* [SignalR](http://www.asp.net/signalr) (by [Boris Yankov](https://github.com/borisyankov))
* [simple-cw-node](https://github.com/astronaughts/simple-cw-node) (by [vvakame](https://github.com/vvakame))
* [Sinon](http://sinonjs.org/) (by [William Sears](https://github.com/mrbigdog2u))
+* [SIPml](http://sipml5.org/) (by [Adriaan Groenenboom](https://github.com/chookies))
* [sjcl](http://crypto.stanford.edu/sjcl/) (by [Eugene Chernyshov](https://github.com/Evgenus))
* [SlickGrid](https://github.com/mleibman/SlickGrid) (by [Josh Baldwin](https://github.com/jbaldwin))
* [smoothie](https://github.com/joewalnes/smoothie) (by [Mike H. Hawley](https://github.com/mikehhawley) and [Drew Noakes](https://drewnoakes.com))
diff --git a/sipml/sipml-test.ts b/sipml/sipml-test.ts
new file mode 100644
index 000000000..5fed26fc6
--- /dev/null
+++ b/sipml/sipml-test.ts
@@ -0,0 +1,169 @@
+///
+
+/* Code borrowed from http://sipml5.org/docgen/index.html?svn=224 */
+
+var acceptMessage = (e: any)=> {
+ e.newSession.accept(); // e.newSession.reject(); to reject the message
+ console.info('SMS-content = ' + e.getContentString() + ' and SMS-content-type = ' + e.getContentType());
+};
+var acceptCall = (e:any)=> {
+ e.newSession.accept(); // e.newSession.reject() to reject the call
+};
+
+ /* Initialize the engine */
+var readyCallback = (e:any)=> {
+ createSipStack(); // see next section
+};
+var errorCallback = (e:any)=> {
+ console.error('Failed to initialize the engine: ' + e.message);
+}
+SIPml.init(readyCallback, errorCallback);
+
+/* Create a SIP stack */
+var sipStack: SIPml.Stack;
+var eventsListener = (e:any)=> {
+ if(e.type == 'started'){
+ login();
+ }
+ else if(e.type == 'i_new_message'){ // incoming new SIP MESSAGE (SMS-like)
+ acceptMessage(e);
+ }
+ else if(e.type == 'i_new_call'){ // incoming audio/video call
+ acceptCall(e);
+ }
+}
+
+function createSipStack(){
+ sipStack = new SIPml.Stack('blaat');
+ sipStack = new SIPml.Stack({
+ realm: 'example.org', // mandatory: domain name
+ impi: 'bob', // mandatory: authorization name (IMS Private Identity)
+ impu: 'sip:bob@example.org', // mandatory: valid SIP Uri (IMS Public Identity)
+ password: 'mysecret', // optional
+ display_name: 'Bob legend', // optional
+ websocket_proxy_url: 'wss://sipml5.org:10062', // optional
+ outbound_proxy_url: 'udp://example.org:5060', // optional
+ enable_rtcweb_breaker: false, // optional
+ events_listener: { events: '*', listener: eventsListener }, // optional: '*' means all events
+ sip_headers: [ // optional
+ { name: 'User-Agent', value: 'IM-client/OMA1.0 sipML5-v1.0.0.0' },
+ { name: 'Organization', value: 'Doubango Telecom' }
+ ]
+ }
+ );
+}
+sipStack.start();
+
+/* Register/login */
+var registerSession: SIPml.Session.Registration;
+var eventsListener = (e:any)=>{
+ console.info('session event = ' + e.type);
+ if(e.type == 'connected' && e.session == registerSession){
+ makeCall();
+ sendMessage();
+ publishPresence();
+ subscribePresence('johndoe'); // watch johndoe's presence status change
+ }
+}
+var login = ()=>{
+ registerSession = sipStack.newSession('register', {
+ events_listener: { events: '*', listener: eventsListener } // optional: '*' means all events
+ });
+ registerSession.register();
+}
+
+/* Making/receiving audio/video call */
+var callSession: SIPml.Session.Call;
+var eventsListener = (e:any)=>{
+ console.info('session event = ' + e.type);
+}
+var makeCall = ()=>{
+ callSession = sipStack.newSession('call-audiovideo', {
+ video_local: document.getElementById('video-local'),
+ video_remote: document.getElementById('video-remote'),
+ audio_remote: document.getElementById('audio-remote'),
+ events_listener: { events: '*', listener: eventsListener } // optional: '*' means all events
+ });
+ callSession.call('johndoe');
+}
+var acceptCall = (e:any)=>{
+ e.newSession.accept(); // e.newSession.reject() to reject the call
+}
+
+/* Send/receive SIP MESSAGE (SMS-like) */
+var messageSession: SIPml.Session.Message;
+var eventsListener = (e:any)=>{
+ console.info('session event = ' + e.type);
+}
+var sendMessage = ()=>{
+ messageSession = sipStack.newSession('message', {
+ events_listener: { events: '*', listener: eventsListener } // optional: '*' means all events
+ });
+ messageSession.send('johndoe', 'Pêche à la moule', 'text/plain;charset=utf-8');
+}
+
+/* Publish presence status */
+var publishSession: SIPml.Session.Publish;
+var eventsListener = (e:any)=>{
+ console.info('session event = ' + e.type);
+}
+var publishPresence = ()=>{
+ publishSession = sipStack.newSession('publish', {
+ events_listener: { events: '*', listener: eventsListener } // optional: '*' means all events
+ });
+ var contentType = 'application/pidf+xml';
+ var content = '\n' +
+ '\n' +
+ '\n' +
+ '\n'+
+ ' open\n' +
+ ' away\n' +
+ '\n' +
+ 'tel:+33600000000\n' +
+ 'Bonjour de Paris :)\n' +
+ '\n' +
+ '';
+
+ // send the PUBLISH request
+ publishSession.publish(content, contentType,{
+ expires: 200,
+ sip_caps: [
+ { name: '+g.oma.sip-im' },
+ { name: '+sip.ice' },
+ { name: 'language', value: '\"en,fr\"' }
+ ],
+ sip_headers: [
+ { name: 'Event', value: 'presence' },
+ { name: 'Organization', value: 'Doubango Telecom' }
+ ]
+ });
+}
+
+/* Subscribe for presence status */
+var subscribeSession: SIPml.Session.Subscribe;
+var eventsListener = (e:any)=>{
+ console.info('session event = ' + e.type);
+ if(e.type == 'i_notify'){
+ console.info('NOTIFY content = ' + e.getContentString());
+ console.info('NOTIFY content-type = ' + e.getContentType());
+ }
+}
+var subscribePresence = (to:string)=>{
+ subscribeSession = sipStack.newSession('subscribe', {
+ expires: 200,
+ events_listener: { events: '*', listener: eventsListener },
+ sip_headers: [
+ { name: 'Event', value: 'presence' }, // only notify for 'presence' events
+ { name: 'Accept', value: 'application/pidf+xml' } // supported content types (COMMA-sparated)
+ ],
+ sip_caps: [
+ { name: '+g.oma.sip-im', value: null },
+ { name: '+audio', value: null },
+ { name: 'language', value: '\"en,fr\"' }
+ ]
+ });
+ // start watching for entity's presence status (You may track event type 'connected' to be sure that the request has been accepted by the server)
+ subscribeSession.subscribe(to);
+}
diff --git a/sipml/sipml.d.ts b/sipml/sipml.d.ts
new file mode 100644
index 000000000..a4fe32784
--- /dev/null
+++ b/sipml/sipml.d.ts
@@ -0,0 +1,149 @@
+// Type definitions for SIPml5
+// Project: http://sipml5.org/
+// Definitions by: A. Groenenboom
+// Definitions: https://github.com/borisyankov/DefinitelyTyped
+
+declare module SIPml {
+ class Event {
+ public description: string;
+ public type: string;
+
+ public getContent(): Object;
+ public getContentString(): string;
+ public getContentType(): Object;
+ public getSipResponseCode(): number;
+ }
+
+ class EventTarget {
+ public addEventListener(type: any, listener: Function): void;
+ public removeEventListener(type: any): void;
+ }
+
+ class Session {
+ public accept(configuration?: Session.Configuration): number;
+ public getId(): number;
+ public getRemoteFriendlyName(): string;
+ public getRemoteUri(): string;
+ public reject(configuration?: Session.Configuration): number;
+ public setConfiguration(configuration?: Session.Configuration): void;
+ }
+
+ export module Session {
+ interface Configuration {
+ audio_remote?: HTMLAudioElement;
+ bandwidth?: Object;
+ expires?: number;
+ from?: string;
+ sip_caps?: Object[];
+ sip_headers?: Object[];
+ video_local?: HTMLVideoElement;
+ video_remote?: HTMLVideoElement;
+ video_size?: Object;
+ }
+
+ class Call extends Session {
+ public acceptTransfer(configuration?: Session.Configuration): number;
+ public call(to: string, configuration?: Session.Configuration): number;
+ public dtmf(): number;
+ public hangup(configuration?: Session.Configuration): number;
+ public hold(configuration?: Session.Configuration): number;
+ public info(): number;
+ public rejectTransfer(): number;
+ public resume(): number;
+ public transfer(): number;
+ }
+
+ class Event extends SIPml.Event {
+ public session: Session;
+
+ public getTransferDestinationFriendlyName(): string;
+ }
+
+ class Message extends Session {
+ public send(to: string, content?: any, contentType?: string, configuration?: Session.Configuration): number;
+ }
+
+ class Publish extends Session {
+ public publish(content?: any, contentType?: string, configuration?: Session.Configuration): number;
+
+ public unpublish(configuration?: Session.Configuration): void;
+ }
+
+ class Registration extends Session {
+ public register(configuration?: Session.Configuration): void;
+ public unregister(configuration?: Session.Configuration): void;
+ }
+
+ class Subscribe extends Session {
+ public subscribe(to: string, configuration?: Session.Configuration): number;
+ public unsubscribe(configuration?: Session.Configuration): number;
+ }
+ }
+
+ class Stack extends EventTarget {
+ public constructor(configuration?: Stack.Configuration);
+ public setConfiguration(configuration: Stack.Configuration): number;
+ public newSession(type: string, configuration: Stack.Configuration): any;
+ public start(): number;
+ public stop(timeout: number): number;
+ }
+
+ export module Stack {
+ interface Configuration {
+ bandwidth?: Object;
+ display_name?: string;
+ enable_click2call?: boolean;
+ enable_early_ims?: boolean;
+ enable_media_stream_cache?: boolean;
+ enable_rtcweb_breaker?: boolean;
+ events_listener?: Object;
+ ice_servers?: Object[];
+ impi?: string;
+ impu?: string;
+ outbound_proxy_url?: string;
+ password?: string;
+ realm?: string;
+ sip_headers?: Object[];
+ video_size?: Object;
+ websocket_proxy_url?: string;
+ }
+
+ class Event extends SIPml.Event {
+ public description: string;
+ public newSession: Session;
+ public type: string;
+ }
+ }
+
+ function getNavigatorFriendlyName(): string;
+
+ function getNavigatorVersion(): string;
+
+ function getSystemFriendlyName(): string;
+
+ function getWebRtc4AllVersion(): string;
+
+ function haveMediaStream(): boolean;
+
+ function init(readyCallback?: (e:any) => any, errorCallback?: (e:any) => any): boolean;
+
+ function isInitialized(): boolean;
+
+ function isNavigatorOutdated(): boolean;
+
+ function isReady(): boolean;
+
+ function isScreenShareSupported(): boolean;
+
+ function isWebRtcPluginOutdated(): boolean;
+
+ function isWebRtc4AllSupported(): boolean;
+
+ function isWebRtcSupported(): boolean;
+
+ function isWebSocketSupported(): boolean;
+
+ function setDebugLevel(level: string): void;
+
+ function setWebRtcType(type: string): boolean;
+}