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.
Related
I'm using a run-time assignment of functions to account for browser differences. However for un-supported browsers, I want to return an empty function so that a JavaScript error is not thrown.
But, jslint complains about empty functions. What is the jslint happy way to do this?
Empty block.
$R.functionNull = function () {
// events not supported;
};
$R.Constructor.prototype.createEvent = (function () {
if (doc.createEvent) {
return function (type) {
var event = doc.createEvent("HTMLEvents");
event.initEvent(type, true, false);
$NS.eachKey(this, function (val) {
val.dispatchEvent(event);
});
};
}
if (doc.createEventObject) {
return function (type) {
var event = doc.createEventObject();
event.eventType = type;
$NS.eachKey(this, function (val) {
val.fireEvent('on' + type, event);
});
};
}
return $R.functionNull;
}());
You can add a body to your function and have it return undefined:
$R.functionNull = function() {
// Events not supported.
return undefined;
};
This keeps the same semantics as a "truly empty" function, and should satisfy JSLint.
Use the lambda expression:
$R.functionNull = () => void 0;
For me this works best:
emptyFunction = Function();
console.log(emptyFunction); // logs 'ƒ anonymous() {}'
console.log(emptyFunction()); // logs 'undefined'
It's so short that I wouldn't even assign it to a variable (of course you can also use a constant-like variable "EF" or so, that's even shorter and doesn't need the additioal "()" brackets). Just use "Function()" anywhere you need a truly empty function, that doesn't even have a name, not even when you assign it to a variable, and that's the small behaviour difference between my solution and Frédéric's:
// --- Frédéric ---
emptyFunction = function() {
return undefined;
}
console.log(emptyFunction.name); // logs '"emptyFunction"'
// --- me ---
emptyFunction = Function();
console.log(emptyFunction.name); // logs '""' (or '"anonymous"' in chrome, to be fair)
What about returning
return () => undefined;
instead of
return $R.functionNull;
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();
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.
This is my code:
request_xml: function()
{
http_request = false;
http_request = new XMLHttpRequest();
if (http_request.overrideMimeType)
{
http_request.overrideMimeType('text/xml');
}
if (!http_request)
{
return false;
}
http_request.onreadystatechange = this.response_xml;
http_request.open('GET', realXmlUrl, true);
http_request.send(null);
xmlDoc = http_request.responseXML;
},
response_xml:function ()
{
if (http_request.readyState == 4)
{
if(http_request.status == 404 && countXmlUrl<=3)
{
countXmlUrl++;
realXmlUrl = xmlUrl[countXmlUrl];
this.request_xml();
}
if (http_request.status == 200)
{
xmlDoc = http_request.responseXML;
alert("need to update3");
this.peter_save_data();
}
}
},
peter_save_data:function()
{
// removed function code
},
Strangely, the alert fires without a problem but the function call underneath gives me this error:
Error: this.peter_save_data is not a function
Calling the same damn function from another function elsewhere works fine.
You could do this, right before you call the XML generation.
var that = this;
and later...
that.peter_save_data();
Because this frequently changes when changing scope by using a new function, you can't access the original value by using it. Aliasing it to that allows you still to access the original value of this.
One important piece of the puzzle that is missing is how response_xml is being called. This is important, because it will change what this is (see Jared's comment).
Remember that this can be thought of as (roughly) "the receiver of the method call". If response_xml is passed directly to use as a callback then of course it won't work -- this will likely be window.
Consider these:
var x = {f: function () { return this }}
var g = x.f
x.f() === x // true
g() === x // false
g() === window // true
Happy coding.
The "fix" is likely just to change how response_xml is being called. There are numerous ways to do this (generally with a closure).
Examples:
// Use a closure to keep he object upon which to explicitly invoke the method
// inside response_xml "this" will be "that",
// which was "this" of the current scope
http_request.onreadystatechange = (function (that) {
return function () { return that.response_xml() }
}(this)
// Or, alternatively,
// capture the current "this" as a closed-over variable...
// (assumes this is in a function: var in global context does not create a lexical)
var self = this
http_request.onreadystatechange = function () {
// ...and invoke the method upon it
return self.response_xml()
}
Personally, I would just use jQuery or similar ;-)
If you want a class-like behavior, use the right syntax, The libraries that use that, are using JSON to pass a parameter to a function that makes a class out of it.
function MyClass(CTOR paarams){
var response_xml=function ()
{
if (http_request.readyState == 4)
{
if(http_request.status == 404 && countXmlUrl<=3)
{
countXmlUrl++;
realXmlUrl = xmlUrl[countXmlUrl];
this.request_xml();
}
if (http_request.status == 200)
{
xmlDoc = http_request.responseXML;
alert("need to update3");
this.peter_save_data();
}
}
}
var peter_save_data=function()
{
// removed function code
}
}
var Test = new MyClass(somthing,another_something);
Test.response_xml();
//etc etc.
Or, use the libraries like Mootools where you can do it as JSON:
var T = new Class({
response_xml:function ()
{
if (http_request.readyState == 4)
{
if(http_request.status == 404 && countXmlUrl<=3)
{
countXmlUrl++;
realXmlUrl = xmlUrl[countXmlUrl];
this.request_xml();
}
if (http_request.status == 200)
{
xmlDoc = http_request.responseXML;
alert("need to update3");
this.peter_save_data();
}
}
},
peter_save_data:function()
{
// removed function code
}
});
var X = new T();//etc etc
I have built a js class that is have the control (html Control ) parameter, I tried to add dynamically an onchange event to the control but I have the following error:
htmlfile: Not implemented
//-------------- the code
Contrl.prototype.AddChangeEvent = function() {
var element = this.docID;
var fn = function onChange(element) {
// action
};
if (this.tag == "input" && (this.el.type == "radio")) {
this.el.onclick = fn(element); // there i have the error
}
else {
this.el.onchange = fn(element); // there i have the error
}
}
By writing this.el.onclick = fn(element), you're calling fn immediately, and assigning whatever fn returns to onclick.
You need to make an anonymous function that calls fn with the arguments you want it to get, like this:
this.el.onclick = function() { return fn(element); };
However, this is not the correct way to assign event handlers in Javascript.
You should call attachEvent (for IE) or addEventListener (for everything else), like this:
function bind(elem, eventName, handler) {
if (elem.addEventListener)
elem.addEventListener(eventName, handler, false);
else if (elem.attachEvent)
elem.attachEvent("on" + eventName, handler);
else
throw Error("Bad browser");
}