I'm trying to create a global function where I can use it anywhere inside a .js file.
We have more than 50 javascript files joined together and inside each files I want to be able to use this library anywhere.
Localized.js
(function (root, factory) {
if (typeof define === 'function' && define.amd) {
// AMD. Register as an anonymous module.
define(factory);
} else if (typeof exports === 'object') {
// Node. Does not work with strict CommonJS, but
// only CommonJS-like enviroments that support module.exports,
// like Node.
module.exports = factory();
} else {
// Browser globals (root is window)
if (typeof Localized !== 'undefined') {
throw 'Localized" already in use';
}
root.Localized = factory();
}
}(this, function () {
var _strings,
_readyCallback,
_isReady = false,
_requestedStrings = false;
function ready( data ) {
_readyCallback = _readyCallback || function(){};
function domReady() {
// If the DOM isn't ready yet, repeat when it is
if ( document.readyState !== "complete" ) {
document.onreadystatechange = domReady;
return;
}
document.onreadystatechange = null;
_strings = data;
_isReady = true;
_readyCallback();
}
domReady();
}
// Get the current lang from the document's HTML element, which the
// server set when the page was first rendered. This saves us having
// to pass extra locale info around on the URL.
function getCurrentLang() {
var html = document.querySelector( "html" );
return html && html.lang ? html.lang : "en-US";
}
var Localized = {
get: function( key ) {
if ( !_strings ) {
console.error( "[goggles.webmaker.org] Error: string catalog not found." );
return "";
}
return ( _strings[ key ] || "" );
},
getCurrentLang: getCurrentLang,
// Localized strings are ready
ready: function( cb ) {
if ( !_requestedStrings ) {
_requestedStrings = true;
_readyCallback = cb;
function onload( data ) {
ready( data );
}
onload.error = console.log;
var xhr = new XMLHttpRequest();
xhr.open('GET', '/strings/' + getCurrentLang() + '?bust=' + Date.now(), false);
xhr.send(null);
if (xhr.status !== 200) {
err = new Error(id + ' HTTP status: ' + status);
err.xhr = xhr;
onload.error(err);
return;
}
onload(JSON.parse(xhr.responseText));
};
if ( _isReady ) {
_readyCallback();
}
},
isReady: function() {
return !!_isReady;
}
};
return Localized;
}));
So I want to be able to go into any of the 50 files and do Localized.get("something");
But then I don't even have the Localized object available in the web console. For example if you have jQuery you can do $ in the web console and you can do anything there.
Have you ever looked at the Three.js global function? It's super easy to understand!
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
typeof define === 'function' && define.amd ? define(['exports'], factory) :
(factory((global.THREE = global.THREE || {})));
}(this, (function (exports) { 'use strict';
So it turns out that my javascript is globally defined and accessible everywhere within the file that is included and it can be call from the console as well except I have to initialize that by doing Localized.ready(function(){}); then I can get it to work.
So if anyone is looking to create their own global function and make it standard they can follow this way.
amdWeb.js is what I use as a standard to create global function.
Related
I'm now in the process of transforming several functions into prototypes and I'm stuck on callbacks.
Below is a minimal example of what I want to achieve:
WebSocketClient.prototype.send = function(t, data)
{
this.ws.send(data);
this.ws.onmessage = function(evt)
{
var msg = evt.data;
var jsonData = JSON.parse(msg);
if(jsonData["callback"] !== 'undefined' && jsonData["callback"] !== "") // jsonData = {callback:"on_test", data:[0,1,2]}
{
// How to transform callback into call ???
var fn = window[jsonData["callback"]]; // == undefined
if(typeof fn === 'function')
fn(jsonData["data"]);
}
};
};
function Test()
{
this.wc = new WebsocketClient();
// here ws.connect, etc.
}
Test.prototype.send = function()
{
this.wc.send(test, '{request:"get_data", callback:"on_test"')
}
Test.prototype.on_test = function(arr)
{
// ...
}
var test = new Test();
test.send();
I want to make a call to t.callback(data) but can't figure out how to do this?
I tried:
window[jsonData["callback"]]; // == undefined
window['Test.prototype.' + jsonData["callback"]]; // == undefined
window['Test.' + jsonData["callback"]]; // == undefined
There must be an error here:
Test.prototype.send = function()
{
// use 'this' instead of 'test'
// this.wc.send(test, '{request:"get_data", callback:"on_test"')
this.wc.send(this, '{request:"get_data", callback:"on_test"')
}
And since on_test() is defined on Test.prototype, call it this way:
WebSocketClient.prototype.send = function(t, data)
{
this.ws.send(data);
this.ws.onmessage = function(evt)
{
var msg = evt.data;
var jsonData = JSON.parse(msg);
if(jsonData["callback"] !== 'undefined' && jsonData["callback"] !== "") // jsonData = {callback:"on_test", data:[0,1,2]}
{
var fn = t[jsonData["callback"]]; // t will be available in this scope, because you've created a closure
if(typeof fn === 'function') {
fn(jsonData["data"]);
// OR, preserving scope of Test class instance t
fn.call(t, jsonData["data"]);
}
}
};
};
UPDATE: And be wary, that by calling fn(jsonData["data"]); you are loosing the original scope of the method. This way, this inside the on_test() method will point to global scope. If this is undesired, use call() (see corrected above).
If your function is in the global scope you could use
window.call(this, 'functionName', arguments)
In your case,
window.call(this, jsonData['callback'], jsonData['data'])
By doing that, the callback will be invoked with jsonData['data'] as a parameter.
If the function is within an object test, just use
test.call(this, 'on_test', jsonData['data'])
I am developing a site using a third-party CMS and I have to include functions across various parts of the content depending on which page is being displayed. To reduce the amount of functions being called on each page load, I would like to loop through an array of functions to check if they exist before firing them.
This single function would then be called at body onload.
I have adapted code from Javascript Array of Functions and How to implement an array of functions in Javascript? as well as isFunction.
My understanding was that I could put the functions in an array without () and they would not be called but in my console in Chrome an Uncaught Reference error is generated on the line in the array where a function name is mentioned.
e.g. the jb_underimage_height function is not in the code on all pages, so this generates the error when it does not exist.
Here is the code so far:
function jb_onloads() {
var functionArray = [
jb_category_price_POA,
jb_highlight_tech_columns,
jb_underimage_height,
jb_moveGuestButton,
jb_loginCheck,
jb_moveRefineSpan,
jb_style_from_url,
materials_dropdown,
jb_remove_search_spaces,
jb_init_social,
checkCookies,
jb_category_change_class,
jb_move_basket_text,
jb_style_form,
jb_checkNotifyEnvelope
]; // end of functionArray
$.each(functionArray, function(key, value) {
if(typeof functionArray[key] !== 'undefined' && typeof functionArray[key] === "function") { functionArray[key](); }
});
} // end of jb_onloads
And this was my workaround when I had to this.
function a() { alert ("I am a") };
function b() { alert ("I am b") };
var arr = [
typeof a === "function" && a || 0,
typeof b === "function" && b || 0,
typeof c === "function" && c || 0
];
arr.forEach(function(func) {
if(typeof func === "function") {
func();
}
});
maybe we can do it as:
1 function defining:
if (typeof myFuncCollections == "undefined") // window.myFuncCollections
myFuncCollections = {};
myFuncCollections.func1 = function func1() {
console.log("func1");
};
//or
myFuncCollections['funcname'] = function funcname() {
console.log("funcname");
}
....
2 jb_onloads()
function jb_onloads() {
if (typeof myFuncCollections == "undefined")
myFuncCollections = {};
$.each(myFuncCollections, function(i) {
myFuncCollections[i]();
});
}
3 call jb_onloads() after including 1 and 2. And That do not require inlcuding 1-script before 2-script. Also, your can use any function in 1-script outside jb_onloads after including 1-script.
Since using Global value, please use special prefix for naming your "myFuncCollections"
You are trying to insert function references to the array. But if the function is not defined then that name does not exists and thus the error.
Add them as strings
function jb_onloads() {
var functionArray = [
'jb_category_price_POA',
'jb_highlight_tech_columns',
'jb_underimage_height',
'jb_moveGuestButton',
'jb_loginCheck',
'jb_moveRefineSpan',
'jb_style_from_url',
'materials_dropdown',
'jb_remove_search_spaces',
'jb_init_social',
'checkCookies',
'jb_category_change_class',
'jb_move_basket_text',
'jb_style_form',
'jb_checkNotifyEnvelope'
]; // end of functionArray
$.each(functionArray, function(index, functionName) {
// assuming functions are in the global scope
var func = window[ functionName ],
funcType = typeof func;
if (funcType === "function") {
func();
}
});
} // end of jb_onloads
I would like to create my modules in a way, that they can be used with and without requirejs (without require js they should just work normally, so I have to make sure they load correctly, like putting script tags in the right order).
So jQuery does it kindof like this:
// export module
if ( typeof define === "function" && define.amd ) {
define(["dep"], function(dep){
dep.fn.test = test;
return dep;
});
}
else{
dep.fn.test = test;
}
The actual module is defined as like so
var dep = function(...){...}
This definition and the export part is within an IIFE to keep everything in out of the global scope.
Generally it works well with one exception, the dependency is not available.
This problem can be solved by defining the function within the define part, but this would mean defining it twice, in the define part and below in the else part.
How can I get this to work but only define the module once?
I have "plugin-like" extensions to a core dep which should all be in separate files so the main dep must be passed as a depenency
This works fine btw. but it would mean I write the code for test twice.
(function(){
// export module
if ( typeof define === "function" && define.amd ) {
define(["dep"], function(dep){
dep.fn.test = function(){...ssomething using dep...};
return dep;
});
}
else{
dep.fn.test = unction(){...ssomething using dep...};
}
})
Okay, I try another example
animate/animate.js (this is my main file)
define(function(){
...
return animate;
});
animate/modules/easing.js (this is a module file)
(function(){
var ease = function(){
// using animate main function from animate.js
// animate is not available here
...
};
if ( typeof define === "function" && define.amd ) {
define(["animate/animate"], function(animate){
// animate is available here
...
animate.fn.ease = ease;
return animate;
});
}
else
{
// if no requirejs, animate is global
animate.fn.ease = ease;
}
});
I think you're just writing the define incorrectly and so it's not getting registered. This is what I use.
if (typeof define === "function" && define.amd) {
define("telegraph", [], function () { return telegraph; });
}
Put in context
(function(window) {
var telegraph = function() { };
telegraph.prototype.test = function() {
// do something
};
if (typeof define === "function" && define.amd) {
define("telegraph", [], function () { return telegraph; });
}
window.telegraph = telegraph;
})(window);
EDIT
Do the question is really how do you define test and make use of dep internally so that you don't have to supply it as a dependency and can define a named dep module. One solution is to register the second-level functions in the constructor and capture this as self (or another variable) to use within the function. The key thing here is that you use define to define the named module and by using the captured context in the constructor, you don't need to supply the parent object as a dependency. Example (with working fiddle at http://jsfiddle.net/YS8v6/):
(function(){
var dep = function() {
var self = this;
self.fn.test = function() {
self.foo();
};
};
dep.prototype.foo = function() {
alert('foo');
};
dep.prototype.fn = function() {
};
if ( typeof define === "function" && define.amd ) {
define('dep', [], function() { return dep; });
}
})();
The actual problem seems to be that define is not available within the IIFE but window.define is. So passing define as an argument to the IIFE solves the problem.
(function(define){
// export module
if ( typeof define === "function" && define.amd ) {
define(["dep"], function(dep){
dep.fn.test = function(){...ssomething using dep...};
return dep;
});
}
else{
dep.fn.test = unction(){...ssomething using dep...};
}
}(window.define))
Before it would check for define, not find it and immediately try to attache it to dep.fn.test without the requirejs define part.
I'm trying to understand the different ways to create objects and methods in javascript. I've read a lot of articles, blogs and stackoverflow questions and I think I get the notion in general. But I've encountered a small javascript library (written in coffeescript) and the the way it creates objects and methods confused me a little.
I've included a snippet but if you want you can find the complete script at instafeed.js.
Code:
(function() {
var Instafeed, root;
Instafeed = (function() {
function Instafeed(params) {
var option, value;
this.options = {
target: 'instafeed',
get: 'popular',
resolution: 'thumbnail',
sortBy: 'most-recent',
links: true,
limit: 15,
mock: false
};
if (typeof params === 'object') {
for (option in params) {
value = params[option];
this.options[option] = value;
}
}
}
Instafeed.prototype.run = function() {
var header, instanceName, script;
if (typeof this.options.clientId !== 'string') {
if (typeof this.options.accessToken !== 'string') {
throw new Error("Missing clientId or accessToken.");
}
}
if (typeof this.options.accessToken !== 'string') {
if (typeof this.options.clientId !== 'string') {
throw new Error("Missing clientId or accessToken.");
}
}
if ((this.options.before != null) && typeof this.options.before === 'function') {
this.options.before.call(this);
}
if (typeof document !== "undefined" && document !== null) {
script = document.createElement('script');
script.id = 'instafeed-fetcher';
script.src = this._buildUrl();
header = document.getElementsByTagName('head');
header[0].appendChild(script);
instanceName = "instafeedCache" + this.unique;
window[instanceName] = new Instafeed(this.options);
window[instanceName].unique = this.unique;
}
return true;
}
...
return Instafeed;
})();
root = typeof exports !== "undefined" && exports !== null ? exports : window;
root.Instafeed = Instafeed;
}).call(this);
I'm having difficulties understanding the following:
Why did the author prefer to wrap everything with (function(){...}).call(this);? Maybe to avoid creating global variables?
What purpose does the .call(this) part at the very end of the script serve?
Why did the author create the root variable and what are the following lines for?
root = typeof exports !== "undefined" && exports !== null ? exports : window;
root.Instafeed = Instafeed;
Since this the preferred way to create objects and methods in coffeescript I suppose this is one of the better ways to do it. But its advantages over the following version escapes me:
function Instafeed(params) {
...
}
Instafeed.prototype.run = function() {
...
}
Yes; this makes all formerly top-level vars into local variables.
It makes this equal to the global object inside the function
It lets it work as a CommonJS module (for Node.js or Browserify)
I am trying to set a custom error handler for 3rd party plugins/modules in my core library, but somehow, myHandler does not alert the e.message.
Can somebody help me please? thank you
Function.prototype.setErrorHandler = function(f) {
if (!f) {
throw new Error('No function provided.');
}
var that = this;
var g = function() {
try {
var a = [];
for(var i=0; i<arguments.length; i++) {
a.push(arguments[i]);
}
that.apply(null,a);
}
catch(e) {
return f(e);
}
};
g.old = this;
return g;
};
function myHandler(e) {
alert(e.message)
};
// my Core library object
(function(){
if (typeof window.Core === 'undefined') {
var Core = window.Core = function() {
this.addPlugin = function(namespace, obj){
if (typeof this[namespace] === 'undefined') {
if (typeof obj === 'function') {
obj.setErrorHandler(myHandler);
} else if (!!obj && typeof obj === 'object') {
for (var o in obj) {
if (obj.hasOwnProperty(o) && typeof obj[o] === 'function') {
obj[o].setErrorHandler(myHandler);
}
}
}
this[namespace] = obj;
return true;
} else {
alert("The namespace '" + namespace + "' is already taken...");
//return false;
}
};
};
window.Core = new Core();
}
})();
// test plugin
(function(){
var myPlugin = {
init: function() {},
conf: function() {
return this.foo.x; // error here
}
};
Core.addPlugin("myPlugin", myPlugin);
})();
// test
Core.myPlugin.conf(); // supposed to alert(e.message) from myHandler()
setErrorHandler in the above code doesn't set an error handler on a Function, as such. JavaScript does not give you the ability to change the called code inside a Function object.
Instead it makes a wrapped version of the function it's called on, and returns it.
obj.setErrorHandler(myHandler);
Can't work as the returned wrapper function is thrown away, not assigned to anything.
You could say:
obj[o]= obj[o].setErrorHandler(myHandler);
though I'm a bit worried about the consequences of swapping out functions with different, wrapped versions. That won't necessarily work for all cases and could certainly confuse third-party code. At the least, you'd want to ensure you don't wrap functions twice, and also retain the call-time this value in the wrapper:
that.apply(this, a);
(Note: you don't need the manual conversion of arguments to an Array. It's valid to pass the arguments object directly to apply.)