{"id":0,"registry":{"0":{"scripts":[{"__isSmartRef__":true,"id":1},{"__isSmartRef__":true,"id":2},{"__isSmartRef__":true,"id":3},{"__isSmartRef__":true,"id":4}],"id":"4E4E9594-35D2-4DC2-9AB6-CC4EEB8354DA","shape":{"__isSmartRef__":true,"id":5},"__layered_droppingEnabled__":true,"halosEnabled":true,"registeredForMouseEvents":true,"showsHalos":false,"name":"WormHole","partsBinMetaInfo":{"__isSmartRef__":true,"id":6},"eventHandler":{"__isSmartRef__":true,"id":43},"derivationIds":[127,"F0291F62-F100-480C-AEBF-0230398F7983","AE8B5ED7-E305-45BC-BE4A-1C28A49EE12B","39BA345C-17FA-45A0-BAC2-F0CF5654ACF8","FE91EA31-D8AE-461B-8A49-95DA5FBAE5B4","F38439A3-2F4E-4AC2-BF73-2633C00B1393","6748F5B5-2284-4748-A6EE-F1A261CBAD0B","BBAFB862-93AE-4F89-BCFF-9F1C67B7FE4C","7EC17E78-593A-4CC8-B7E3-859BCBBFE785","BD8BECDB-A9A1-4009-B433-C925F0AFCCFC","F2677A9D-5855-4DF2-B248-94545B34B762","D52188B3-7DC0-410E-96B1-50892AC00A37","1C17567C-751A-447E-A93C-0CE8CAEEF262","C31986AF-AB07-427F-BD76-E0F3859AC975","99E256EC-042E-4A46-8253-162EC4FACB6A","CC1894D6-C1DF-42B3-9695-56BC7F3D1AF8","2BCB7AFE-6CAC-42F3-BD92-0909AE5F6170"],"partTests":{"__isSmartRef__":true,"id":44},"_ClipMode":"auto","moved":true,"droppingEnabled":true,"isBeingDragged":false,"submorphs":[],"attributeConnections":[{"__isSmartRef__":true,"id":49}],"doNotSerialize":["audioContext","socket"],"doNotCopyProperties":["$$submorphs"],"wsUrl":"ws://localhost:1234","buffer":[],"sources":[],"isCopyMorphRef":true,"morphRefId":1,"sumSize":0,"prevScroll":[0,0],"$$submorphs":[],"sizeSum":0,"upstreamData":0,"downstreamData":0,"session":{"__isSmartRef__":true,"id":51},"sendCount":0,"audioBuffer":[],"playingAudioBuffer":false,"_Rotation":0,"_Scale":1,"idRequestCallbacks":[],"takeoverCallbacks":[],"knownStreams":[],"frameLoadingCallbacks":[],"__serializedExpressions__":["_Position"],"__serializedLivelyClosures__":{"__isSmartRef__":true,"id":72},"__LivelyClassName__":"lively.morphic.Box","__SourceModuleName__":"Global.lively.morphic.Core","_Position":"lively.pt(0.0,0.0)"},"1":{"target":{"__isSmartRef__":true,"id":0},"selector":"sendBuffer","args":[],"stopped":false,"tickTime":10,"__LivelyClassName__":"lively.morphic.TargetScript","__SourceModuleName__":"Global.lively.morphic.Core"},"2":{"target":{"__isSmartRef__":true,"id":0},"selector":"showKbps","args":[],"stopped":false,"tickTime":1000,"__LivelyClassName__":"lively.morphic.TargetScript","__SourceModuleName__":"Global.lively.morphic.Core"},"3":{"target":{"__isSmartRef__":true,"id":0},"selector":"checkAudioBufferStatus","args":[],"stopped":false,"tickTime":1000,"__LivelyClassName__":"lively.morphic.TargetScript","__SourceModuleName__":"Global.lively.morphic.Core"},"4":{"target":{"__isSmartRef__":true,"id":0},"selector":"checkSocketState","args":[],"stopped":false,"tickTime":1000,"__LivelyClassName__":"lively.morphic.TargetScript","__SourceModuleName__":"Global.lively.morphic.Core"},"5":{"_BorderWidth":1,"_ClipMode":"visible","_BorderRadius":3.335,"_Opacity":1,"_BorderStyle":"solid","__serializedExpressions__":["position","_Extent","_BorderColor","_Fill","_Padding"],"__LivelyClassName__":"lively.morphic.Shapes.Rectangle","__SourceModuleName__":"Global.lively.morphic.Shapes","position":"lively.pt(0.0,0.0)","_Extent":"lively.pt(776.0,487.0)","_BorderColor":"Color.rgb(0,0,0)","_Fill":"Color.rgb(255,255,255)","_Padding":"lively.rect(0,0,0,0)"},"6":{"partsSpaceName":"PartsBin/Felix/","comment":"Streams every dropped media morph to connected clients\n","migrationLevel":9,"partName":"WormHole","changes":[{"__isSmartRef__":true,"id":7},{"__isSmartRef__":true,"id":8},{"__isSmartRef__":true,"id":9},{"__isSmartRef__":true,"id":10},{"__isSmartRef__":true,"id":11},{"__isSmartRef__":true,"id":12},{"__isSmartRef__":true,"id":13},{"__isSmartRef__":true,"id":14},{"__isSmartRef__":true,"id":15},{"__isSmartRef__":true,"id":16},{"__isSmartRef__":true,"id":17},{"__isSmartRef__":true,"id":18},{"__isSmartRef__":true,"id":19},{"__isSmartRef__":true,"id":20},{"__isSmartRef__":true,"id":21},{"__isSmartRef__":true,"id":22},{"__isSmartRef__":true,"id":23},{"__isSmartRef__":true,"id":24},{"__isSmartRef__":true,"id":25},{"__isSmartRef__":true,"id":26},{"__isSmartRef__":true,"id":27},{"__isSmartRef__":true,"id":28},{"__isSmartRef__":true,"id":29},{"__isSmartRef__":true,"id":30},{"__isSmartRef__":true,"id":31},{"__isSmartRef__":true,"id":32},{"__isSmartRef__":true,"id":33},{"__isSmartRef__":true,"id":34},{"__isSmartRef__":true,"id":35},{"__isSmartRef__":true,"id":36},{"__isSmartRef__":true,"id":37},{"__isSmartRef__":true,"id":38},{"__isSmartRef__":true,"id":39},{"__isSmartRef__":true,"id":40},{"__isSmartRef__":true,"id":41},{"__isSmartRef__":true,"id":42}],"__serializedExpressions__":["lastModifiedDate"],"__LivelyClassName__":"lively.PartsBin.PartsBinMetaInfo","__SourceModuleName__":"Global.lively.PartsBin","lastModifiedDate":"new Date(\"Wed Dec 17 2014 17:11:07 GMT-0800 (PST)\")"},"7":{"author":"felix","message":"no comment","id":"B1F11F96-5A60-4506-9858-22D40B72BAC4","__serializedExpressions__":["date"],"date":"new Date(\"Wed Dec 10 2014 09:44:22 GMT-0800 (PST)\")"},"8":{"author":"felix","message":"new streaming interface","id":"7EBFBE17-E8C5-4E97-89B4-F74C5620AB88","__serializedExpressions__":["date"],"date":"new Date(\"Mon Dec 08 2014 17:06:54 GMT-0800 (PST)\")"},"9":{"author":"felix","message":"no comment","id":"AA761B82-E7F9-4A70-9986-311FFAFF1AAC","__serializedExpressions__":["date"],"date":"new Date(\"Mon Dec 01 2014 15:49:21 GMT-0800 (PST)\")"},"10":{"author":"felix","message":"no comment","id":"FB84CABA-968A-4BE7-B474-869EC6F2CB93","__serializedExpressions__":["date"],"date":"new Date(\"Fri Nov 21 2014 12:01:52 GMT-0800 (PST)\")"},"11":{"author":"felix","message":"no comment","id":"9B08EF37-FE23-401E-889A-121D2285DA04","__serializedExpressions__":["date"],"date":"new Date(\"Thu Nov 20 2014 17:18:32 GMT-0800 (PST)\")"},"12":{"author":"felix","message":"no comment","id":"08193759-8AC7-4682-9F4E-A46A1755AF60","__serializedExpressions__":["date"],"date":"new Date(\"Tue Nov 11 2014 11:09:11 GMT-0800 (PST)\")"},"13":{"author":"felix","message":"no comment","id":"6C7CAB95-87E0-4D58-8C82-D70FF6C75A2E","__serializedExpressions__":["date"],"date":"new Date(\"Tue Nov 11 2014 10:25:18 GMT-0800 (PST)\")"},"14":{"author":"felix","message":"no comment","id":"685C1E71-A1AF-43E7-BA5A-87D10E9F3AF6","__serializedExpressions__":["date"],"date":"new Date(\"Mon Nov 10 2014 16:25:06 GMT-0800 (PST)\")"},"15":{"author":"felix","message":"no comment","id":"704D13A7-009A-4885-9FB8-07F6A42F55F6","__serializedExpressions__":["date"],"date":"new Date(\"Mon Nov 10 2014 13:14:38 GMT-0800 (PST)\")"},"16":{"author":"lauritz","message":"dropping enabled for the blue rectangle","id":"81D350CB-D926-491F-8FF3-A6C9C831BDF2","__serializedExpressions__":["date"],"date":"new Date(\"Wed Mar 12 2014 07:00:28 GMT-0700 (PDT)\")"},"17":{"author":"jenslincke","message":"no comment","id":"ECD12BFF-E872-4918-8341-C6571E72F36D","__serializedExpressions__":["date"],"date":"new Date(\"Tue Feb 04 2014 01:24:27 GMT-0800 (PST)\")"},"18":{"author":"robertkrahn","message":"no comment","id":"FBBB9C99-B61C-4D5F-8750-6E2987DF0C9C","__serializedExpressions__":["date"],"date":"new Date(\"Thu Jun 20 2013 15:16:24 GMT-0700 (PDT)\")"},"19":{"author":"robertkrahn","message":"no comment","id":"67FD74CD-7AE4-4050-ADC7-4DF73804258D","__serializedExpressions__":["date"],"date":"new Date(\"Thu May 30 2013 00:18:06 GMT-0700 (PDT)\")"},"20":{"author":"jenslincke","message":"no comment","id":"AEB2F41B-2C88-4BBA-8875-93CD17CBA1C8","__serializedExpressions__":["date"],"date":"new Date(\"Wed Jan 23 2013 03:57:18 GMT-0800 (PST)\")"},"21":{"author":"undefined","message":"whoopsie","id":"3F17A2D2-3C24-424B-B0FA-E43112267D23","__serializedExpressions__":["date"],"date":"new Date(\"Thu May 03 2012 06:51:36 GMT-0700 (PDT)\")"},"22":{"author":"undefined","message":"whoopsie","id":"CEA5DCD5-2DB7-40AD-A776-262A7A0666FC","__serializedExpressions__":["date"],"date":"new Date(\"Thu May 03 2012 06:51:30 GMT-0700 (PDT)\")"},"23":{"author":"undefined","message":"sorry","id":"44B56D2E-9B59-4C67-A305-49A6E10E66C2","__serializedExpressions__":["date"],"date":"new Date(\"Wed Mar 14 2012 02:53:45 GMT-0700 (PDT)\")"},"24":{"author":"fbo","message":"","id":"BD1C654D-9100-4B66-BC62-B15FF2498B2B","__serializedExpressions__":["date"],"date":"new Date(\"Fri Feb 24 2012 17:59:09 GMT-0800 (PST)\")"},"25":{"author":"undefined","message":"css transitions","id":"1004E0FC-D96B-4F40-B3E0-F514A3FCFFD7","__serializedExpressions__":["date"],"date":"new Date(\"Wed Mar 14 2012 02:42:26 GMT-0700 (PDT)\")"},"26":{"author":"undefined","message":"suddenly, the rectangle became a CarDemo","id":"B5083AA8-9BAE-48DD-A6B4-FD7DB3998350","__serializedExpressions__":["date"],"date":"new Date(\"Mon Apr 09 2012 04:42:07 GMT-0700 (PDT)\")"},"27":{"author":"bgnauk","message":"no comment","id":"0124E570-50B7-4CE3-83A1-6E7BA89B5CD8","__serializedExpressions__":["date"],"date":"new Date(\"Mon Jun 18 2012 10:35:08 GMT-0700 (PDT)\")"},"28":{"author":"jenslincke","message":"I want my blue rectangle back!","id":"5AA9291A-869F-4D36-8095-4721B5A5B205","__serializedExpressions__":["date"],"date":"new Date(\"Thu Nov 22 2012 01:35:51 GMT-0800 (PST)\")"},"29":{"author":"jenslincke","message":"no comment","id":"A4B86A7E-A398-4C29-BEE0-4AA045ABABD2","__serializedExpressions__":["date"],"date":"new Date(\"Fri Jan 18 2013 07:42:31 GMT-0800 (PST)\")"},"30":{"author":"jenslincke","message":"added legend","id":"7ADA3049-56C1-4D32-874B-F664A79DDB20","__serializedExpressions__":["date"],"date":"new Date(\"Fri Jan 18 2013 07:47:05 GMT-0800 (PST)\")"},"31":{"author":"SAPLivelyScreen","message":"no comment","id":"EF145198-0649-41BD-A92E-521D98FEC49D","__serializedExpressions__":["date"],"date":"new Date(\"Wed May 29 2013 15:36:50 GMT-0700 (PDT)\")"},"32":{"author":"SAPLivelyScreen","message":"no comment","id":"7A44D8CA-6D5F-42A3-A4B4-4D7F51EECA52","__serializedExpressions__":["date"],"date":"new Date(\"Tue Jun 18 2013 11:49:27 GMT-0700 (PDT)\")"},"33":{"author":"robertkrahn","message":"no comment","id":"A452871F-4EB9-45D1-94B8-6065CBA7B8FD","__serializedExpressions__":["date"],"date":"new Date(\"Mon Jul 29 2013 23:20:51 GMT-0700 (PDT)\")"},"34":{"author":"jenslincke","message":"no comment","id":"ADFBF069-513C-4DE6-95A9-8AD4985C6508","__serializedExpressions__":["date"],"date":"new Date(\"Tue Feb 04 2014 01:23:53 GMT-0800 (PST)\")"},"35":{"author":"felix","message":"no comment","id":"C1907B5B-FB30-49C9-8B30-302A1543247C","__serializedExpressions__":["date"],"date":"new Date(\"Tue Nov 11 2014 11:21:52 GMT-0800 (PST)\")"},"36":{"author":"felix","message":"no comment","id":"5EE4166B-5E27-4F19-93F7-0107AC1F253C","__serializedExpressions__":["date"],"date":"new Date(\"Tue Nov 11 2014 11:35:20 GMT-0800 (PST)\")"},"37":{"author":"felix","message":"no comment","id":"B30B2B7D-4925-4576-A684-DDF6D42523A5","__serializedExpressions__":["date"],"date":"new Date(\"Tue Nov 11 2014 15:02:33 GMT-0800 (PST)\")"},"38":{"author":"felix","message":"no comment","id":"44836AC4-5D63-4FE8-8724-728BE550048E","__serializedExpressions__":["date"],"date":"new Date(\"Tue Nov 11 2014 15:24:07 GMT-0800 (PST)\")"},"39":{"author":"felix","message":"no comment","id":"40FFE19E-D9D5-4353-B434-2F1F4E5479E9","__serializedExpressions__":["date"],"date":"new Date(\"Fri Nov 14 2014 15:14:18 GMT-0800 (PST)\")"},"40":{"author":"felix","message":"no comment","id":"9CEB08FC-E60D-4786-8E2A-F39C380FA385","__serializedExpressions__":["date"],"date":"new Date(\"Tue Nov 18 2014 09:58:55 GMT-0800 (PST)\")"},"41":{"author":"felix","message":"no comment","id":"B9AB3390-1D1F-4909-8CCE-2874BF6A7905","__serializedExpressions__":["date"],"date":"new Date(\"Wed Dec 17 2014 17:11:07 GMT-0800 (PST)\")"},"42":{"__serializedExpressions__":["date"],"author":"Felix","message":"no comment","id":"4A5124C4-C14D-4A79-9786-F70C07FBBEA1","date":"new Date(\"Fri Jan 02 2015 16:42:05 GMT-0800 (PST)\")"},"43":{"morph":{"__isSmartRef__":true,"id":0},"__LivelyClassName__":"lively.morphic.EventHandler","__SourceModuleName__":"Global.lively.morphic.Events"},"44":{"__serializedLivelyClosures__":{"__isSmartRef__":true,"id":45}},"45":{"test01IsMorph":{"__isSmartRef__":true,"id":46}},"46":{"varMapping":{"__isSmartRef__":true,"id":47},"source":"function test01IsMorph(aPart) {\n    this.assert(aPart.isMorph, 'rectangle should be a morph');\n}","funcProperties":{"__isSmartRef__":true,"id":48},"__LivelyClassName__":"lively.Closure"},"47":{"this":{"__isSmartRef__":true,"id":44}},"48":{},"49":{"sourceObj":{"__isSmartRef__":true,"id":0},"sourceAttrName":"submorphs","targetObj":{"__isSmartRef__":true,"id":0},"targetMethodName":"onMorphRemoved","varMapping":{"__isSmartRef__":true,"id":50},"__LivelyClassName__":"AttributeConnection","__SourceModuleName__":"Global.lively.bindings.Core"},"50":{"source":{"__isSmartRef__":true,"id":0},"target":{"__isSmartRef__":true,"id":0}},"51":{"sessionId":"client-session:EF393533-7CC2-4AD8-87E2-82435A929C48","trackerId":"tracker-3502087E-C28C-4EAF-8266-4B5AB51E4C47","__serializedExpressions__":["sessionTrackerURL"],"username":"Felix","_status":"connected","_heartbeatProcess":898117,"registerTimeout":60000,"activityTimeReportDelay":20000,"getSessionsCacheInvalidationTimeout":10000,"timeOfCreation":1420054602549,"webSocket":{"__isSmartRef__":true,"id":52},"actions":{"__isSmartRef__":true,"id":59},"_lastReportedActivity":1420245725036,"_reportActivitiesTimer":972801,"attributeConnections":[{"__isSmartRef__":true,"id":60},{"__isSmartRef__":true,"id":62},{"__isSmartRef__":true,"id":64},{"__isSmartRef__":true,"id":66},{"__isSmartRef__":true,"id":68},{"__isSmartRef__":true,"id":70}],"doNotSerialize":["$$message","$$sessionClosed"],"doNotCopyProperties":["$$message","$$sessionClosed"],"_getSessionsCachedResult":null,"__LivelyClassName__":"lively.net.SessionTrackerConnection","__SourceModuleName__":"Global.lively.net.SessionTracker","sessionTrackerURL":"URL.create(\"http://lively-web.org:8080/nodejs/SessionTracker/\")"},"52":{"uri":"ws://lively-web.org:8080/nodejs/SessionTracker/connect","_erroredCount":0,"_openedCount":1,"_closedCount":0,"_messageOutCounter":330,"_messageInCounter":165,"_lastMessagesOut":["{\"sender\":\"client-session:EF393533-7CC2-4AD8-87E2-82435A929C48\",\"action\":\"reportActivity\",\"data\":{\"lastActivity\":1420245704877},\"target\":\"tracker-3502087E-C28C-4EAF-8266-4B5AB51E4C47\",\"messageIndex\":325,\"messageId\":\"client-msg:6A6CC815-6E92-4B16-95D9-C62899419C9B\"}","{\"sender\":\"client-session:EF393533-7CC2-4AD8-87E2-82435A929C48\",\"action\":\"reportActivity\",\"data\":{\"lastActivity\":1420245725036},\"target\":\"tracker-3502087E-C28C-4EAF-8266-4B5AB51E4C47\",\"messageIndex\":327,\"messageId\":\"client-msg:E4443D4C-676C-4D09-8ABD-BB0C4BCC4C14\"}","{\"sender\":\"client-session:EF393533-7CC2-4AD8-87E2-82435A929C48\",\"action\":\"heartbeat\",\"data\":{},\"target\":\"tracker-3502087E-C28C-4EAF-8266-4B5AB51E4C47\",\"messageIndex\":329,\"messageId\":\"client-msg:CCE0A449-0BE9-45D5-9FB6-40DE0C1E5CEB\"}"],"_lastMessagesIn":["{\"action\":\"reportActivityResult\",\"inResponseTo\":\"client-msg:6A6CC815-6E92-4B16-95D9-C62899419C9B\",\"data\":{\"success\":true},\"sender\":\"tracker-3502087E-C28C-4EAF-8266-4B5AB51E4C47\",\"messageId\":\"lively-msg:9C2664E6-A64B-4337-A272-D5AE085F540C\"}","{\"action\":\"reportActivityResult\",\"inResponseTo\":\"client-msg:E4443D4C-676C-4D09-8ABD-BB0C4BCC4C14\",\"data\":{\"success\":true},\"sender\":\"tracker-3502087E-C28C-4EAF-8266-4B5AB51E4C47\",\"messageId\":\"lively-msg:F8567011-92B8-4FCB-AA4C-010CDFCBAD4B\"}","{\"action\":\"heartbeatResult\",\"inResponseTo\":\"client-msg:CCE0A449-0BE9-45D5-9FB6-40DE0C1E5CEB\",\"data\":{\"time\":1420245725401},\"sender\":\"tracker-3502087E-C28C-4EAF-8266-4B5AB51E4C47\",\"messageId\":\"lively-msg:1B1414F7-8671-480B-BB63-79FB580919BF\"}"],"_lastErrors":[],"reopenClosedConnection":true,"_open":true,"sendTimeout":3000,"messageQueue":[],"protocol":"lively-json","attributeConnections":[{"__isSmartRef__":true,"id":53},{"__isSmartRef__":true,"id":55},{"__isSmartRef__":true,"id":57}],"doNotSerialize":["$$error","$$lively-message","$$closed"],"doNotCopyProperties":["$$error","$$lively-message","$$closed"],"__LivelyClassName__":"lively.net.WebSocket","__SourceModuleName__":"Global.lively.net.WebSockets"},"53":{"sourceObj":{"__isSmartRef__":true,"id":52},"sourceAttrName":"error","targetObj":{"__isSmartRef__":true,"id":51},"targetMethodName":"connectionError","varMapping":{"__isSmartRef__":true,"id":54},"__LivelyClassName__":"AttributeConnection","__SourceModuleName__":"Global.lively.bindings.Core"},"54":{"source":{"__isSmartRef__":true,"id":52},"target":{"__isSmartRef__":true,"id":51}},"55":{"sourceObj":{"__isSmartRef__":true,"id":52},"sourceAttrName":"lively-message","targetObj":{"__isSmartRef__":true,"id":51},"targetMethodName":"dispatchLivelyMessage","varMapping":{"__isSmartRef__":true,"id":56},"updaterString":"function ($upd, msg) { $upd(msg, session); }","__LivelyClassName__":"AttributeConnection","__SourceModuleName__":"Global.lively.bindings.Core"},"56":{"session":{"__isSmartRef__":true,"id":51},"source":{"__isSmartRef__":true,"id":52},"target":{"__isSmartRef__":true,"id":51}},"57":{"sourceObj":{"__isSmartRef__":true,"id":52},"sourceAttrName":"closed","targetObj":{"__isSmartRef__":true,"id":51},"targetMethodName":"connectionClosed","varMapping":{"__isSmartRef__":true,"id":58},"removeAfterUpdate":true,"__LivelyClassName__":"AttributeConnection","__SourceModuleName__":"Global.lively.bindings.Core"},"58":{"source":{"__isSmartRef__":true,"id":52},"target":{"__isSmartRef__":true,"id":51}},"59":{},"60":{"sourceObj":{"__isSmartRef__":true,"id":51},"sourceAttrName":"message","targetMethodName":"messageReceived","varMapping":{"__isSmartRef__":true,"id":61},"__LivelyClassName__":"AttributeConnection","__SourceModuleName__":"Global.lively.bindings.Core"},"61":{"source":{"__isSmartRef__":true,"id":51}},"62":{"sourceObj":{"__isSmartRef__":true,"id":51},"sourceAttrName":"sessionClosed","targetMethodName":"messageReceived","varMapping":{"__isSmartRef__":true,"id":63},"__LivelyClassName__":"AttributeConnection","__SourceModuleName__":"Global.lively.bindings.Core"},"63":{"source":{"__isSmartRef__":true,"id":51}},"64":{"sourceObj":{"__isSmartRef__":true,"id":51},"sourceAttrName":"sessionClosed","targetMethodName":"messageReceived","varMapping":{"__isSmartRef__":true,"id":65},"__LivelyClassName__":"AttributeConnection","__SourceModuleName__":"Global.lively.bindings.Core"},"65":{"source":{"__isSmartRef__":true,"id":51}},"66":{"sourceObj":{"__isSmartRef__":true,"id":51},"sourceAttrName":"sessionClosed","targetMethodName":"messageReceived","varMapping":{"__isSmartRef__":true,"id":67},"__LivelyClassName__":"AttributeConnection","__SourceModuleName__":"Global.lively.bindings.Core"},"67":{"source":{"__isSmartRef__":true,"id":51}},"68":{"sourceObj":{"__isSmartRef__":true,"id":51},"sourceAttrName":"sessionClosed","targetMethodName":"messageReceived","varMapping":{"__isSmartRef__":true,"id":69},"__LivelyClassName__":"AttributeConnection","__SourceModuleName__":"Global.lively.bindings.Core"},"69":{"source":{"__isSmartRef__":true,"id":51}},"70":{"sourceObj":{"__isSmartRef__":true,"id":51},"sourceAttrName":"sessionClosed","targetMethodName":"messageReceived","varMapping":{"__isSmartRef__":true,"id":71},"__LivelyClassName__":"AttributeConnection","__SourceModuleName__":"Global.lively.bindings.Core"},"71":{"source":{"__isSmartRef__":true,"id":51}},"72":{"onMorphAdded":{"__isSmartRef__":true,"id":73},"onMorphRemoved":{"__isSmartRef__":true,"id":76},"initVars":{"__isSmartRef__":true,"id":79},"onLoad":{"__isSmartRef__":true,"id":82},"send":{"__isSmartRef__":true,"id":85},"openWebSocket":{"__isSmartRef__":true,"id":88},"sendBuffer":{"__isSmartRef__":true,"id":91},"lzwDecode":{"__isSmartRef__":true,"id":94},"lzwEncode":{"__isSmartRef__":true,"id":97},"onPenEvent":{"__isSmartRef__":true,"id":100},"showKbps":{"__isSmartRef__":true,"id":103},"playAudioBuffer":{"__isSmartRef__":true,"id":106},"arraybufferToString":{"__isSmartRef__":true,"id":109},"stringToArraybuffer":{"__isSmartRef__":true,"id":112},"withLively2LivelySessionDo":{"__isSmartRef__":true,"id":115},"subscribe":{"__isSmartRef__":true,"id":118},"updatePublisherList":{"__isSmartRef__":true,"id":121},"toggleSubscription":{"__isSmartRef__":true,"id":124},"to32BitBuffer":{"__isSmartRef__":true,"id":127},"checkAudioBufferStatus":{"__isSmartRef__":true,"id":130},"fillupConfig":{"__isSmartRef__":true,"id":133},"attachStreamingUtils":{"__isSmartRef__":true,"id":136},"assignSteppingFunction":{"__isSmartRef__":true,"id":139},"lookupSteppingFunction":{"__isSmartRef__":true,"id":142},"startStreaming":{"__isSmartRef__":true,"id":145},"getScreen":{"__isSmartRef__":true,"id":148},"checkSocketState":{"__isSmartRef__":true,"id":151},"deactivateStreamScreens":{"__isSmartRef__":true,"id":154},"requestNewStreamId":{"__isSmartRef__":true,"id":157},"sendProgress":{"__isSmartRef__":true,"id":160},"unsubscribe":{"__isSmartRef__":true,"id":163},"visualizeProgress":{"__isSmartRef__":true,"id":166},"requestTakeover":{"__isSmartRef__":true,"id":169},"decideTakeover":{"__isSmartRef__":true,"id":172},"releaseStream":{"__isSmartRef__":true,"id":175},"handleTakeover":{"__isSmartRef__":true,"id":178},"stopStreaming":{"__isSmartRef__":true,"id":181},"continueStreaming":{"__isSmartRef__":true,"id":184},"loadAmountOfFrames":{"__isSmartRef__":true,"id":187},"loadFramesByDuration":{"__isSmartRef__":true,"id":190},"decodeAll":{"__isSmartRef__":true,"id":193}},"73":{"varMapping":{"__isSmartRef__":true,"id":74},"source":"function onMorphAdded(aMorph) {\n    if (!this.socket) {\n        show('no websocket open');\n        return;\n    }\n    \n    // use the morphs config, fill up all required fields\n    this.fillupConfig(aMorph);\n    var config = aMorph.streamingConfig;\n    \n    if (config.mediatype === 'unknown') {\n        show('Unknown media type. Set the mediatype field in the morphs streamingConfig');\n        return;\n    }\n    \n    if (this.sources.indexOf(aMorph) === -1) {\n        this.sources.push(aMorph);\n    }\n    \n    var _this = this;\n    this.requestNewStreamId(function(id) {\n        _this.attachStreamingUtils(aMorph, id);\n        _this.startStreaming(aMorph);\n    });\n}","funcProperties":{"__isSmartRef__":true,"id":75},"__LivelyClassName__":"lively.Closure"},"74":{"this":{"__isSmartRef__":true,"id":0}},"75":{"user":"felix","__serializedExpressions__":["timestamp"],"timestamp":"new Date(\"Mon Dec 15 2014 11:04:46 GMT-0800 (PST)\")"},"76":{"varMapping":{"__isSmartRef__":true,"id":77},"source":"function onMorphRemoved() {\n    var before = this.sources.slice();\n    \n    // substract the current submorphs from the registered sources,\n    // then there should be one morph left in the sources, which is\n    // the one that has been removed\n    this.submorphs.forEach(function(ea) {\n        var idx = before.indexOf(ea);\n        if (idx == -1) {\n            show('Invalid state in sources array');\n            return;\n        }\n        before.splice(idx, 1);\n    });\n    \n    if (before.length != 1) {\n        // there is not exactly one morph left\n        show('Invalid state after cleaning sources array');\n        return;\n    }\n    \n    var removedMorph = before.first();\n    var idx = this.sources.indexOf(removedMorph);\n    \n    this.sources.splice(idx, 1);\n    \n    this.stopStreaming(removedMorph);\n}","funcProperties":{"__isSmartRef__":true,"id":78},"__LivelyClassName__":"lively.Closure"},"77":{"this":{"__isSmartRef__":true,"id":0}},"78":{"user":"felix","__serializedExpressions__":["timestamp"],"timestamp":"new Date(\"Thu Dec 18 2014 15:38:52 GMT-0800 (PST)\")"},"79":{"varMapping":{"__isSmartRef__":true,"id":80},"source":"function initVars() {\n    // this.wsUrl = 'ws://104.131.62.171:1234';\n    this.wsUrl = 'ws://localhost:1234';\n    this.openWebSocket();\n    this.buffer = [];\n    this.sources = [];\n    this.idRequestCallbacks = [];\n    this.takeoverCallbacks = [];\n    this.frameLoadingCallbacks = [];\n    this.knownStreams = [];\n    this.upstreamData = 0;\n    this.downstreamData = 0;\n    this.sendCount = 0;\n    this.audioBuffer = [];\n    this.playingAudioBuffer = false;\n    this.audioContext = this.audioContext ? this.audioContext : new Global.AudioContext();\n}","funcProperties":{"__isSmartRef__":true,"id":81},"__LivelyClassName__":"lively.Closure"},"80":{"this":{"__isSmartRef__":true,"id":0}},"81":{"__serializedExpressions__":["timestamp"],"user":"Felix","timestamp":"new Date(\"Wed Dec 31 2014 11:06:32 GMT-0800 (PST)\")"},"82":{"varMapping":{"__isSmartRef__":true,"id":83},"source":"function onLoad() {\n    var _this = this;\n    function init(err, session) {\n        if (err) {\n            show('Error while retrieving l2l session. Are you connected?');\n            return;\n        }\n        \n        _this.session = session;\n        _this.doNotSerialize = ['audioContext', 'socket'];\n        _this.initVars();\n        \n        var publisherList = $morph('WormHolePublisherList');\n        if (publisherList) publisherList.onLoad();\n        \n        _this.startStepping(10, 'sendBuffer');\n        _this.startStepping(1000, 'showKbps');\n        _this.startStepping(1000, 'checkAudioBufferStatus');\n        _this.startStepping(1000, 'checkSocketState');\n    }\n    \n    this.withLively2LivelySessionDo(5000, init);\n}","funcProperties":{"__isSmartRef__":true,"id":84},"__LivelyClassName__":"lively.Closure"},"83":{"this":{"__isSmartRef__":true,"id":0}},"84":{"user":"felix","__serializedExpressions__":["timestamp"],"timestamp":"new Date(\"Mon Dec 08 2014 10:40:49 GMT-0800 (PST)\")"},"85":{"varMapping":{"__isSmartRef__":true,"id":86},"source":"function send(message) {\n    // we are always sending strings, so stringify the message\n    message = JSON.stringify(message);\n    \n    this.buffer.push(message);\n    this.sendCount++;\n}","funcProperties":{"__isSmartRef__":true,"id":87},"__LivelyClassName__":"lively.Closure"},"86":{"this":{"__isSmartRef__":true,"id":0}},"87":{"user":"felix","__serializedExpressions__":["timestamp"],"timestamp":"new Date(\"Mon Dec 01 2014 15:44:25 GMT-0800 (PST)\")"},"88":{"varMapping":{"__isSmartRef__":true,"id":89},"source":"function openWebSocket() {\n    if (this.socket && this.socket.readyState < 2) {\n        // socket is open or connecting\n        this.socket.close();\n    }\n    var socket = new Global.WebSocket(this.wsUrl);\n    socket.binaryType = 'arraybuffer';\n    \n    var _this = this;\n    \n    socket.onopen = function() {\n        Global.alertOK('socket open');\n        // greet the server to be registered\n        var message = {\n            type: 'hello-network',\n            senderId: _this.session.sessionId,\n            senderName: _this.session.username\n        }\n        \n        _this.send(message);\n    };\n    \n    socket.onerror = function(err) {\n        show(err);\n    }\n    \n    socket.onclose = function() {\n        Global.alertOK('socket closed');\n    };\n    \n    socket.onmessage = function(evt) {\n        var message = evt.data;\n        _this.downstreamData += message.length;\n        message = JSON.parse(message);\n        if (message.timestamp) message.timestamp = Number.parseInt(message.timestamp);\n        \n        switch (message.type) {\n            case 'image':\n                // got image data\n                var imageURL = message.image;\n                var lzwEncoded = message.lzwEncoded;\n            \n                if (lzwEncoded) {\n                    imageURL = _this.lzwDecode(imageURL);\n                }\n                \n                _this.getScreen(message).newFrame(imageURL, message.timestamp);\n                break;\n            case 'audio':\n                // got audio data\n                var audioString = message.audioBuffer;\n                if (message.lzwEncoded) {\n                    audioString = Global.LZString.decompressFromUTF16(audioString);\n                }\n                \n                // retrieve an ArrayBuffer from the audioString\n                var buffer = _this.stringToArraybuffer(audioString);\n                \n                // if the bit depth was reduced to 16 bit, interpret it as 32 bit\n                if (message.reducedBitDepth) {\n                    buffer = _this.to32BitBuffer(buffer);\n                } else {\n                    buffer = new Global.Float32Array(buffer);\n                }\n                \n                _this.audioBuffer.push(buffer);\n                if (!_this.playingAudioBuffer) {\n                    _this.playAudioBuffer();\n                }\n                break;\n            case 'data':\n                // got raw data packet\n                var dataString = message.data;\n                if (message.lzwEncoded) {\n                    dataString = _this.lzwDecode(dataString);\n                }\n                \n                console.log(dataString);\n                break;\n            case 'initial-information':\n                _this.knownStreams = message.streams;\n                _this.updatePublisherList();\n                break;\n            case 'new-clients':\n                var clients = message.clients;\n                break;\n            case 'stream-id':\n                var callback = _this.idRequestCallbacks.shift();\n                if (callback) callback(message.id);\n                break;\n            case 'stream-changes':\n                _this.knownStreams = message.streams;\n                var removedStreams = _this.updatePublisherList();\n                _this.deactivateStreamScreens(removedStreams);\n                break;\n            case 'recorded-data':\n                var requestId = message.requestId;\n                var data = _this.decodeAll(message.data);\n                \n                var record = _this.frameLoadingCallbacks.find(function(record) {\n                    return record.requestId === requestId;\n                });\n                \n                var idx = _this.frameLoadingCallbacks.indexOf(record);\n                _this.frameLoadingCallbacks.splice(idx, 1);\n                \n                record.callback(data);\n                break;\n            case 'request-takeover':\n                var streamId = message.streamId;\n                var requesterId = message.requesterId;\n                var requesterName = message.requesterName;\n                _this.decideTakeover(streamId, requesterId, requesterName);\n                break;\n            case 'response-takeover':\n                var streamId = message.streamId;\n                var response = message.response;\n                \n                var callbackRecord = _this.takeoverCallbacks.find(function(record) {\n                    return record.streamId === streamId;\n                });\n                \n                var idx = _this.takeoverCallbacks.indexOf(callbackRecord);\n                if (idx >= 0) _this.takeoverCallbacks.splice(idx, 1);\n                \n                callbackRecord.callback(response);\n                break;\n            case 'continue-streaming':\n                var streamId = message.streamId;\n                Global.alertOK('continue stream ' + streamId);\n                _this.continueStreaming(streamId);\n                break;\n            case 'progress-update':\n                _this.visualizeProgress(message.streamId, message.timestamps);\n                break;\n            case 'error':\n                show('Received error message: ' + message.message);\n                break;\n            default:\n                show('Received unknown message type: ' + message.type);\n                return;\n        }\n    }\n    \n    this.socket = socket;\n}","funcProperties":{"__isSmartRef__":true,"id":90},"__LivelyClassName__":"lively.Closure"},"89":{"this":{"__isSmartRef__":true,"id":0}},"90":{"__serializedExpressions__":["timestamp"],"user":"Felix","timestamp":"new Date(\"Wed Dec 31 2014 10:56:53 GMT-0800 (PST)\")"},"91":{"varMapping":{"__isSmartRef__":true,"id":92},"source":"function sendBuffer() {\n    var OPEN = 1;\n    // write all messages in the buffer into the socket\n    while (true) {\n        // pop the first message\n        var message = this.buffer.shift();\n        // no more messages left\n        if (message === undefined) break;\n        // send the message\n        if (this.socket.readyState === OPEN) {\n            this.socket.send(message);\n            this.upstreamData += message.length;\n        }\n    }\n}","funcProperties":{"__isSmartRef__":true,"id":93},"__LivelyClassName__":"lively.Closure"},"92":{"this":{"__isSmartRef__":true,"id":0}},"93":{"user":"felix","__serializedExpressions__":["timestamp"],"timestamp":"new Date(\"Thu Nov 20 2014 13:12:39 GMT-0800 (PST)\")"},"94":{"varMapping":{"__isSmartRef__":true,"id":95},"source":"function lzwDecode(string) {\n    var dict = {};\n    var data = (string + \"\").split(\"\");\n    var currChar = data[0];\n    var oldPhrase = currChar;\n    var out = [currChar];\n    var code = 256;\n    var phrase;\n    for (var i=1; i<data.length; i++) {\n        var currCode = data[i].charCodeAt(0);\n        if (currCode < 256) {\n            phrase = data[i];\n        }\n        else {\n           phrase = dict[currCode] ? dict[currCode] : (oldPhrase + currChar);\n        }\n        out.push(phrase);\n        currChar = phrase.charAt(0);\n        dict[code] = oldPhrase + currChar;\n        code++;\n        oldPhrase = phrase;\n    }\n    return out.join(\"\");\n}","funcProperties":{"__isSmartRef__":true,"id":96},"__LivelyClassName__":"lively.Closure"},"95":{"this":{"__isSmartRef__":true,"id":0}},"96":{"user":"felix","__serializedExpressions__":["timestamp"],"timestamp":"new Date(\"Mon Nov 10 2014 12:19:05 GMT-0800 (PST)\")"},"97":{"varMapping":{"__isSmartRef__":true,"id":98},"source":"function lzwEncode(string) {\n    var dict = {};\n    var data = (string + \"\").split(\"\");\n    var out = [];\n    var currChar;\n    var phrase = data[0];\n    var code = 256;\n    for (var i=1; i<data.length; i++) {\n        currChar=data[i];\n        if (dict[phrase + currChar] != null) {\n            phrase += currChar;\n        }\n        else {\n            out.push(phrase.length > 1 ? dict[phrase] : phrase.charCodeAt(0));\n            dict[phrase + currChar] = code;\n            code++;\n            phrase=currChar;\n        }\n    }\n    out.push(phrase.length > 1 ? dict[phrase] : phrase.charCodeAt(0));\n    for (var i=0; i<out.length; i++) {\n        out[i] = String.fromCharCode(out[i]);\n    }\n    return out.join(\"\");\n}","funcProperties":{"__isSmartRef__":true,"id":99},"__LivelyClassName__":"lively.Closure"},"98":{"this":{"__isSmartRef__":true,"id":0}},"99":{"user":"felix","__serializedExpressions__":["timestamp"],"timestamp":"new Date(\"Mon Nov 10 2014 12:18:26 GMT-0800 (PST)\")"},"100":{"varMapping":{"__isSmartRef__":true,"id":101},"source":"function onPenEvent() {\n    \n}","funcProperties":{"__isSmartRef__":true,"id":102},"__LivelyClassName__":"lively.Closure"},"101":{"this":{"__isSmartRef__":true,"id":0}},"102":{"user":"felix","__serializedExpressions__":["timestamp"],"timestamp":"new Date(\"Thu Dec 04 2014 17:12:29 GMT-0800 (PST)\")"},"103":{"varMapping":{"__isSmartRef__":true,"id":104},"source":"function showKbps() {\n    // upstream\n    var text = $morph('WormHoleControlPanel').get('upstream');\n    var size = Global.Numbers.humanReadableByteSize(this.upstreamData);\n    text.setTextString('Upstream: ' + size + '/s');\n    this.upstreamData = 0;\n    \n    // downstream\n    text = $morph('WormHoleControlPanel').get('downstream');\n    size = Global.Numbers.humanReadableByteSize(this.downstreamData);\n    text.setTextString('Downstream: ' + size + '/s');\n    this.downstreamData = 0;\n    \n    // number of data frames per second\n    text = $morph('WormHoleControlPanel').get('sendCount');\n    text.setTextString(this.sendCount + ' DataFrames/s');\n    this.sendCount = 0;\n    \n    // number of buffered audio frames\n    text = $morph('WormHoleControlPanel').get('audioBufferLength');\n    text.setTextString('Buffered audio frames: ' + this.audioBuffer.length);\n}","funcProperties":{"__isSmartRef__":true,"id":105},"__LivelyClassName__":"lively.Closure"},"104":{"this":{"__isSmartRef__":true,"id":0}},"105":{"user":"felix","__serializedExpressions__":["timestamp"],"timestamp":"new Date(\"Thu Dec 04 2014 15:36:55 GMT-0800 (PST)\")"},"106":{"varMapping":{"__isSmartRef__":true,"id":107},"source":"function playAudioBuffer() {\n    var minBufferedFrames = 3;\n    var maxBufferedFrames = 10;\n    \n    if (this.audioBuffer.length < minBufferedFrames) {\n        // still buffering\n        this.playingAudioBuffer = false;\n        return;\n    }\n    \n    if (this.audioBuffer.length > maxBufferedFrames) {\n        // Buffer is packed, so shrink it to the min size\n        this.audioBuffer = this.audioBuffer.slice(-minBufferedFrames);\n    }\n    \n    this.playingAudioBuffer = true;\n    \n    // retrieve a sample from the buffer\n    var buffer = this.audioBuffer.shift();\n    \n    // create buffer with 1 channel, #buffer.length samples, 11025Hz sampling rate\n    var audioBuffer = this.audioContext.createBuffer(1, buffer.length, 11025);\n    var channel = audioBuffer.getChannelData(0);\n    \n    // fill the buffer\n    for (var i = 0; i < buffer.length; i++) {\n        channel[i] = buffer[i];\n    }\n    \n    var source = this.audioContext.createBufferSource();\n    source.buffer = audioBuffer;\n    var _this = this;\n    source.onended = function() {\n        // play the next audio frame\n        _this.playAudioBuffer();\n    };\n    source.connect(this.audioContext.destination);\n    source.start();\n}","funcProperties":{"__isSmartRef__":true,"id":108},"__LivelyClassName__":"lively.Closure"},"107":{"this":{"__isSmartRef__":true,"id":0}},"108":{"user":"felix","__serializedExpressions__":["timestamp"],"timestamp":"new Date(\"Mon Dec 01 2014 15:29:31 GMT-0800 (PST)\")"},"109":{"varMapping":{"__isSmartRef__":true,"id":110},"source":"function arraybufferToString(buffer) {\n    // create a Uint16Array data view from the buffer\n    var array = new Global.Uint16Array(buffer);\n    // create a string from the values in the array\n    var str = String.fromCharCode.apply(null, array);\n    \n    return str;\n}","funcProperties":{"__isSmartRef__":true,"id":111},"__LivelyClassName__":"lively.Closure"},"110":{"this":{"__isSmartRef__":true,"id":0}},"111":{"user":"felix","__serializedExpressions__":["timestamp"],"timestamp":"new Date(\"Thu Nov 20 2014 11:42:23 GMT-0800 (PST)\")"},"112":{"varMapping":{"__isSmartRef__":true,"id":113},"source":"function stringToArraybuffer(string) {\n    // create buffer with 2 bytes for each char\n    var buffer = new Global.ArrayBuffer(string.length * 2);\n    // create a 16 bit view on that buffer\n    var bufferView = new Global.Uint16Array(buffer);\n    // fill the buffer with the char codes of the string\n    for (var i = 0; i < string.length; i++) {\n        bufferView[i] = string.charCodeAt(i);\n    }\n    \n    return buffer;\n}","funcProperties":{"__isSmartRef__":true,"id":114},"__LivelyClassName__":"lively.Closure"},"113":{"this":{"__isSmartRef__":true,"id":0}},"114":{"user":"felix","__serializedExpressions__":["timestamp"],"timestamp":"new Date(\"Thu Nov 20 2014 11:47:42 GMT-0800 (PST)\")"},"115":{"varMapping":{"__isSmartRef__":true,"id":116},"source":"function withLively2LivelySessionDo(timeoutMs, thenDo) {\n    // wait for a lively2lively connection\n    if (!thenDo) { thenDo = timeoutMs; timeoutMs = 5000; }\n    Functions.composeAsync(\n        function(next) { Global.require('lively.net.SessionTracker').toRun(function() { next() }); },\n        function(next) { lively.whenLoaded(function() { next(); }); },\n        function(next) {\n            Functions.waitFor(timeoutMs,\n                function() { return !!lively.net.SessionTracker.getSession(); },\n                function(err) { next(err, lively.net.SessionTracker.getSession()) })\n        },\n        function(sess, next) {\n            var online = false;\n            sess.whenOnline(function() { online = true; })\n            Functions.waitFor(timeoutMs,\n                function() { return !!online; },\n                function(err) { next(err, sess); });\n        })(thenDo);\n}","funcProperties":{"__isSmartRef__":true,"id":117},"__LivelyClassName__":"lively.Closure"},"116":{"this":{"__isSmartRef__":true,"id":0}},"117":{"user":"felix","__serializedExpressions__":["timestamp"],"timestamp":"new Date(\"Thu Nov 20 2014 16:18:31 GMT-0800 (PST)\")"},"118":{"varMapping":{"__isSmartRef__":true,"id":119},"source":"function subscribe(streamIds) {\n    var _this = this;\n    streamIds.forEach(function(streamId) {\n        var message = {\n            type: 'subscribe',\n            senderId: _this.session.sessionId,\n            streamId: streamId\n        }\n        \n        _this.send(message);\n        \n        var screen = $morph('CanvasScreen-' + streamId);\n        if (screen) {\n            screen.startReportingProgress();\n        }\n    });\n}","funcProperties":{"__isSmartRef__":true,"id":120},"__LivelyClassName__":"lively.Closure"},"119":{"this":{"__isSmartRef__":true,"id":0}},"120":{"user":"felix","__serializedExpressions__":["timestamp"],"timestamp":"new Date(\"Mon Dec 15 2014 12:39:05 GMT-0800 (PST)\")"},"121":{"varMapping":{"__isSmartRef__":true,"id":122},"source":"function updatePublisherList() {\n    var list = $morph('WormHolePublisherList');\n    if (!list) return;\n    \n    return list.updateList(this.knownStreams);\n}","funcProperties":{"__isSmartRef__":true,"id":123},"__LivelyClassName__":"lively.Closure"},"122":{"this":{"__isSmartRef__":true,"id":0}},"123":{"user":"felix","__serializedExpressions__":["timestamp"],"timestamp":"new Date(\"Mon Dec 22 2014 14:05:32 GMT-0800 (PST)\")"},"124":{"varMapping":{"__isSmartRef__":true,"id":125},"source":"function toggleSubscription(streamId, newVal) {\n    if (newVal == false) {\n        this.unsubscribe(streamId);  \n    } else {\n        this.subscribe([streamId]);\n    }\n}","funcProperties":{"__isSmartRef__":true,"id":126},"__LivelyClassName__":"lively.Closure"},"125":{"this":{"__isSmartRef__":true,"id":0}},"126":{"user":"felix","__serializedExpressions__":["timestamp"],"timestamp":"new Date(\"Mon Dec 15 2014 12:26:48 GMT-0800 (PST)\")"},"127":{"varMapping":{"__isSmartRef__":true,"id":128},"source":"function to32BitBuffer(buffer) {\n    // this function expects an ArrayBuffer as buffer\n    \n    // create int-buffers for bit-shifting\n    var buffer16Bit = new Global.Uint32Array(buffer);\n    var tmpBuffer = new Global.Uint32Array(buffer16Bit.length * 2);\n    \n    for (var i = 0; i < buffer16Bit.length; i++) {\n        tmpBuffer[2*i] = buffer16Bit[i] & 0xffff0000;\n        tmpBuffer[2*i + 1] = (buffer16Bit[i] & 0x0000ffff) << 16;\n    }\n    \n    // interpret the resulting buffer as float32\n    return new Global.Float32Array(tmpBuffer.buffer);\n}","funcProperties":{"__isSmartRef__":true,"id":129},"__LivelyClassName__":"lively.Closure"},"128":{"this":{"__isSmartRef__":true,"id":0}},"129":{"user":"felix","__serializedExpressions__":["timestamp"],"timestamp":"new Date(\"Mon Dec 01 2014 14:04:29 GMT-0800 (PST)\")"},"130":{"varMapping":{"__isSmartRef__":true,"id":131},"source":"function checkAudioBufferStatus() {\n    // This function is called periodically every second.\n    // Sometimes the callback onended in playAudioBuffer is not called, \n    // which leads to an overflow of the audioBuffer. So we check here,\n    // if such a situation is present and if so, call playAudioBuffer\n    // explicitly.\n    if (this.playingAudioBuffer && this.audioBuffer.length > 20) {\n        this.playAudioBuffer();\n    }\n}","funcProperties":{"__isSmartRef__":true,"id":132},"__LivelyClassName__":"lively.Closure"},"131":{"this":{"__isSmartRef__":true,"id":0}},"132":{"user":"felix","__serializedExpressions__":["timestamp"],"timestamp":"new Date(\"Mon Dec 01 2014 15:45:47 GMT-0800 (PST)\")"},"133":{"varMapping":{"__isSmartRef__":true,"id":134},"source":"function fillupConfig(morph) {\n    if (!morph.streamingConfig) morph.streamingConfig = {};\n    \n    var config = morph.streamingConfig;\n    \n    // set defaults for required fields\n    config.mediatype = config.mediatype || 'unknown';\n    config.steptime = config.steptime || 100;\n    config.streaming = config.streaming || function() { return true; };\n    config.compressionParameters = config.compressionParameters || {};\n    \n    // set defaults for media-specific properties\n    switch (config.mediatype) {\n        case 'image':\n            var params = config.compressionParameters;\n            params.imgCompression = params.imgCompression || 'image/webp';\n            params.imgQuality = params.imgQuality || 1;\n            if (params.lzwCompression === undefined) {\n                params.lzwCompression = true\n            }\n            break;\n        case 'audio':\n            config.steptime = -1;\n            if (config.compressionParameters.reducedBitDepth === undefined) {\n                config.compressionParameters.reducedBitDepth = true;\n            }\n            break;\n    }\n}","funcProperties":{"__isSmartRef__":true,"id":135},"__LivelyClassName__":"lively.Closure"},"134":{"this":{"__isSmartRef__":true,"id":0}},"135":{"user":"felix","__serializedExpressions__":["timestamp"],"timestamp":"new Date(\"Tue Dec 23 2014 11:13:00 GMT-0800 (PST)\")"},"136":{"varMapping":{"__isSmartRef__":true,"id":137},"source":"function attachStreamingUtils(morph, streamId) {\n    var config = morph.streamingConfig;\n    config.streamId = streamId;\n    \n    this.assignSteppingFunction(morph);\n}","funcProperties":{"__isSmartRef__":true,"id":138},"__LivelyClassName__":"lively.Closure"},"137":{"this":{"__isSmartRef__":true,"id":0}},"138":{"user":"felix","__serializedExpressions__":["timestamp"],"timestamp":"new Date(\"Fri Dec 12 2014 18:25:09 GMT-0800 (PST)\")"},"139":{"varMapping":{"__isSmartRef__":true,"id":140},"source":"function assignSteppingFunction(morph) {\n    if (typeof morph.streamingConfig.steppingFunction === 'function') {\n        // this morph already has his own stepping function\n        morph.streamingFunction = morph.streamingConfig.steppingFunction;\n        return;\n    }\n    \n    var steppingFunction = this.lookupSteppingFunction(morph);\n    morph.streamingConfig.steppingFunction = steppingFunction;\n    \n    // morph.streamingFunction will be called with every tick\n    morph.streamingFunction = steppingFunction;\n}","funcProperties":{"__isSmartRef__":true,"id":141},"__LivelyClassName__":"lively.Closure"},"140":{"this":{"__isSmartRef__":true,"id":0}},"141":{"user":"felix","__serializedExpressions__":["timestamp"],"timestamp":"new Date(\"Tue Dec 16 2014 11:37:03 GMT-0800 (PST)\")"},"142":{"varMapping":{"__isSmartRef__":true,"id":143},"source":"function lookupSteppingFunction(morph) {\n    var _this = this;\n    var session = this.session;\n    \n    function lookupStreamingConfig() {\n        return morph.streamingConfig;\n    }\n    \n    switch (morph.streamingConfig.mediatype) {\n        case 'image':\n            return function(force) {\n                // lookup the config at time of execution, so that it can be\n                // changed dynamically during steaming\n                var config = lookupStreamingConfig();\n                \n                // Check if the morph wants to be streamed at the moment.\n                // Although, it can be forced when the flag is set.\n                if (!force && !config.streaming()) return;\n                \n                var compressionParams = config.compressionParameters;\n                \n                // aquire compression parameters for each frame from the params\n                var encoding = compressionParams.imgCompression;\n                var quality = compressionParams.imgQuality;\n                var compression = compressionParams.lzwCompression;\n                \n                // capture a video frame\n                var imageURL = morph.captureFrame(encoding, quality);\n                \n                // apply lzw compression, if desired\n                if (compression) {\n                    imageURL = _this.lzwEncode(imageURL);\n                }\n                \n                // create a data packet\n                var obj = {\n                    type: config.mediatype,\n                    senderId: session.sessionId,\n                    streamId: config.streamId,\n                    senderName: session.username,\n                    image: imageURL,\n                    record: !!config.record,\n                    size: {\n                        width: morph.getExtent().x,\n                        height: morph.getExtent().y\n                    },\n                    lzwEncoded: compression\n                }\n                \n                // send it out into the universe\n                _this.send(obj);\n            }\n        case 'audio':\n            return function(typedArray, force) {\n                // lookup the config at time of execution, so that it can be\n                // changed dynamically during steaming\n                var config = lookupStreamingConfig();\n                \n                // Check if the morph wants to be streamed at the moment.\n                // Although, it can be forced when the flag is set.\n                if (!force && !config.streaming()) return;\n                \n                var audioString = _this.arraybufferToString(typedArray.buffer);\n                \n                var obj = {\n                    type: config.mediatype,\n                    senderId: session.sessionId,\n                    senderName: session.username,\n                    audioBuffer: audioString,\n                    lzwEncoded: false,\n                    reducedBitDepth: config.compressionParameters.reducedBitDepth\n                }\n                \n                _this.send(obj);\n            }\n        case 'data':\n            return function(dataString, force) {\n                // lookup the config at time of execution, so that it can be\n                // changed dynamically during steaming\n                var config = lookupStreamingConfig();\n                \n                // Check if the morph wants to be streamed at the moment.\n                // Although, it can be forced when the flag is set.\n                if (!force && !config.streaming()) return;\n                \n                dataString = dataString || morph.captureFrame();\n                \n                if (config.compressionParameters.lzwCompression) {\n                    dataString = _this.lzwEncode(dataString);\n                }\n                \n                var obj = {\n                    type: config.mediatype,\n                    senderId: session.sessionId,\n                    senderName: session.username,\n                    data: dataString,\n                    lzwEncoded: !!config.compressionParameters.lzwCompression\n                }\n                \n                _this.send(obj);\n            }\n        default:\n            show('No steppingFunction registered for this mediatype. Either put one into the streamingConfig or write one into lookupSteppingFunction of the WormHole-object');\n            return;\n    }\n}","funcProperties":{"__isSmartRef__":true,"id":144},"__LivelyClassName__":"lively.Closure"},"143":{"this":{"__isSmartRef__":true,"id":0}},"144":{"user":"felix","__serializedExpressions__":["timestamp"],"timestamp":"new Date(\"Mon Dec 15 2014 10:30:53 GMT-0800 (PST)\")"},"145":{"varMapping":{"__isSmartRef__":true,"id":146},"source":"function startStreaming(morph) {\n    var steptime = morph.streamingConfig.steptime;\n    \n    morph.isBeingStreamed = true;\n    \n    // just start stepping, if the morph wants to be streamed periodically\n    if (steptime >= 0) {\n        if (morph.streamingConfig.onStartStreaming) {\n            morph.streamingConfig.onStartStreaming();\n        }\n        \n        morph.startStepping(steptime, 'streamingFunction');\n    }\n}","funcProperties":{"__isSmartRef__":true,"id":147},"__LivelyClassName__":"lively.Closure"},"146":{"this":{"__isSmartRef__":true,"id":0}},"147":{"user":"felix","__serializedExpressions__":["timestamp"],"timestamp":"new Date(\"Fri Dec 05 2014 11:20:54 GMT-0800 (PST)\")"},"148":{"varMapping":{"__isSmartRef__":true,"id":149},"source":"function getScreen(message) {\n    var id = message.streamId;\n    var screen = $morph('CanvasScreen-' + id);\n    if (!screen) {\n        var size = message.size;\n        var stream = this.knownStreams.find(function(stream) {\n            return stream.id === id;\n        });\n        if (!stream) {\n            show('getScreen failed: stream unknown');\n            return;\n        }\n        \n        screen = $world.loadPartItem('BackInTimeCanvas', 'PartsBin/Felix');\n        screen.setName('CanvasScreen-' + id);\n        screen.setScreenExtent(lively.pt(size.width, size.height));\n        screen.streamId = id;\n        screen.starttime = stream.starttime;\n        screen.openInHand();\n    }\n    \n    return screen;\n}","funcProperties":{"__isSmartRef__":true,"id":150},"__LivelyClassName__":"lively.Closure"},"149":{"this":{"__isSmartRef__":true,"id":0}},"150":{"user":"felix","__serializedExpressions__":["timestamp"],"timestamp":"new Date(\"Mon Dec 22 2014 14:48:55 GMT-0800 (PST)\")"},"151":{"varMapping":{"__isSmartRef__":true,"id":152},"source":"function checkSocketState() {\n    var state = this.socket.readyState;\n    var indicator = $morph('ConnectionIndicator');\n    if (indicator) {\n        indicator.toggle(state === this.socket.OPEN)\n    }\n}","funcProperties":{"__isSmartRef__":true,"id":153},"__LivelyClassName__":"lively.Closure"},"152":{"this":{"__isSmartRef__":true,"id":0}},"153":{"user":"felix","__serializedExpressions__":["timestamp"],"timestamp":"new Date(\"Mon Dec 08 2014 10:41:19 GMT-0800 (PST)\")"},"154":{"varMapping":{"__isSmartRef__":true,"id":155},"source":"function deactivateStreamScreens(streams) {\n    streams.forEach(function(stream) {\n        var screen = $morph('CanvasScreen-' + stream.id);\n        \n        if (screen) {\n            screen.stopReportingProgress();\n        }\n    });\n}","funcProperties":{"__isSmartRef__":true,"id":156},"__LivelyClassName__":"lively.Closure"},"155":{"this":{"__isSmartRef__":true,"id":0}},"156":{"user":"felix","__serializedExpressions__":["timestamp"],"timestamp":"new Date(\"Mon Dec 15 2014 12:51:20 GMT-0800 (PST)\")"},"157":{"varMapping":{"__isSmartRef__":true,"id":158},"source":"function requestNewStreamId(callback) {\n    // callback is needed\n    if (typeof callback !== 'function') return;\n    \n    this.send({\n        type: 'request-stream-id'\n    });\n    \n    this.idRequestCallbacks.push(callback);\n}","funcProperties":{"__isSmartRef__":true,"id":159},"__LivelyClassName__":"lively.Closure"},"158":{"this":{"__isSmartRef__":true,"id":0}},"159":{"user":"felix","__serializedExpressions__":["timestamp"],"timestamp":"new Date(\"Fri Dec 12 2014 18:01:48 GMT-0800 (PST)\")"},"160":{"varMapping":{"__isSmartRef__":true,"id":161},"source":"function sendProgress(streamId, currentTime) {\n    var obj = {\n        type: 'progress-time',\n        senderId: this.session.sessionId,\n        streamId: streamId,\n        progressTime: currentTime\n    }\n    \n    this.send(obj);\n}","funcProperties":{"__isSmartRef__":true,"id":162},"__LivelyClassName__":"lively.Closure"},"161":{"this":{"__isSmartRef__":true,"id":0}},"162":{"user":"felix","__serializedExpressions__":["timestamp"],"timestamp":"new Date(\"Mon Dec 15 2014 11:56:22 GMT-0800 (PST)\")"},"163":{"varMapping":{"__isSmartRef__":true,"id":164},"source":"function unsubscribe(streamId) {\n    this.send({\n        type: 'unsubscribe',\n        senderId: this.session.sessionId,\n        streamId: streamId\n    });\n    \n    // if there is a screen associated to the stream,\n    // stop sending progress updates\n    var screen = $morph('CanvasScreen-' + streamId);\n    if (screen) {\n        screen.stopReportingProgress();\n    }\n}","funcProperties":{"__isSmartRef__":true,"id":165},"__LivelyClassName__":"lively.Closure"},"164":{"this":{"__isSmartRef__":true,"id":0}},"165":{"user":"felix","__serializedExpressions__":["timestamp"],"timestamp":"new Date(\"Mon Dec 15 2014 12:39:45 GMT-0800 (PST)\")"},"166":{"varMapping":{"__isSmartRef__":true,"id":167},"source":"function visualizeProgress(streamId, progressData) {\n    var screen = $morph('CanvasScreen-' + streamId);\n    if (!screen) return;\n    \n    screen.newTimelineData(progressData);\n}","funcProperties":{"__isSmartRef__":true,"id":168},"__LivelyClassName__":"lively.Closure"},"167":{"this":{"__isSmartRef__":true,"id":0}},"168":{"user":"felix","__serializedExpressions__":["timestamp"],"timestamp":"new Date(\"Thu Dec 18 2014 16:13:18 GMT-0800 (PST)\")"},"169":{"varMapping":{"__isSmartRef__":true,"id":170},"source":"function requestTakeover(streamId, callback) {\n    var message = {\n        type: 'request-takeover',\n        senderId: this.session.sessionId,\n        streamId: streamId\n    }\n    \n    this.takeoverCallbacks.push({\n        streamId: streamId,\n        callback: callback\n    });\n    \n    this.send(message);\n}","funcProperties":{"__isSmartRef__":true,"id":171},"__LivelyClassName__":"lively.Closure"},"170":{"this":{"__isSmartRef__":true,"id":0}},"171":{"user":"felix","__serializedExpressions__":["timestamp"],"timestamp":"new Date(\"Wed Dec 17 2014 16:48:11 GMT-0800 (PST)\")"},"172":{"varMapping":{"__isSmartRef__":true,"id":173},"source":"function decideTakeover(streamId, requesterId, requesterName) {\n    Global.alertOK(requesterName + ' wants to take over');\n    \n    var decision = 'ok';\n    var message = {\n        type: 'response-takeover',\n        senderId: this.session.sessionId,\n        streamId: streamId,\n        requesterId: requesterId,\n        response: decision\n    }\n    \n    this.send(message);\n    \n    this.handleTakeover(streamId);\n}","funcProperties":{"__isSmartRef__":true,"id":174},"__LivelyClassName__":"lively.Closure"},"173":{"this":{"__isSmartRef__":true,"id":0}},"174":{"user":"felix","__serializedExpressions__":["timestamp"],"timestamp":"new Date(\"Thu Dec 18 2014 12:29:30 GMT-0800 (PST)\")"},"175":{"varMapping":{"__isSmartRef__":true,"id":176},"source":"function releaseStream(streamId) {\n    var message = {\n        type: 'release-stream',\n        senderId: this.session.sessionId,\n        streamId: streamId\n    }\n    \n    this.send(message);\n    \n    var screen = this.getScreen(streamId);\n    screen.stopStreaming();\n    this.subscribe([streamId]);\n}","funcProperties":{"__isSmartRef__":true,"id":177},"__LivelyClassName__":"lively.Closure"},"176":{"this":{"__isSmartRef__":true,"id":0}},"177":{"user":"felix","__serializedExpressions__":["timestamp"],"timestamp":"new Date(\"Thu Dec 18 2014 15:46:56 GMT-0800 (PST)\")"},"178":{"varMapping":{"__isSmartRef__":true,"id":179},"source":"function handleTakeover(streamId) {\n    var streamedMorph = this.submorphs.find(function(morph) {\n        return morph.streamingConfig.streamId === streamId;\n    });\n    // save the function 'streaming' and set a new one which returns false\n    // this results in not sending any frame anymore (== mute)\n    var config = streamedMorph.streamingConfig;\n    config.originalStreaming = config.streaming;\n    config.streaming = function() { return false; };\n}","funcProperties":{"__isSmartRef__":true,"id":180},"__LivelyClassName__":"lively.Closure"},"179":{"this":{"__isSmartRef__":true,"id":0}},"180":{"user":"felix","__serializedExpressions__":["timestamp"],"timestamp":"new Date(\"Thu Dec 18 2014 14:59:52 GMT-0800 (PST)\")"},"181":{"varMapping":{"__isSmartRef__":true,"id":182},"source":"function stopStreaming(morph, keepStream) {\n    var mediatype = morph.streamingConfig.mediatype;\n    switch (mediatype) {\n        case 'image':\n            morph.stopStepping();\n            morph.isBeingStreamed = false;\n            delete morph.streamingFunction;\n            break;\n        case 'audio':\n            morph.isBeingStreamed = false;\n            delete morph.streamingFunction;\n            break;\n        case 'data':\n            morph.stopStepping();\n            morph.isBeingStreamed = false;\n            delete morph.streamingFunction;\n            break;\n        default: \n            show('Unknown media type');\n            return;\n    }\n    \n    if (!keepStream) {\n        this.send({\n            type: 'unpublish',\n            streamId: morph.streamingConfig.streamId,\n            senderId: this.session.sessionId\n        });\n    }\n}","funcProperties":{"__isSmartRef__":true,"id":183},"__LivelyClassName__":"lively.Closure"},"182":{"this":{"__isSmartRef__":true,"id":0}},"183":{"user":"felix","__serializedExpressions__":["timestamp"],"timestamp":"new Date(\"Thu Dec 18 2014 15:40:30 GMT-0800 (PST)\")"},"184":{"varMapping":{"__isSmartRef__":true,"id":185},"source":"function continueStreaming(streamId) {\n    var morph = this.submorphs.find(function(morph) {\n        return morph.streamingConfig.streamId === streamId;\n    });\n    \n    // 'unmute' the streamed morph\n    var config = morph.streamingConfig;\n    config.streaming = config.originalStreaming;\n    delete config.originalStreaming;\n}","funcProperties":{"__isSmartRef__":true,"id":186},"__LivelyClassName__":"lively.Closure"},"185":{"this":{"__isSmartRef__":true,"id":0}},"186":{"user":"felix","__serializedExpressions__":["timestamp"],"timestamp":"new Date(\"Thu Dec 18 2014 15:50:40 GMT-0800 (PST)\")"},"187":{"varMapping":{"__isSmartRef__":true,"id":188},"source":"function loadAmountOfFrames(streamId, before, amount, callback) {\n    var message = {\n        type: 'request-recorded-data',\n        senderId: this.session.sessionId,\n        streamId: streamId,\n        requestId: Date.now(),\n        before: before,\n        amount: amount\n    }\n    \n    this.frameLoadingCallbacks.push({\n        requestId: message.requestId,\n        callback: callback\n    });\n    \n    this.send(message);\n}","funcProperties":{"__isSmartRef__":true,"id":189},"__LivelyClassName__":"lively.Closure"},"188":{"this":{"__isSmartRef__":true,"id":0}},"189":{"user":"felix","__serializedExpressions__":["timestamp"],"timestamp":"new Date(\"Tue Dec 23 2014 10:24:43 GMT-0800 (PST)\")"},"190":{"varMapping":{"__isSmartRef__":true,"id":191},"source":"function loadFramesByDuration(streamId, timecode, duration, callback) {\n    var message = {\n        type: 'request-recorded-data',\n        senderId: this.session.sessionId,\n        streamId: streamId,\n        requestId: Date.now(),\n        timecode: timecode,\n        duration: duration\n    }\n    \n    this.frameLoadingCallbacks.push({\n        requestId: message.requestId,\n        callback: callback\n    });\n    \n    this.send(message);\n}","funcProperties":{"__isSmartRef__":true,"id":192},"__LivelyClassName__":"lively.Closure"},"191":{"this":{"__isSmartRef__":true,"id":0}},"192":{"user":"felix","__serializedExpressions__":["timestamp"],"timestamp":"new Date(\"Mon Dec 22 2014 17:02:00 GMT-0800 (PST)\")"},"193":{"varMapping":{"__isSmartRef__":true,"id":194},"source":"function decodeAll(data) {\n    var _this = this;\n    data.forEach(function(frame) {\n        if (frame.lzwEncoded) {\n            frame.image = _this.lzwDecode(frame.image);\n        }\n    });\n    \n    return data;\n}","funcProperties":{"__isSmartRef__":true,"id":195},"__LivelyClassName__":"lively.Closure"},"194":{"this":{"__isSmartRef__":true,"id":0}},"195":{"user":"felix","tags":[],"__serializedExpressions__":["timestamp"],"timestamp":"new Date(\"Tue Dec 23 2014 11:18:28 GMT-0800 (PST)\")"},"isSimplifiedRegistry":true}}