diff --git a/Core/GDCore/BuiltinExtensions/FileExtension.cpp b/Core/GDCore/BuiltinExtensions/FileExtension.cpp index 4d1049f4e07..71f844e14fd 100644 --- a/Core/GDCore/BuiltinExtensions/FileExtension.cpp +++ b/Core/GDCore/BuiltinExtensions/FileExtension.cpp @@ -127,13 +127,13 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsFileExtension(gd::Platf .MarkAsAdvanced(); extension.AddAction("LaunchFile", - _("Launch a file"), - _("This action launch the specified file."), - _("Launch the file _PARAM0_"), + _("Open an URL or a file"), + _("This action launch the specified file or URL, in a browser (or in a new tab if the game is using the Web platform and is launched inside a browser)."), + _("Open URL (or file) _PARAM0_"), _("Files"), "res/actions/launchFile24.png", "res/actions/launchFile.png") - .AddParameter("file", _("Filename"), "",false) + .AddParameter("string", _("URL (or filename)"), "",false) .MarkAsAdvanced(); extension.AddAction("ExecuteCmd", diff --git a/GDJS/GDJS/BuiltinExtensions/FileExtension.cpp b/GDJS/GDJS/BuiltinExtensions/FileExtension.cpp index 828bef3007e..7fba0ed2285 100644 --- a/GDJS/GDJS/BuiltinExtensions/FileExtension.cpp +++ b/GDJS/GDJS/BuiltinExtensions/FileExtension.cpp @@ -41,6 +41,8 @@ FileExtension::FileExtension() .codeExtraInformation.SetFunctionName("gdjs.evtTools.storage.deleteElementFromJSONFile"); GetAllActions()["DeleteFichier"].SetGroup(_("Storage")) .codeExtraInformation.SetFunctionName("gdjs.evtTools.storage.clearJSONFile"); + GetAllActions()["LaunchFile"] + .codeExtraInformation.SetFunctionName("gdjs.evtTools.window.openURL"); StripUnimplementedInstructionsAndExpressions(); //Unimplemented things are listed here: /* @@ -54,16 +56,6 @@ FileExtension::FileExtension() .AddParameter("file", _("Filename"), "",false) .codeExtraInformation.SetFunctionName("FileExists").SetIncludeFile("GDCpp/BuiltinExtensions/FileTools.h"); - AddAction("LaunchFile", - _("Launch a file"), - _("This action launch the specified file."), - _("Launch the file _PARAM0_"), - _("Files"), - "res/actions/launchFile24.png", - "res/actions/launchFile.png") - .AddParameter("file", _("Filename"), "",false) - .codeExtraInformation.SetFunctionName("LaunchFile").SetIncludeFile("GDCpp/BuiltinExtensions/FileTools.h"); - AddAction("ExecuteCmd", _("Execute a command"), _("This action execute the specified command."), diff --git a/GDJS/GDJS/Exporter.cpp b/GDJS/GDJS/Exporter.cpp index 012b46c8ccf..9701ea4acec 100644 --- a/GDJS/GDJS/Exporter.cpp +++ b/GDJS/GDJS/Exporter.cpp @@ -540,6 +540,12 @@ bool Exporter::ExportWholeProject(gd::Project & project, std::string exportDir, fs.MkDir(exportDir+"/Extensions"); std::vector includesFiles; + if (exportForCocoonJS) + { + fs.MkDir(exportDir+"/libs/CocoonJS"); + includesFiles.push_back("libs/CocoonJS/cocoon.min.js"); + } + gd::Project exportedProject = project; //Export the resources ( before generating events as some resources filenames may be updated ) diff --git a/GDJS/Runtime/libs/CocoonJS/cocoon.min.js b/GDJS/Runtime/libs/CocoonJS/cocoon.min.js new file mode 100644 index 00000000000..cc21280afa9 --- /dev/null +++ b/GDJS/Runtime/libs/CocoonJS/cocoon.min.js @@ -0,0 +1,4 @@ +!function(){Cocoon=window.Cocoon?window.Cocoon:{},Cocoon.version="3.0.0",Cocoon.nativeAvailable=!!window.ext,Cocoon.extend=function(subc,superc){var subcp=subc.prototype,CocoonJSExtendHierarchyChainClass=function(){};CocoonJSExtendHierarchyChainClass.prototype=superc.prototype,subc.prototype=new CocoonJSExtendHierarchyChainClass,subc.superclass=superc.prototype,subc.prototype.constructor=subc,superc.prototype.constructor===Object.prototype.constructor&&(superc.prototype.constructor=superc);for(var method in subcp)subcp.hasOwnProperty(method)&&(subc.prototype[method]=subcp[method])},Cocoon.clone=function(obj,copy){if(null==obj||"object"!=typeof obj)return obj;var arr=[];for(var attr in obj)arr.push(copy.hasOwnProperty(attr)?copy[attr]:obj[attr]);return arr},Cocoon.callNative=function(nativeExtensionObjectName,nativeFunctionName,args,async){if(Cocoon.nativeAvailable){var argumentsArray=Array.prototype.slice.call(args);argumentsArray.unshift(nativeFunctionName);var nativeExtensionObject=ext[nativeExtensionObjectName],makeCallFunction=async?nativeExtensionObject.makeCallAsync:nativeExtensionObject.makeCall,ret=makeCallFunction.apply(nativeExtensionObject,argumentsArray),finalRet=ret;if("string"==typeof ret)try{finalRet=JSON.parse(ret)}catch(error){console.log(error)}return finalRet}},Cocoon.getObjectFromPath=function(baseObject,objectPath){for(var parts=objectPath.split("."),obj=baseObject,i=0,len=parts.length;len>i;++i)obj[parts[i]]=obj[parts[i]]||void 0,obj=obj[parts[i]];return obj},Cocoon.EventHandler=function(nativeExtensionObjectName,CocoonExtensionObjectName,nativeEventName,chainFunction){return this.listeners=[],this.listenersOnce=[],this.chainFunction=chainFunction,this.addEventListener=function(listener){if(chainFunction){var f=function(){chainFunction.call(this,arguments.callee.sourceListener,Array.prototype.slice.call(arguments))};listener.CocoonEventHandlerChainFunction=f,f.sourceListener=listener,listener=f}var CocoonExtensionObject=Cocoon.getObjectFromPath(Cocoon,CocoonExtensionObjectName);if(CocoonExtensionObject&&CocoonExtensionObject.nativeAvailable)ext[nativeExtensionObjectName].addEventListener(nativeEventName,listener);else{var indexOfListener=this.listeners.indexOf(listener);0>indexOfListener&&this.listeners.push(listener)}},this.addEventListenerOnce=function(listener){if(chainFunction){var f=function(){chainFunction(arguments.callee.sourceListener,Array.prototype.slice.call(arguments))};f.sourceListener=listener,listener=f}var CocoonExtensionObject=Cocoon.getObjectFromPath(Cocoon,CocoonExtensionObjectName);if(CocoonExtensionObject.nativeAvailable)ext[nativeExtensionObjectName].addEventListenerOnce(nativeEventName,listener);else{var indexOfListener=this.listeners.indexOf(listener);0>indexOfListener&&this.listenersOnce.push(listener)}},this.removeEventListener=function(listener){chainFunction&&(listener=listener.CocoonEventHandlerChainFunction,delete listener.CocoonEventHandlerChainFunction);var CocoonExtensionObject=Cocoon.getObjectFromPath(Cocoon,CocoonExtensionObjectName);if(CocoonExtensionObject.nativeAvailable)ext[nativeExtensionObjectName].removeEventListener(nativeEventName,listener);else{var indexOfListener=this.listeners.indexOf(listener);indexOfListener>=0&&this.listeners.splice(indexOfListener,1)}},this.removeEventListenerOnce=function(listener){chainFunction&&(listener=listener.CocoonEventHandlerChainFunction,delete listener.CocoonEventHandlerChainFunction);var CocoonExtensionObject=Cocoon.getObjectFromPath(Cocoon,CocoonExtensionObjectName);if(CocoonExtensionObject.nativeAvailable)ext[nativeExtensionObjectName].removeEventListenerOnce(nativeEventName,listener);else{var indexOfListener=this.listenersOnce.indexOf(listener);indexOfListener>=0&&this.listenersOnce.splice(indexOfListener,1)}},this.notifyEventListeners=function(){var CocoonExtensionObject=Cocoon.getObjectFromPath(Cocoon,CocoonExtensionObjectName);if(CocoonExtensionObject&&CocoonExtensionObject.nativeAvailable)ext[nativeExtensionObjectName].notifyEventListeners(nativeEventName);else{var argumentsArray=Array.prototype.slice.call(arguments),listeners=Array.prototype.slice.call(this.listeners),listenersOnce=Array.prototype.slice.call(this.listenersOnce),_this=this;setTimeout(function(){for(var i=0;i=200&&xhr.status<=299||0===xhr.status){checkEmulatedWebViewReady();var callback=function(){Cocoon.App.onLoadInTheWebViewSucceed.notifyEventListeners(path),Cocoon.App.EmulatedWebViewIFrame.removeEventListener("load",callback)};Cocoon.App.EmulatedWebViewIFrame.addEventListener("load",callback),Cocoon.App.EmulatedWebViewIFrame.contentWindow.location.href=path}else this.onreadystatechange=null,Cocoon.App.onLoadInTheWebViewFailed.notifyEventListeners(path)},xhr.open("GET",path,!0),xhr.send()}},extension.reloadWebView=function(){Cocoon.App.nativeAvailable&&navigator.isCocoonJS?Cocoon.callNative("IDTK_APP","reloadWebView",arguments):(checkEmulatedWebViewReady(),Cocoon.App.EmulatedWebViewIFrame.contentWindow.location.reload())},extension.showTheWebView=function(x,y,width,height){Cocoon.App.nativeAvailable&&navigator.isCocoonJS?Cocoon.callNative("IDTK_APP","showTheWebView",arguments):(checkEmulatedWebViewReady(),Cocoon.App.EmulatedWebViewIFrame.style.width=(width?width/window.devicePixelRatio:window.innerWidth)+"px",Cocoon.App.EmulatedWebViewIFrame.style.height=(height?height/window.devicePixelRatio:window.innerHeight)+"px",Cocoon.App.EmulatedWebView.style.left=(x?x:0)+"px",Cocoon.App.EmulatedWebView.style.top=(y?y:0)+"px",Cocoon.App.EmulatedWebView.style.width=(width?width/window.devicePixelRatio:window.innerWidth)+"px",Cocoon.App.EmulatedWebView.style.height=(height?height/window.devicePixelRatio:window.innerHeight)+"px",Cocoon.App.EmulatedWebView.style.display="block")},extension.hideTheWebView=function(){if(Cocoon.App.nativeAvailable){var javaScriptCodeToForward="ext.IDTK_APP.makeCall('hide');";return Cocoon.App.forwardAsync(javaScriptCodeToForward)}navigator.isCocoonJS||(checkEmulatedWebViewReady(),Cocoon.App.EmulatedWebView.style.display="none")},extension.exitCallback=function(appShouldFinishCallback){navigator.isCocoonJS&&Cocoon.App.nativeAvailable&&(window.onidtkappfinish=appShouldFinishCallback)},extension.forwardedEventFromTheWebView=function(eventName,eventDataString){var eventData=JSON.parse(eventDataString);eventData.target=window;var event=new Event(eventName);for(var att in eventData)event[att]=eventData[att];event.target=window,window.dispatchEvent(event);for(var canvases=document.getElementsByTagName("canvas"),i=0;i=0&&(eventData[att]=event[att]);var jsCode="Cocoon && Cocoon.App && Cocoon.App.forwardedEventFromTheWebView && Cocoon.App.forwardedEventFromTheWebView("+JSON.stringify(eventName)+", '"+JSON.stringify(eventData)+"');";Cocoon.App.forward(jsCode)}if(Cocoon.App.proxifyConsole(),!Cocoon.App.nativeAvailable&&"CocoonJS_App_ForCocoonJS_WebViewIFrame"==window.name){Cocoon.App.forwardEventsToCocoonJSEnabled=!1;var EVENT_ATTRIBUTES=["timeStamp","button","type","x","y","pageX","pageY","clientX","clientY","offsetX","offsetY"],EVENTS=["dblclick","touchmove","mousemove","touchend","touchcancel","mouseup","touchstart","mousedown","release","dragleft","dragright","swipeleft","swiperight"];for(i=0;i=0&&eventListeners.splice(eventCallbackIndex,1)}},this},parentObject[typeName]._cocoonjs_proxy_type_data={originalType:originalType,proxyObjects:[]},parentObject[typeName]._cocoonjs_proxy_type_data.deleteProxyObject=function(object){var proxyObjectKey=extension.getKeyForValueInDictionary(this.proxyObjects,object);if(proxyObjectKey){var jsCode="Cocoon.Proxify.deleteDestinationProxyObject("+JSON.stringify(typeName)+", "+object._cocoonjs_proxy_object_data.id+");";Cocoon.App.forwardAsync(jsCode),object._cocoonjs_proxy_object_data=null,delete this.proxyObjects[proxyObjectKey]}},parentObject[typeName]._cocoonjs_proxy_type_data.callProxyObjectEventHandler=function(id,eventHandlerName){var object=this.proxyObjects[id],eventHandler=object._cocoonjs_proxy_object_data.eventHandlers[eventHandlerName];eventHandler&&eventHandler({target:object})},parentObject[typeName]._cocoonjs_proxy_type_data.callProxyObjectEventListeners=function(id,eventTypeName){for(var object=this.proxyObjects[id],eventListeners=object._cocoonjs_proxy_object_data.eventListeners[eventTypeName].slice(),i=0;i0&&!arguments[0]){var str="Assertion failed: "+(arguments.length>1?arguments[1]:"");newConsole.error(str)}}),window.console=newConsole}},extension.deproxifyConsole=function(){!window.navigator.isCocoonJS&&Cocoon.nativeAvailable&&"undefined"!=typeof Cocoon.originalConsole&&(window.console=Cocoon.originalConsole,Cocoon.originalConsole=void 0)},extension}),Cocoon.define("Cocoon.Device",function(extension){"use strict";return extension.DeviceInfo={os:null,version:null,dpi:null,brand:null,model:null,imei:null,platformId:null,odin:null,openudid:null},extension.getDeviceId=function(){return Cocoon.nativeAvailable?window.ext.IDTK_APP.makeCall("getDeviceId"):void 0},extension.getDeviceInfo=function(){return Cocoon.nativeAvailable?window.ext.IDTK_APP.makeCall("getDeviceInfo"):void 0},extension.getOrientation=function(){return Cocoon.nativeAvailable?window.ext.IDTK_APP.makeCall("getPreferredOrientation"):0},extension.setOrientation=function(preferredOrientation){Cocoon.nativeAvailable&&window.ext.IDTK_APP.makeCall("setPreferredOrientation",preferredOrientation)},extension.Orientations={PORTRAIT:1,PORTRAIT_UPSIDE_DOWN:2,LANDSCAPE_LEFT:4,LANDSCAPE_RIGHT:8,LANDSCAPE:12,BOTH:15},extension.autoLock=function(){return Cocoon.nativeAvailable?Cocoon.callNative("IDTK_APP","setAutoLockEnabled",arguments):void 0},extension}),Cocoon.define("Cocoon.Motion",function(extension){"use strict";return extension.nativeAvailable=Cocoon.nativeAvailable,extension.setAccelerometerInterval=function(updateIntervalInSeconds){return Cocoon.Motion.nativeAvailable?window.ext.IDTK_APP.makeCall("setAccelerometerUpdateIntervalInSeconds",updateIntervalInSeconds):void 0},extension.getAccelerometerInterval=function(){return Cocoon.Motion.nativeAvailable?window.ext.IDTK_APP.makeCall("getAccelerometerUpdateIntervalInSeconds"):void 0},extension.setGyroscopeInterval=function(updateIntervalInSeconds){return Cocoon.Motion.nativeAvailable?window.ext.IDTK_APP.makeCall("setGyroscopeUpdateIntervalInSeconds",updateIntervalInSeconds):void 0},extension.getGyroscopeInterval=function(){Cocoon.Motion.nativeAvailable&&window.ext.IDTK_APP.makeCall("getGyroscopeUpdateIntervalInSeconds")},extension}),Cocoon.define("Cocoon.Touch",function(extension){return extension.addADivToDisableInput=function(){var div=document.createElement("div");div.id="CocoonJSInputBlockingDiv",div.style.left=0,div.style.top=0,div.style.width="100%",div.style.height="100%",div.style.position="absolute",div.style.backgroundColor="transparent",div.style.border="0px solid #000",div.style.zIndex=999999999,document.body.appendChild(div)},extension.removeTheDivToEnableInput=function(){var div=document.getElementById("CocoonJSInputBlockingDiv");div&&document.body.removeChild(div)},extension.disable=function(){Cocoon.nativeAvailable?window.ext.IDTK_APP.makeCall("disableTouchLayer","CocoonJSView"):navigator.isCocoonJS||Cocoon.App.EmulatedWebViewIFrame||(Cocoon.App.forwardEventsToCocoonJSEnabled=!1,Cocoon.App.forwardAsync("Cocoon && Cocoon.Touch && Cocoon.Touch.disable();"))},extension.enable=function(){Cocoon.nativeAvailable?window.ext.IDTK_APP.makeCall("enableTouchLayer","CocoonJSView"):navigator.isCocoonJS||Cocoon.App.EmulatedWebViewIFrame||(Cocoon.App.forwardEventsToCocoonJSEnabled=!0,Cocoon.App.forwardAsync("Cocoon && Cocoon.Touch && Cocoon.Touch.enable();"))},extension.disableInWebView=function(){Cocoon.nativeAvailable?window.ext.IDTK_APP.makeCall("disableTouchLayer","WebView"):navigator.isCocoonJS||(Cocoon.App.EmulatedWebViewIFrame?Cocoon.App.forwardAsync("Cocoon && Cocoon.Touch && Cocoon.Touch.disableInWebView();"):Cocoon.Touch.addADivToDisableInput())},extension.enableInWebView=function(){Cocoon.nativeAvailable?window.ext.IDTK_APP.makeCall("enableTouchLayer","WebView"):navigator.isCocoonJS||(Cocoon.App.EmulatedWebViewIFrame?Cocoon.Touch.forwardAsync("Cocoon && Cocoon.Touch && Cocoon.Touch.enableInWebView();"):Cocoon.Touch.removeTheDivToEnableInput())},extension}),Cocoon.define("Cocoon.Widget",function(extension){"use strict";return extension.WebDialog=function(){if(Cocoon.App.nativeAvailable)this.webDialogID=window.ext.IDTK_APP.makeCall("createWebDialog");else{var iframe=document.createElement("iframe");iframe.id="CocoonJSWebDialogIFrame",iframe.name="CocoonJSWebDialogIFrame",iframe.style.cssText="position:fixed;left:0;top:0;bottom:0;right:0; width:100%; height:100%;margin:0;padding:0;";var me=this;iframe.onload=function(){me.iframeloaded=!0;var js="Cocoon = {}; Cocoon.Widget = {}; Cocoon.Widget.WebDialog = {} Cocoon.Widget.WebDialog.close = function(){ window.parent.CocoonJSCloseWebDialog();};";me.evalIframe(js);for(var i=0;i0}},extension.Manager=new extension.ManagerService,extension}),Cocoon.define("Cocoon.Social",function(extension){return extension.LocalStorageService=function(){Cocoon.Social.LocalStorageService.superclass.constructor.call(this),this.fakeSocialService=!0},extension.LocalStorageService.prototype={loggedIn:!1,keys:{score:"Cocoon.Social.LocalStorageService.score",earnedAchievements:"Cocoon.Social.LocalStorageService.earned"},isLoggedIn:function(){return this.loggedIn},login:function(callback){this.loggedIn||this.onLoginStatusChanged.notifyEventListeners(!0),this.loggedIn=!0,callback&&setTimeout(function(){callback(!0)},0)},logout:function(callback){this.loggedIn&&this.onLoginStatusChanged.notifyEventListeners(!0),this.loggedIn=!1,callback&&setTimeout(function(){callback()},0)},getLoggedInUser:function(){return new Cocoon.Social.User("me","LocalStorage")},requestUser:function(callback,userID){var user=new Cocoon.Social.User(userID||"me","LocalStorage");callback&&setTimeout(function(){callback(user)},0)},requestScore:function(callback){var scoreItem=localStorage.getItem(this.keys.score),score=parseInt(scoreItem)||0;setTimeout(function(){callback(new Cocoon.Social.Score("me",score))},0)},submitScore:function(score,callback){var scoreItem=localStorage.getItem(this.keys.score),topScore=parseInt(scoreItem)||0;score>topScore&&localStorage.setItem(this.keys.score,score),callback&&setTimeout(function(){callback()},0)},getLocalStorageEarnedAchievements:function(){var achievementsItem=localStorage.getItem(this.keys.earnedAchievements),earned=[];if(achievementsItem){var array=JSON.stringify(achievementsItem);array&&array.length&&(earned=array)}return earned},requestAchievements:function(callback){var earned=this.getLocalStorageEarnedAchievements();setTimeout(function(){callback(earned)},0)},submitAchievement:function(achievementID,callback){if(null===achievementID||"undefined"==typeof achievementID)throw"No achievementID specified";for(var earned=this.getLocalStorageEarnedAchievements(),exists=!1,i=0;i0){var item=response.items[0],data=new Cocoon.Social.Score(playerId,item.scoreValue,"","",item.leaderboard_id);callback(data,null)}else callback(null,null)}})},submitScore:function(score,callback,params){params=params||{};var leaderboardID=params.leaderboardID||this.gapi.settings.defaultLeaderboard;if(!leaderboardID)throw"leaderboardID not provided in the params. You can also set the default leaderboard in the init method";this.gapi.client.request({path:this.gapi.gamesAPI+"/leaderboards/"+leaderboardID+"/scores",method:"POST",params:{score:score},callback:function(response){callback&&callback(response?response.error:null)}})},showLeaderboard:function(callback,params){params=params||{};var leaderboardID=params.leaderboardID||this.gapi.settings.defaultLeaderboard;if(!leaderboardID)throw"leaderboardID not provided in the params. You can also set the default leaderboard in the init method";var ios=/(iPad|iPhone|iPod)/gi.test(navigator.userAgent);if(!ios&&this.gapi.nativeAvailable){var timeScope=params.timeScope||0;Cocoon.callNative(this.gapi.nativeExtensionName,"showLeaderboard",[leaderboardID,timeScope,callback],!0)}else{if(!this._leaderboardsTemplate)throw"Please, provide a html template for leaderboards with the setTemplates method";var dialog=new Cocoon.Widget.WebDialog,callbackSent=!1;dialog.show(this._leaderboardsTemplate,function(error){dialog.closed=!0,!callbackSent&&callback&&callback(error)});var collection=params.friends?"SOCIAL":"PUBLIC",timeSpan="ALL_TIME";params.timeScope===Cocoon.Social.TimeScope.WEEK?timeSpan="WEEKLY":params.timeScope===Cocoon.Social.TimeScope.TODAY&&(timeSpan="DAILY"),this.gapi.client.request({path:this.gapi.gamesAPI+"/leaderboards/"+leaderboardID+"/window/"+collection,method:"GET",params:{timeSpan:timeSpan},callback:function(response){if(!dialog.closed){if(response.error)return void(callback&&(callbackSent=!0,callback(response.error),dialog.close()));var scores=[],items=[];response&&response.items&&(items=response.items.slice(0)),response&&response.playerScore&&items.push(response.playerScore);for(var i=0;i3?arguments[1]:"GET",options=null;3==arguments.length&&(options=arguments[1]),4==arguments.length&&(options=arguments[2]);var callback=arguments.length>1?arguments[arguments.length-1]:function(){};return Cocoon.callNative(this.nativeExtensionName,"api",[openGraph,httpMethod,options,callback],!0)}FB.api(path,method,params,cb)},ui:function(params,cb){if(this.nativeAvailable){var params=arguments[0],callback=arguments.length>1?arguments[1]:function(){};return Cocoon.callNative(this.nativeExtensionName,"ui",[params,callback],!0)}navigator.isCocoonJS||FB.ui(params,cb)},requestAdditionalPermissions:function(permissionsType,permissions,callback){if(this.nativeAvailable){var permsArray=permissions.split(",");Cocoon.callNative(this.nativeExtensionName,"requestAdditionalPermissions",[permissionsType,permsArray,function(session,error){callback&&callback(fromCocoonFBSessionToFBAPISession(session,error))}],!0)}else FB.login(callback,{scope:permissions})},getPermissions:function(callback){this.api("me/permissions",function(response){callback(response.data&&response.data[0]?response.data[0]:{})})},showShareDialog:function(params,callback){this.nativeAvailable?Cocoon.callNative(this.nativeExtensionName,"showShareDialog",[params,callback],!0):(params.method="feed",FB.ui(params,callback))},uploadPhoto:function(file,callback){this.nativeAvailable?Cocoon.callNative(this.nativeExtensionName,"uploadPhoto",[file,callback],!0):callback({error:{message:"Not implemented"}})},showFriendPicker:function(callback){this.nativeAvailable?Cocoon.callNative(this.nativeExtensionName,"showFriendPicker",[callback],!0):callback({error:{message:"Not implemented"}})}},extension.FacebookEvent=function(nativeAvailable){return this.nativeAvailable=nativeAvailable,this},extension.FacebookEvent.prototype={subscribe:function(name,callback){if(this.nativeAvailable){var eventKey=name+"listeners";this[eventKey]=this[eventKey]||[],this[eventKey].push(callback)}else navigator.isCocoonJS||FB.Event.subscribe(name,callback)},unsubscribe:function(name,callback){if(this.nativeAvailable){var eventKey=name+"listeners",array=this[eventKey]; +if(array){var index=array.indexOf(callback);-1!==index&&array.splice(index,1)}}else navigator.isCocoonJS||FB.Event.unsubscribe(name,callback)},notify:function(name,param){var eventKey=name+"listeners",array=this[eventKey];if(array)for(var i=0;i0){var data=fromFBScoreToCocoonScore(response.data[0]);callback(data,null)}else callback(null,null)})},submitScore:function(score,callback,params){var me=this;this.preparePublishAction(function(granted){granted?me.requestScore(function(currentScore,error){if(error)return void(callback&&callback(error));var topScore=currentScore?currentScore.score:0;if(topScore>=score)return void(callback&&callback(null));var apiCall="/"+(params&¶ms.userID?params.userID:"me")+"/scores";me.fb.api(apiCall,"POST",{score:score},function(response){callback&&callback(response.error)})},params):callback&&callback({message:"No publish_actions permission granted"})})},showLeaderboard:function(callback){if(!this._leaderboardsTemplate)throw"Please, provide a html template for leaderboards with the setTemplates method";var dialog=new Cocoon.Widget.WebDialog,callbackSent=!1;dialog.show(this._leaderboardsTemplate,function(error){dialog.closed=!0,!callbackSent&&callback&&callback(error)});var me=this;this.fb.api(me.fb._appId+"/scores",function(response){if(!dialog.closed){if(response.error)return void(callback&&(callbackSent=!0,callback(response.error),dialog.close()));var scores=[];if(response.data&&response.data.length)for(var i=0;i=request.minPlayers){for(var playerIDs=[],i=0;i