Trigger a flash objects function embedded via swfobject's embedSWF? - javascript

I have looked through every resource I could find to create a streamlined way of doing this but I get one of the fundamental errors each time I try. I simply cannot get it to work!
Here is my flash test actionscript from the first keyframe of my movie:
import flash.external.*; //THIS...
System.security.allowDomain("http://localhost", "*"); //...AND THIS ALSO REQUIRED!
if (ExternalInterface.available) {
trace("ExternalInterface= " + ExternalInterface.available);
flash.external.ExternalInterface.addCallback("myExternalMethod", null, myFunction);
}
function myFunction() {
gotoAndStop(2);
}
And here is my javascript/little bit of jQuery:
<script type="text/javascript">
var flashvars = {};
var params = {
wmode: "transparent",
allowscriptaccess: "always"
};
var attributes = { //The addition of these attributes make it work!
id: "number-input",
name: "number-input"
};
var embedHandler = function (e){
mySWF = e.ref;
};
swfobject.embedSWF("images/flash-form.swf", "number-input", "800", "320", "9.0.0", "expressInstall.swf", flashvars, params, attributes, embedHandler);
function executeFlash() {
getObjectById("number-input").myExternalMethod();
}
function getObjectById(objectIdStr) {
var r = null;
var o = document.getElementById(objectIdStr);
if (o && o.nodeName == "OBJECT") {
if (typeof o.SetVariable != undefined) {
r = o;
} else {
var n = o.getElementsByTagName(OBJECT)[0];
if (n) {
r = n;
}
}
}
return r;
}
$(function() {
$('#main-button-big').click(function(event){
event.preventDefault();
executeFlash();
});
});
</script>
The flash seems to embed successfully, if I do something silly like...
$(mySWF).hide();
...it hides, so I'm sure the script can see the object.
No matter what methods I've tried to do I always (testing in firefox) get an error like:
Error: TypeError: mySWF.myExternalMethod is not a function.
In safari:
TypeError: 'undefined' is not a function (evaluating 'mySWF.myExternalMethod()')
If I try the jQuery version of getElementById (circumnavigating the embedHandler) I get:
Error: TypeError: $(...).myExternalMethod is not a function. or TypeError: 'undefined' is not a function (evaluating '$('#number-plate-input').myExternalMethod()')
I need to get this project finished rapidly and am at the end of my tether. Any help would be very gratefully received.
UPDATED: Please note that the embedHandler is no longer needed, nor used to trigger any event of any kind. It is a useful piece of code though, so I've opted to leave it in.

Change the attributes variable in your javascript from an empty object to:
var attributes = {
id: "flash_movie",
name: "flash_movie"
};
And then you should be able to call the exposed function using:
document.getElementById("flash_movie").myExternalMethod();

Related

Get global module variable in vanilla javascript

So i have below module created according to an example i found.
I initialize it with
equipment_booking = new equipment({
equipment: '#equipment_order',
type: '#equipment_type_order',
validation: '#equipment_validation_order',
submit: "#submit_booking"
});
What I want to do is access the typeVar from outside the module. I found out that I need to use the this. in front of it. And indeed I could access it. However I can't seem to access it inside the module functions. How would I accomplish this? This is a part of the module I have written. I have tried different ways in the checkInput function. Any help or suggestions are realy appriciated.
var equipment = (function() {
function equipment(options) {
// Set the standard options
var o = {
equipment: null,
type: null,
validation: null,
submit: null
};
// Get some other needed variables
number = "";
this.typeVar = 0;
var error = [{
code: 0,
msg: ""
}];
// Then we can override the given options to the standard options
for (var k in options) {
if (options.hasOwnProperty(k)) {
o[k] = document.querySelector(options[k]);
}
}
// Add the eventlisteners
o.equipment.addEventListener('keydown', checkInput);
o.equipment.addEventListener('blur', validateInput);
o.type.addEventListener('change', validateInput);
function checkInput(e) {
console.log("CheckInput called"); // This console.log is called
console.log(typeVar); // Error: typeVar is not defined
console.log(this.typeVar); // Undefined
e.preventDefault(); // The e works
}
function validateInput() {
// Validating input
}
}
return equipment;
})();
It seems that you are using some very old vanilla js. I suppose it is to support old browsers. Let's keep that support using a simple technique that it's called "saving this in a helper variable self"
function equipment(options) {
// Set the standard options
var o = {
equipment: null,
type: null,
validation: null,
submit: null
};
// Get some other needed variables
number = "";
this.typeVar = 0;
var error = [{
code: 0,
msg: ""
}];
// Then we can override the given options to the standard options
for (var k in options) {
if (options.hasOwnProperty(k)) {
o[k] = document.querySelector(options[k]);
}
}
// Add the eventlisteners
o.equipment.addEventListener('keydown', checkInput);
o.equipment.addEventListener('blur', validateInput);
o.type.addEventListener('change', validateInput);
var self = this;
function checkInput(e) {
console.log("CheckInput called"); // This console.log is called
console.log(typeVar); // Error: typeVar is not defined
console.log(this.typeVar); // Undefined
console.log(self.typeVar); // Defined
e.preventDefault(); // The e works
}
function validateInput() {
// Validating input
}
}
return equipment;
})();
The reason this works is because when the interpreter creates a new function such as checkInput, it sees all the variables that are around and puts them inside a "backpack". This backpack of variables is available when you execute the function later. Now you may ask: "Why is this not referencing my equipment class? Why is not in the backpack?". Thats because this in that callback is referencing other thing from the event callback. It got "replaced" inside that callback. Thats why you don't have it.
Edit:
A more modern approach would be to use class syntax without changing too much the code, and for the callbacks, we would use class properties as arrow functions which hold the correct this class context when the event listeners call them.
Please, take a careful look at the name "Equipment", now it has a capital "E". The class instantiation should be new Equipment() now.
More info on https://devopedia.org/naming-conventions
class Equipment {
constructor(options) {
// Set the standard options
const o = {
equipment: null,
type: null,
validation: null,
submit: null
};
// Get some other needed variables
let number = "";
this.typeVar = 0;
const error = [{
code: 0,
msg: ""
}];
// Then we can override the given options to the standard options
for (let k in options) {
if (options.hasOwnProperty(k)) {
o[k] = document.querySelector(options[k]);
}
}
// Add the event-listeners
o.equipment.addEventListener('keydown', this.checkInput);
o.equipment.addEventListener('blur', this.validateInput);
o.type.addEventListener('change', this.validateInput);
}
checkInput = (e) => {
console.log("CheckInput called"); // This console.log is called
console.log(this.typeVar); // defined
e.preventDefault(); // The e works
}
validateInput = () => {
// Validating input
}
}

Uncaught TypeError: Cannot read property 'isSynchronized' of undefined

I have a problem with sorting my ExtJS grid. I'm using Ext JS version 5.1.1.451.
The grid loads well but when I try to sort the columns I get the "Uncaught TypeError: Cannot read property 'isSynchronized' of undefined" error.
The method addCls is triggered on mouse movement and when the error occures, me.getData() returns undefined, where me is
constructor
$observableInitialized: true
component: constructor
config: Object
destroy: () {}
dom: null
el: null
events: Object
hasListeners: statics.prepareClass.HasListeners
id: "ext-quicktips-tip"
initConfig: () {}
initialConfig: Object
isDestroyed: true
lastBox: null
managedListeners: Array[0]
shadow: null
This is the addCls method where the error occurs:
addCls: function(names, prefix, suffix) {
var me = this,
elementData = me.getData(),
hasNewCls, dom, map, classList, i, ln, name;
if (!names) {
return me;
}
if (!elementData.isSynchronized) {
me.synchronize();
}
Has anyone encounter this problem before? Is it a known issue or am I doing something wrong?
Thank you
I've overriden the handleMouseDown method and commented out the quick tips part.
Ext.override(Ext.dd.DragDropManager, {
handleMouseDown: function (e, oDD) {
var me = this,
xy, el;
me.isMouseDown = true;
//if (Ext.quickTipsActive) {
// Ext.tip.QuickTipManager.ddDisable();
//}
me.currentPoint = e.getPoint();
if (me.dragCurrent) {
me.handleMouseUp(e);
}
me.mousedownEvent = e;
me.currentTarget = e.getTarget();
me.dragCurrent = oDD;
el = oDD.getEl();
Ext.fly(el).setCapture();
xy = e.getXY();
me.startX = xy[0];
me.startY = xy[1];
me.offsetX = me.offsetY = 0;
me.deltaX = me.startX - el.offsetLeft;
me.deltaY = me.startY - el.offsetTop;
me.dragThreshMet = false;
}
});
If anyone has a better answer, post it and I will mark it if it's good.
I was having this issue because I was rendering a different value based on some mappings I needed to load separately. If the mappings weren't loaded into the viewmodel then the renderer seemed to fail with that error.
I solved it with some checks for existence of what it needed:
// on the renderer for the column where I have a combobox editor field
renderer: function(value) {
var mappings = vm.get('ruleMapping')
if(!!mappings) {
var rule = _.find(mappings, ['id', value]
if(!!rule) {
return `<a href="${rule.link}>${rule.name}</a>"`
}
}
return 'Setting value'
}

JavaScript: Not Calling Constructor Function

Can someone shed some light as to why this doesn't work the way I think it should (or what I'm overlooking).
function Pane(data) {
var state = {
show: function(data) {
var pane = document.querySelector('.pane[data-content='+data.target+']');
pane.classList.add('active');
},
hide: function(data) {
var pane = document.querySelector('.pane[data-content='+data.target+']');
var paneSibling = $(pane.parentNode.childNodes);
paneSibling.each(function(sibling) {
if(check.isElement(sibling)) {
var isActive = sibling.classList.contains('active');
if(sibling != pane && isActive) {
sibling.classList.remove('active');
};
};
});
}
}
return state;
}
So I can console log Pane(arg).show/hide and it'll log it as a function, so why is it when I call Pane(arg).show it doesn't do anything? The functions in the object work (outside of the constructor function in their own functions).
The function is returning the state object, so it will never return the constructed object, even when used with new. Since state contains those methods, you can just call the function and immediately invoke one of the methods on the returned object.
Now, if you're expecting show and hide to automatically have access to data via closure, it's not working because you're shadowing the variable by declaring the method parameters. You can do this instead:
function Pane(data) {
var state = {
show: function() {
var data = data || arguments[0];
var pane = document.querySelector('.pane[data-content='+data.target+']');
pane.classList.add('active');
},
hide: function() {
var data = data || arguments[0];
var pane = document.querySelector('.pane[data-content='+data.target+']');
var paneSibling = $(pane.parentNode.childNodes);
paneSibling.each(function(sibling) {
if(check.isElement(sibling)) {
var isActive = sibling.classList.contains('active');
if(sibling != pane && isActive) {
sibling.classList.remove('active');
};
};
});
}
}
return state;
}
Then you can use it like this:
Pane({}).show();
Or like this:
var p = Pane();
p.show();
Or force a new argument when needed:
p.show({foo:'bar'});
You are overriding the original argument in each function.
So what you are doing is to find elements with the attribute data-content='undefined'
This obviously doesn't work.
So to fix this you should just remove the data argument in the show/hide function.
Here is a plnkr showing the problem and fix.

Object [object global] has no method 'attachEvent'

I have a WebForms page which is including MicrosoftAjax.debug.js (4.1.7.123) as a script resource:
// Name: MicrosoftAjax.debug.js
// Assembly: AjaxControlToolkit
// Version: 4.1.7.123
// FileVersion: 4.1.7.0123
// (c) 2010 CodePlex Foundation
On load this script self invokes, eventually calling this function:
var attachEvent = !!document.attachEvent;
...
function listenOnce(target, name, ieName, callback, isReadyState, isScript) {
function onEvent() {
if (!attachEvent || !isReadyState || /loaded|complete/.test(target.readyState)) {
if (attachEvent) {
target.detachEvent(ieName || ("on" + name), onEvent);
}
else {
target.removeEventListener(name, onEvent, false);
if (isScript) {
target.removeEventListener("error", onEvent, false);
}
}
callback.apply(target);
target = null;
}
}
if (attachEvent) {
target.attachEvent(ieName || ("on" + name), onEvent);
}
else {
if (target.addEventListener) {
target.addEventListener(name, onEvent, false);
}
if (isScript) {
target.addEventListener("error", onEvent, false);
}
}
}
The problem is that in Chrome I'm getting the following Javascript error:
Uncaught TypeError: Object [object global] has no method 'attachEvent'
On the following line:
target.attachEvent(ieName || ("on" + name), onEvent);
Attaching the debugger, target is the window object, which as you'd expect does not have the attachEvent() method in Chrome.
document.attachEvent() is the following function:
function (sType, fHandler) {
var shortTypeName = sType.replace(/on/, "");
fHandler._ieEmuEventHandler = function (e) {
window.event = e;
return fHandler();
};
this.addEventListener(shortTypeName, fHandler._ieEmuEventHandler, false);
}
Is this a bug in the Microsoft Ajax script? Chrome? Or is it being caused by some condition on the page?
Either way, how can I resolve it?
You shouldn't reassign document.attachEvent to begin with, so you may want to get rid of that. attachEvent is true because of that. That doesn't mean that target.attachEvent exists, though. It seems like you should check if (!!target.attachEvent) before calling it on target instead of just looking at your attachEvent variable.
I'll leave this question up in case anyone else runs into the same problem. However the error was being caused by a legacy Javascript library reassigning the document.attachEvent() method.
This was the offending code:
function emulateAttachEvent() {
HTMLDocument.prototype.attachEvent =
HTMLElement.prototype.attachEvent = function (sType, fHandler) {
var shortTypeName = sType.replace(/on/, "");
fHandler._ieEmuEventHandler = function (e) {
window.event = e;
return fHandler();
};
this.addEventListener(shortTypeName, fHandler._ieEmuEventHandler, false);
};
HTMLDocument.prototype.detachEvent =
HTMLElement.prototype.detachEvent = function (sType, fHandler) {
var shortTypeName = sType.replace(/on/, "");
if (typeof fHandler._ieEmuEventHandler == "function")
this.removeEventListener(shortTypeName, fHandler._ieEmuEventHandler, false);
else
this.removeEventListener(shortTypeName, fHandler, true);
};
}
Fortunately, I was able to remove the legacy library. However this won't help if you have a genuine case for reassigning the document.attachEvent() method, in which case you will need to come up with an alternative solution.

How to proxying functions on a function in javascript?

I am newbie about javascript.So I do not know what is the name of I looking for and How do I do it?
After you read question if you thing question title is wrong, you should change title.
I am using console.log for debugging but this is causing error if browser IE. I made below function for this problem.
var mylog=function(){
if (devmode && window.console){
console.log(arguments);
}
};
mylog("debugging");
Now I want to use all console functions without error and I can do that as below.
var myconsole={
log:function(){
if (devmode && window.console){
console.log(arguments);
}
}
,error:function(){
if (devmode && window.console){
console.error(arguments);
}
}
...
...
...
};
But I do not want to add all console functions to myconsole object severally.
I can do that in PHP with below code.
class MyConsole
{
function __call($func,$args)
{
if ($devmode && function_exists('Console')){
Console::$func($args); // Suppose that there is Console class.
}
}
}
MyConsole::warn("name",$name);
MyConsole::error("lastname",$lastname);
This is possible with __noSuchMethod__ method but this is specific to only firefox.
Thanks for helping.
Unfortunately, you can't do that in JavaScript, the language doesn't have support for the "no such method" concept.
Two options for you:
Option 1:
Use strings for your method name, e.g.:
function myconsole(method) {
var args;
if (devmode && window.console) {
args = Array.prototype.slice.apply(arguments, 1);
window.console[method].apply(window.console, args);
}
}
Usage:
myconsole("log", "message");
myconsole("error", "errormessage");
The meat of myconsole is here:
args = Array.prototype.slice.apply(arguments, 1);
window.console[method].apply(window.console, args);
The first line copies all of the arguments supplied to myconsole except the first one (which is the name of the method we want to use). The second line retrieves the function object for the property named by the string in method from the console object and then calls it via the JavaScript apply function, giving it those arguments.
Option 2:
A second alternative came to me which is best expressed directly in code:
var myconsole = (function() {
var methods = "log debug info warn error assert clear dir dirxml trace group groupCollapsed groupEnd time timeEnd profile profileEnd count exception table".split(' '),
index,
myconsole = {},
realconsole = window.console;
for (index = 0; index < methods.length; ++index) {
proxy(methods[index]);
}
function proxy(method) {
if (!devmode || !realconsole || typeof realconsole[method] !== 'function') {
myconsole[method] = noop;
}
else {
myconsole[method] = function() {
return realconsole[method].apply(realconsole, arguments);
};
}
}
function noop() {
}
return myconsole;
})();
Then you just call log, warn, etc., on myconsole as normal.

Categories

Resources