How can I access/get a player that already created?
In my case want add event listener on a player already create like this :
https://developers.google.com/youtube/iframe_api_reference#Example_Video_Player_Constructors
The player is a local variable so it seems if I haven't global variable to reference it I can't access to this player outside the function.
To understand my problem, there is a website with an Youtube iframe wich defines a player locally in a function.
I don't want create a new player on this iFrame and add event listener like this :
https://developers.google.com/youtube/iframe_api_reference#Adding_event_listener
player.addEventListener(event:String, listener:String):Void
I want add event listener to keep listener already defined when the player was created.
I don't want change the code wich declare the player because my code is in Google Tag Manager and it will add behavior on players already defined.
Google Tag Manager
I'm pratically sure I can access player from iFrame but I don't find how...
Or with the object window['YT'] defined in the script : iframe_api
Thanks
Regards
Thomas
So, I find this solution :
https://stackoverflow.com/a/36366737/6748629
I can get the player declare on an iFrame youtube with :
YT.get(#iFrame_id)
So i just have to parse the document, get player and addEventListener :
for (var e = document.getElementsByTagName("iframe"), x = e.length; x--;) {
if (/youtube.com\/embed/.test(e[x].src)) {
// get player if exist or create one without listener
var player = YT.get(e[x].id)||new YT.Player(e[x], {
events: {
}
});
// now you can add listener and keep behavior when player already declared
player.addEventListener("onStateChange",onPlayerStateChange);
}
}
I hope it will help others.
Related
When cloning a button at runtime using Instantiate(), which contained listeners on it's onClick event, the listeners are not present in the clone.
The behaviour can be tested by having a Canvas with a button and this script attached:
void Start () {
var button = transform.GetChild (0);
button.GetComponent<Button> ().onClick.AddListener (new UnityAction(() => Debug.Log("Event triggered!")));
var button2 = Instantiate (button);
button2.SetParent (transform);
}
The cloned button will not print anything to the console when clicked.
Is there a way to clone a GameObject so that it retains event listeners?
Runtime listeners are not persistent and then not serialized. As a result they are not passed on when you clone the button.
Either you'd have to add the method to a script and attach the script to your prefab for it to be serialized along or assign it by code like you do for the first one.
Instantiation. When you call Instantiate() on either a prefab, or a
gameobject that lives in the scene, or on anything else for that
matter (everything that derives from UnityEngine.Object can be
serialized), we serialize the object, then create a new object, and
then we “deserialize” the data onto the new object. (We then run the
same serialization code again in a different variant, where we use it
to report which other UnityEngine.Object’s are being referenced. We
then check for all referenced UnityEngine.Object’s if they are part
of the data being Instantiated(). If the reference is pointing to
something “external” (like a texture) we keep that reference as it
is, if it is pointing to something “internal” (like a child
gameobject), we patch the reference to the corresponding copy).
http://blogs.unity3d.com/2014/06/24/serialization-in-unity/
As for AddListener
This function adds a "non persistent" delegate, which means it will not show up in the inspector, and will be forgotten when you exit play mode in the editor. These differ from "persistent" listeners which you can add during edit-time in the inspector, and which persist between edit and play mode.
https://docs.unity3d.com/ScriptReference/Events.UnityEvent.AddListener.html
For those reasons, I assume the non-persistent is not serialized and then not passed to the cloned object.
I'm building out a map app that pulls in tiles via an ImgTileProvider. I would like to know when the tiles have been correctly loaded, but the only method exposed by the ImgTileProvider.Options is the getUrl function, which helps me know where to get an image tile, not when it returns.
Is there an event that is fired after each tile/all tiles has/have been created/rendered to the page? I noticed that there was an addListener function available for ImgTileProvider instances, which I would subscribe to if I knew which event was being fired on image create.
So, as I was writing this question, a thought occurred: why not check the source? Using Chrome, I prettified the map-render-display.js file and looked for events added via the addListener function. I didn't see anything for ImgTileProvider, but I found several other providers subscribing to a response event. So I added an event listener on my ImgTileProvider instance and it worked! Below is a sample of working code.
// A magic number representing the total number of tiles in the map.
var magicNumber = 22;
// Returns a new ImgTileProvider.
tileProvider = self.imgTileProvider();
tileProvider.addListener("response", function () {
magicNumber--;
if (magicNumber === 0) {
console.log("All tiles loaded");
}
});
Since some days ago I'm struggling to find out the best way to add a custom action in every dashlet's title bar by default.
At this stage, I know the actions are set up as a widget in every dashlet's webscript. For example, in docsummary.get.js:
var dashletTitleBarActions = {
id : "DashletTitleBarActions",
name : "Alfresco.widget.DashletTitleBarActions",
useMessages : false,
options : {
actions: [
{
cssClass: "help",
bubbleOnClick:
{
message: msg.get("dashlet.help")
},
tooltip: msg.get("dashlet.help.tooltip")
}
]
}
};
model.widgets = [docSummary, dashletResizer, dashletTitleBarActions];
Then, the widget is instanciated in when the page is rendered.
I have figured out the next approaches:
Augment the "actions" array of every widget instance with the action I need in a custom JS snippet, every time the page is rendered. I have performed some tests without success using techniques like this: http://acidmartin.wordpress.com/2012/03/19/getting-instance-names-of-a-javascript-object/
Modify the prototype of Alfresco.widget.DashletTitleBarActions by adding my custom action. I believe it doesn't work either as the "actions" object is always overridden when the widget is instantiated, as you can see in the code above pasted.
Create an extension module for every dashlet's webscript which adds the custom action for every Alfresco.widget.DashletTitleBarActions widget definition, similarly as Dave Drapper explains in his post http://blogs.alfresco.com/wp/ddraper/2012/05/22/customizing-share-javascript-widget-instantiation-part-1/
Get every dashlet div container and add the action required directly manipulating the DOM once the page is ready. It should work but is something I consider slightly dirty and inconsistent, hence I would like to avoid it.
Could anyone imagine a better and feasible solution??
Let's start with adding an action to a single existing dashlet. As you suggest in (3) you can define an extensibility module to change the behaviour of the dashlet by intercepting and modifying its model.
Creating an extensibility module is well covered on the blog article and follow-up posts, but the trick here is to provide a controller JavaScript extension which locates the DashletTitleBarActions widget and adds your action to it, e.g.
if (model.widgets)
{
for (var i = 0; i < model.widgets.length; i++)
{
var widget = model.widgets[i];
if (widget.id == "DashletTitleBarActions")
{
widget.actions.push({...})
}
}
}
What you put in the object literal depends on how your action is implemented. If you require some client-side behaviour (rather than say, a static link) then you will also need to bind that in using a CustomEvent - see the RSS Feed dashlet org/alfresco/components/dashlets/rssfeed.get.html.ftl for an example.
The down-side of an extensibility module is that you will need to define an explicit extension JS file for each dashlet. You could easily put the code above in a central file and then include it in each dashlet extension where it is needed, e.g.
<import resource="classpath:alfresco/site-webscripts/org/myco/utils/dashlet.utils.js">
I would take the road number 2 here.
The definition of this widget is in share.js file ({share.context}/js/share.js). For Alfresco 4.2, DashletTitleBarActions is defined at around line 1700. In the onReady handler of the widget, there's a loop that processes the actions.
// Reverse the order of the arrays so that the first entry is furthest to the left...
this.options.actions.reverse();
// Iterate through the array of actions creating a node for each one...
for (var i = 0; i < this.options.actions.length; i++)
{
As you can see, it reverses the order of actions parameter, and then adds starts to loop the actions. So depending if you want your action to be the first or last, you could probably edit this file and add your custom action:
myAction = {
"cssClass": "customCSS"
, "tooltip": this.msg("slingshot.messages.generic.tooltip")
, "eventOnClick": ...
...
}
this.options.actions.push(myAction);
// now move on with the rest of the loop
for (...)
Of course, that requires overwriting shares' own js file, and not extending it. If you can't do that, you would then have to include a custom JS file on each page where share.js is also included and make sure it's executed after share.js but before any of the widgets are ready, and overwrite the onReady method of the widget itself so that it does this.
Here's my scenario. When child is added via a browser event post page load, I want to indicate it in the title. But on page load, child added is called as well.
How can I differentiate the initial child added vs one where a new entry has been actually added.
Thanks, Tim
Firebase very intentionally does not distinguish "initial" data from "new" data. This allows for much simpler development in most cases, because you only have to write one set of logic for your data, rather than handling both the initial data case and the new data case.
I can see how you would want the distinction in this case. I'm not sure exactly what you're doing, but if you're building a chat application, you might want to flash the title based on the timestamp of the most recent message rather than whether or not it's a "new" message. This would allow the title to flash on page load if a message was sent slightly before the page was loaded, which may be desirable. In some other cases, you may actually want to flash the title for unread data, and you may want to consider marking children as "read" and flashing the title only for children that show up without the "read" bit. This would allow things to work seamlessly across page refreshes.
If you absolutely need to know when "new" data shows up, you could try using "once" with a "value" event type to get the data, and then use "on" with a startAt query and a "child_added" event type to display new data after that. It would look something like this:
var data = new Firebase(...);
data.once("value", function(d) {
//TODO: display initial state...
data.startAt(null, <last id in snapshot>).on("child_added", function(newMessSnapshot) {
//TODO: render new child and flash title bar.
}
}
Or if you want to do it the really simple way, you could just set a timer so that the title won't flash for any messages received within the first N seconds of page load.
Hope that helps!
You can set up a call that only receives new events rather than ones already existing using the approach from this SO question.
So basically, call .on('child_added'...) with endAt and limit. You still get one entry (the last one), which you can just ignore:
// connect to firebase
var fb = new Firebase(...);
// retrieve the last record from `ref` (endAt() tells it to start at the end)
var first = true;
fb.child('table_name').endAt().limit(1).on('child_added', function(snapshot) {
if( first ) {
// ignore the first snapshot, which is an existing record
first = false;
return;
}
// all records after the last continue to invoke this function
console.log(snapshot.name(), snapshot.val());
});
I thought I would update Kato's answer as Datasnapshot.name() and limit have now both been depreciated.
I decided to make it a bit more simple as well ;)
// connect to firebase
var fb = new Firebase("https://your-firebase-ref/");
var firstTime = false;
fb.limitToLast(1).on('child_added', function(snapshot) {
if (firstTime) {
console.log(snapshot.key(), snapshot.val());
};
firstTime = true;
});
I hope this helps
So I'm trying to build a tool that will allow me and other users to all open the same .swf, and then I as the Admin user am able to interact with mine while they all see my mouse movements and button clicks etc on theirs.
I'm using BlazeDS to manage this and I'm getting data sent back and forth etc - no difficulties there. The issue I'm running into is this:
In an "Admin" instance, I click a button. I capture that X and Y, then tell Blaze to tell my clients to dispatch a Click event at that X and Y. On my client side, I get that data and dispatch a Click event at that X and Y - but the click is actually caught at the stage level. The click on my client side takes place UNDER all of my buttons and other content - so the whole thing fails.
Does this make sense? Is there a way to tell it to start the click event at the top?
If you are unable to architect the loaded swf's to use a better architecture you could try something a little more hackish to get buttons working.
Have a look at the methods getObjectsUnderPoint and areInaccessibleObjectsUnderPoint of the DisplayObjectContainer. Combined with hasEventListener you should be able to emulate what you want.
Here is some untested pseudo-code:
function detectClick(pt:Point):void
{
var objsUnderPoint:Array = containerStage.getObjectsUnderPoint(pt);
var clickable:Array = [];
for each(dispObj:DisplayObject in objsUnderPoint)
{
if(dispObj.hasEventListener(MouseEvent.CLICK))
{
clickable.push(dispObj);
}
}
if(clickable.length)
{
// sort on depth here
// that might be tricky since you'll be looking at grandchildren
// and not just children but it is doable.
var topMostClickable:DisplayObject = ???
topMostClickable.dispatchEvent(new MouseEvent(MouseEvent.CLICK, true, false));
}
}
areInaccessibleObjectsUnderPoint is important if you think their might be security restrictions (e.g. cross-domain issues) so you can debug if things go wrong.
Also note that you may want to (or need to) fill in more details of the MouseEvent (like the proper target, localX, localyY etc.)
Sounds like you need to set focus to the top most component of the x and y positions before dispatching the click event.
That said, I wonder what the use case is for something like this; as opposed to using a screen sharing tool such as Connect.
This seems like a poor way to implement what you are trying to do. If you "click" in the admin tool you are probably actually triggering some event. Why not trigger that event instead of sending the mouse click?
I'd just keep a map of actions and then when something happens in the Admin interface send the key to the action.
e.g.
In the admin:
myButton.addEventListener(MouseEvent.CLICK, handleClickButton);
function handleClickButton(event:MouseEvent):void
{
doSomeAction();
sendTriggerToClient(MyActions.SOME_TRIGGER);
}
In the client:
var actionMap:Object = {};
actionMap[MyActions.SOME_TRIGGER] = doSomeAction;
function receiveTriggerFromAdmin(trigger:String):void
{
var responseFunc:Function = actionMap[trigger];
responseFunc();
}
I hope that pseudo-code makes sense. Just abstract what happens as a result of a click into a separate function (doSomeAction) and then have the admin send a message before calling that function. In the client wait for any trigger that comes through and map it to the same function.