How can I make functions common between assets/js and static/js? - hugo

I’m working on AMP project. I have an authorization callback that is defined in static/js (for some unknown reason, the callback should be defined in static/js). How I can make the function defined in assets/js to be visible in static/js?
My main js is included in HTML before that static/js and use this construction
{{ $js := resources.Get "js/custom.js" | js.Build }}
<script src="{{ $js.Permalink }}"></script>

Pardon mate, there is no reason to have it in static/js if you need it in assets/js.
But if that is some requirement beyond your control - symlink/mount it.
https://github.com/gohugoio/hugo/issues/5374
Honestly though, without a site repo, access to your codebase, etc. etc. Or a clear question, it's hard to give you more than that.
But, the actual answer to your question is:
https://github.com/gohugoio/hugo/issues/5374

If you define some functions in custom.js with a global scope, they should be visible in the static JS files.
MyObject = {
function1: function(...) {...},
function2: function(...) {...}
// other functions...
}
And then, they can be called from the other file with:
MyObject.function1();
Now, it might be possible that the file is not ready to be called, or that from your custom JS file you need to have the other static JS file loaded first. In that case, you can "wait" until everything is fully loaded.
MyObject = {
function1: function(...) {...},
function2: function(...) {...}
// other functions...
}
window.addEventListener('load', (event) => {
// Page is fully loaded.
MyObject.function1();
});
If this doesn't work, we'd need to understand a bit better the code that's inside static to see how you could achieve this.

Related

Require.js fails when including socket.io [duplicate]

I'm getting this error when I browse my webapp for the first time (usually in a browser with disabled cache).
Error: Mismatched anonymous define() module: function (require) {
HTML:
<html>
.
.
.
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.8.0/jquery.min.js"></script>
<script> var require = { urlArgs: "v=0.4.1.32" }; </script>
<script data-main="assets/js/main" src="assets/js/libs/require.js"></script>
<script src="assets/js/ace/ace.js?v=0.4.1.32"></script>
</body>
</html>
JS:
$(function () {
define(function (require) {
// do something
});
});
Anyone know exactly what this error means and why its happening?
source file, a short discussion about it in the github issues page
Like AlienWebguy said, per the docs, require.js can blow up if
You have an anonymous define ("modules that call define() with no string ID") in its own script tag (I assume actually they mean anywhere in global scope)
You have modules that have conflicting names
You use loader plugins or anonymous modules but don't use require.js's optimizer to bundle them
I had this problem while including bundles built with browserify alongside require.js modules. The solution was to either:
A. load the non-require.js standalone bundles in script tags before require.js is loaded, or
B. load them using require.js (instead of a script tag)
In getting started with require.js I ran into the issue and as a beginner the docs may as well been written in greek.
The issue I ran into was that most of the beginner examples use "anonymous defines" when you should be using a "string id".
anonymous defines
define(function() {
return { helloWorld: function() { console.log('hello world!') } };
})
define(function() {
return { helloWorld2: function() { console.log('hello world again!') } };
})
define with string id
define('moduleOne',function() {
return { helloWorld: function() { console.log('hello world!') } };
})
define('moduleTwo', function() {
return { helloWorld2: function() { console.log('hello world again!') } };
})
When you use define with a string id then you will avoid this error when you try to use the modules like so:
require([ "moduleOne", "moduleTwo" ], function(moduleOne, moduleTwo) {
moduleOne.helloWorld();
moduleTwo.helloWorld2();
});
I had this error because I included the requirejs file along with other librairies included directly in a script tag. Those librairies (like lodash) used a define function that was conflicting with require's define. The requirejs file was loading asynchronously so I suspect that the require's define was defined after the other libraries define, hence the conflict.
To get rid of the error, include all your other js files by using requirejs.
Per the docs:
If you manually code a script tag in HTML to load a script with an
anonymous define() call, this error can occur.
Also seen if you
manually code a script tag in HTML to load a script that has a few
named modules, but then try to load an anonymous module that ends up
having the same name as one of the named modules in the script loaded
by the manually coded script tag.
Finally, if you use the loader
plugins or anonymous modules (modules that call define() with no
string ID) but do not use the RequireJS optimizer to combine files
together, this error can occur. The optimizer knows how to name
anonymous modules correctly so that they can be combined with other
modules in an optimized file.
To avoid the error:
Be sure to load all scripts that call define() via the RequireJS API.
Do not manually code script tags in HTML to load scripts that have
define() calls in them.
If you manually code an HTML script tag, be
sure it only includes named modules, and that an anonymous module that
will have the same name as one of the modules in that file is not
loaded.
If the problem is the use of loader plugins or anonymous
modules but the RequireJS optimizer is not used for file bundling, use
the RequireJS optimizer.
The existing answers explain the problem well but if including your script files using or before requireJS is not an easy option due to legacy code a slightly hacky workaround is to remove require from the window scope before your script tag and then reinstate it afterwords. In our project this is wrapped behind a server-side function call but effectively the browser sees the following:
<script>
window.__define = window.define;
window.__require = window.require;
window.define = undefined;
window.require = undefined;
</script>
<script src="your-script-file.js"></script>
<script>
window.define = window.__define;
window.require = window.__require;
window.__define = undefined;
window.__require = undefined;
</script>
Not the neatest but seems to work and has saved a lot of refractoring.
Be aware that some browser extensions can add code to the pages.
In my case I had an "Emmet in all textareas" plugin that messed up with my requireJs.
Make sure that no extra code is beign added to your document by inspecting it in the browser.
Or you can use this approach.
Add require.js in your code base
then load your script through that code
<script data-main="js/app.js" src="js/require.js"></script>
What it will do it will load your script after loading require.js.
I was also seeing the same error on browser console for a project based out of require.js. As stated under MISMATCHED ANONYMOUS DEFINE() MODULES at https://requirejs.org/docs/errors.html, this error has multiple causes, the interesting one in my case being: If the problem is the use of loader plugins or anonymous modules but the RequireJS optimizer is not used for file bundling, use the RequireJS optimizer. As it turns out, Google Closure compiler was getting used to merge/minify the Javascript code during build. Solution was to remove the Google closure compiler, and instead use require.js's optimizer (r.js) to merge the js files.

TypeError: window.initMap is not a function

I am following this tutorial, basically copy all the code
https://developers.google.com/maps/documentation/javascript/tutorial
but got an error saying that the initMap function is not a function.
I am using angularjs in my project, could that be causing problems?
I copied the same code into plunker and it works just fine...
What are the possible issues?
Actually the error is being generated by the initMap in the Google's api script
<script async defer
src="https://maps.googleapis.com/maps/api/js?key=YOUR_API_KEY&callback=initMap">
</script>
so basically when the Google Map API is loaded then the initMap function is executed.
If you don't have an initMap function, then the initMap is not a function error is generated.
So basically what you have to do is one of the following options:
to create an initMap function
replace the callback function with one of your own that created for the same purpose but named it something else
replace the &callback=angular.noop if you just want an empty function() (only if you use angular)
My explanation of this problem:
The .....&callback=initMap" async defer></script> makes that script load async (in parallel with DOM) and after load - execute initMap function. NOTICE that initMap() in global scope! We can't declare our initMap() after google's script because it should already be at the moment of finish async load. Although we can't load our script before this, because we need google's function to execute ours. This is like vicious circle.
Three solutions:
1'st and worst: make google's script load synchronously
remove async defer
remove &callback=initMap in src attribute
put <script tag with your code after google's script
2'nd and best: just do this =)
leave .....&callback=initMap" async defer></script> as is
and put google's <script tag after your script
write in your script
function initMap() {} // now it IS a function and it is in global
$(() => {
initMap = function() {
// your code like...
var map = new google.maps.Map(document.getElementById('map'), {/*your code*/});
// and other stuff...
}
})
this allow you load google's script async and run yours just after that
3'rd and strange: it will work... some times =)
do same but simply write in global scope
function initMap() {
// your code
}
and if you write it in global scope, that will work regardless of what
which code loads faster, your (sync) or google's (async). More often your wins
I have been struggling for several days with this very popular in the last few months issue - "initMap is not a function".
Those two threads helped me:
How to make a callback to Google Maps init in separate files of a web app
Defer attribute doesn't work with Google Maps API?
Why does the map open sometimes and sometimes not. It depends on several factors like speed of connection, environment, etc. Because the initialization function sometimes runs after the google maps API kicks in, that's why the map is not displayed and the browser console throws an error. For me removing only the async attribute fixed the issue. The defer attribute stays.
If async is present: The script is executed asynchronously with the rest of the page (the script will be executed while the page continues the parsing)
If async is not present and defer is present: The script is executed when the page has finished parsing
If neither async or defer is present: The script is fetched and executed immediately, before the browser continues parsing the page
Source - http://www.w3schools.com/tags/att_script_defer.asp
Hope that helps. Cheers.
Removing =initMap worked for me:
<script async defer src="https://maps.googleapis.com/maps/api/js?key=YOUR_API_KEY&callback"></script>
The problem has to do with the async attribute in the script tag. The Callback Function is trying to call "initMap()" when it doesn't really exists by the time the request finished.
To solve this I placed the Goole Maps Api Script bellow the script where my initMap function was declared.
Hope this helps
This may seem obvious but just in case: If someone placed the JS code inside $(document).ready like this:
$(document).ready(function() {
... Google Maps JS code ...
}
Then that's the problem because using async defer when loading the Google Maps API library, it will load asynchronously, and when it finishes loading, will look for the callback function, which needs to be available by then.
So, you just need to put the code outside $(document).ready, and:
<script src="https://maps.googleapis.com/maps/api/js?key=YOUR_API_KEY&callback=initMap"
async defer></script>
at the very bottom, so your page loads FAST :-)
Solved by adding
<script async defer
src="https://maps.googleapis.com/maps/api/js?key=XXXXXXX&callback=initMap">
<!--
https://developers.google.com/maps/documentation/javascript/examples/map-geolocation
-->
</script>
At the beginning of the same file which contains the rest of the code with function initMap(). It's definitely not the best solution, but it works..
But I think that if you would transform function initMap() to something like var=initMap() and then $(function () ... it would work too.
I am using React and I had mentioned &callback=initMap in the script as below
<script
src="https://maps.googleapis.com/maps/api/js?
key=YOUR_API_KEY&callback=initMap">
</script>
then is just removed the &callback=initMap part now there is no such error as window.initMap is not a function in the console.
your call back method probably is not globally accessible. in my case I'd used transpoiled ES6 codes via webpack which caused my callback method not being global anymore.
Try to attach your callback method explicitly to window like so right after your callback method declaration and see the result
window.initMap = initMap;
it worked for me.
Create initMap method between "" tag or load javascript file before call google api.
<script src="Scripts/main.js"></script>
<script src="https://maps.googleapis.com/maps/api/js?key=abcde&libraries=places&callback=initMap" async defer></script>
For me the main difference was the declaration of the function....
INSTEAD OF
function initMap() {
...
}
THIS WORKED
window.initMap = function () {
...
}
Put this in your html body (taken from the official angular.js website):
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.5/angular.min.js"></script>
I believe that you were using some older angular.js files since you don't have any issues in plunker and therefore you got the specified error.
turns out it has to do with ng-Route and the order of loading script
wrote a directive and put the API script on top of everything works.
I had a similar error. The answers here helped me figure out what to do.
index.html
<!--The div element for the map -->
<div id="map"></div>
<!--The link to external javascript file that has initMap() function-->
<script src="main.js">
<!--Google api, this calls initMap() function-->
<script async defer src="https://maps.googleapis.com/maps/api/js?key=YOUR_API_KEYWY&callback=initMap">
</script>
main.js
// This gives error
// The initMap function has not been executed
const initMap = () => {
const mapDisplayElement = document.getElementById('map');
// The address is Uluru
const address = {lat: -25.344, lng: 131.036};
// The zoom property specifies the zoom level for the map. Zoom: 0 is the lowest zoom,and displays the entire earth.
const map = new google.maps.Map(mapDisplayElement, { zoom: 4, center: address });
const marker = new google.maps.Marker({ position: address, map });
};
The answers here helped me figure out a solution.
I used an immediately invoked the function (IIFE ) to work around it.
The error is as at the time of calling the google maps api the initMap() function has not executed.
main.js
// This works
const mapDisplayElement = document.getElementById('map');
// The address is Uluru
// Run the initMap() function imidiately,
(initMap = () => {
const address = {lat: -25.344, lng: 131.036};
// The zoom property specifies the zoom level for the map. Zoom: 0 is the lowest zoom,and displays the entire earth.
const map = new google.maps.Map(mapDisplayElement, { zoom: 4, center: address });
const marker = new google.maps.Marker({ position: address, map });
})();
What actually worked for me was:
In the HTML file, I removed the async attribute from the <script> tag.
In the JavaScript file, I double-checked that the function was outside the $(document).ready function.
Could be your initMap function is in a $(document).ready function. If it is then it won't work, it has to be outside of any other functions.
In addition to #DB.Null's answer, I used Function.prototype as no-op (no-operation) function on #3 instead of angular.noop (I don't have angular in my project).
So this...
<script async defer src="https://maps.googleapis.com/maps/api/js?key=API_KEY_HERE&callback=Function.prototype" type="text/javascript"></script>
In my case there was a formatting issue earlier on, so the error was a consequence of something else. My server was rendering the lat/lon values with commas instead of periods, because of different regional settings.
I removed the callback of "initMap" from the script tag and placed the callback in my JavaScript file.
<script async src="https://maps.googleapis.com/maps/api/js?key=YOUR_API_KEYWY&callback=initMap"></script>
was changed to
<script async src="https://maps.googleapis.com/maps/api/js?key=YOUR_API_KEYWY"></script>
So the first JS tag is mine.
<script async type=module src="js/app.js"></script>
<script async src="https://maps.googleapis.com/maps/api/js?key=YOUR_API_KEYWY"></script>
I called the initMap function from within my JS file by adding the following to my app.js file.
initMap();
This is the top result when you Google 'initMap function is not a function' so although there are a lot of answers already, I will make it very clear for the rookies like me what is happening.
To load a Google Map your page need 3 things:
A <div id="map"></div>
Load the Google Map javascript.
A function to initiate the map.
The div (easy)
The div is easy. The Google Map Javascript is easy but there is a catch.
Load Google Maps Javascript
<script
src="https://maps.googleapis.com/maps/api/jskey=YOURKEY&callback=initMap"
defer
></script>
There are two important parts of this script. defer, and the callback=initMap
First let's talk about defer. The defer attribute causes the callback to execute after the full HTML document has been parsed. This makes sure your div is loaded and ready to be used by the javascript. However, does not mean that the Google Maps Javascript will load after your Javascript, and that is why the error happens sometimes.
The init function
You need to call a function to run the code that sets up the Google Map. The callback makes it easy because once the Google Maps code loads, it will call a function you tell it to load the map.
I'll repeat that because it is important. The callback=initMap is calling the initMap function in your code.
So if your code hasn't loaded by the time the Google Maps javascript loads, it will call initMap but you get the error because it doesn't know what initMap is because your code, where you define initMap hasn't loaded yet.
This can happen seemingly randomly as network times vary. Sometimes you code loads first, and it works. Sometimes your code doesn't load first and Google Maps code loads first and you get the error.
Solution
There are a lot of solutions, but the one that I like is don't set a callback, and init the Map yourself after you know everything is loaded.
This way, we can load Google Maps Javascript async, which is faster since it loads in a separate process and doesn't wait for anything.
<script async
src="https://maps.googleapis.com/maps/api/js?key=YOURKEY">
</script>
Notice how there is no callback set. So Google Map Javascript will not automatically call my init function, I have to call it when I know everything is ready.
We can do that with a window load event listener.
window.addEventListener("load", docReady);
function docReady() {
initMap();
}
The window load event fires after everything including Javascript has loaded.
Of course this might slow the page down as it has to load everything, including images, and then it will fire and initialize the map, but it ensures it will work every time.
As you learn more, you will learn other ways to make sure things load in the correct order. Good luck!
This solution helped me:
this.initMap = () => {};
...
src="https://maps.google.com/maps/api/js?key=YOUR_API_KEY&callback=initMap"
In my case, I had to load the Map on my Wordpress website and the problem was that the Google's api script was loading before the initMap(). Therefore, I solved the problem with a delay:
<script>
function initMap() {
// Your Javascript Codes for the map
...
}
<?php
// Delay for 5 seconds
sleep(5);
?>
</script>
<script async defer src="https://maps.googleapis.com/maps/api/js?key=YOUR_API_KEYWY&callback=initMap"></script>

Loading angular directives at runtime

I have a service that I call during app.run() and for some reason when I load the files async at that point they don't seem to take.
Here's the service i'm using:
angular.module('nav').service('SubmoduleService', ['submodules_config',
function(config){
this.autoload = function(){
for(var key in config.modules){
for(var i=0; i<config.modules[key].length; i++){
var src = config.modules[key][i].replace(':path', config.path).replace(':name', key);
console.log(src);
var js = document.createElement("script");
js.type = "text/javascript";
js.src = src;
document.body.appendChild(js);
}
}
return true;
};
}]);
Here's the config file:
angular.module('nav').constant('submodules_config', {
path: "scripts/submodules/:name",
modules: {
gallery: [':path/config.js', ':path/directive.js']
}
});
So basically the config defines a module and all the files that need to get loaded for that module.
I see the files get loaded into the DOM, but for some reason when I load the controller that uses that directive, it doesn't work.
NOTE: The directive works when loading the files explicitly.
Any help is appreciated.
E
By default, angular bootstraps the application on the DOM Ready event. When you load scripts asynchronously, this event can be fired before the scripts load, so angular won't know about the directives contained in them when the DOM is $compiled().
There are quite a few ways to work around this, but they all revolve around deferring compilation of the DOM until the required modules are loaded.
On simple (but not the only) way to defer compilation for a fragment of your application is to simply use ng-if. In pseudo code, it would look something like this:
<div ng-if="moduleWithMyDirectiveLoaded" my-directive></div>
This leaves you with the task of figuring out how to determine if a particular script has been loaded and getting that information into your angular application.
Unfortunately, this isn't trivial. You can write this yourself, but others have already done it for you and you'd probably be better off using one of those tested, cross-browser solutions.
require.js comes to mind as an option, but there are many others that would also work.

Using waitfor from stratified in AngularJS

I'm using the StratifiedJS library's waitFor construct.
function myFunction() {
// some declarations
waitfor() {
AsyncService.getThisDone().then(function(result) {
// some more calculation
resume();
});
}
}
I am getting unexpected '{' on the line where waitfor is used as I cannot enclose the above code in <script type="text/sjs"> tag.
How can I get this working in AngularJS
The library you linked says you need to include this on the main page:
<script type="text/sjs">
// Your SJS code here!
</script>
Make sure you've labelled it that way. Most javascript is labelled as "text/javascript" (or has no label at all, in which case the <script> tag implies javascript.
The code you posted runs fine when typed in the command-line-like eval tool they have on the StratifiedJS page - likely there is nothing wrong with the code itself, just the way it's loaded.
As #blgt said, you need to put the SJS code inside an appropriate <script type="text/sjs"> tag (since most StratifiedJS code is not valid JavaScript).
In order to access SJS functions from plain JS frameworks like SJS, you could attach them to some well known object, e.g:
<script type="text/sjs">
window.myFunction = function() {
waitfor() {
AsyncService.getThisDone().then(function(result) {
// some more calculation
resume();
});
}
}
</script>
You can then call window.myFunction from any JS code. In real code you'd probably pick a more unique name ;)
Note: it's impossible to actually wait for the execution of a suspending StratifiedJS function like this if you call it from JS. So if you're calling StratifiedJS functions from JS, generally they should be fire-and-forget (i.e functions which are only executed for their side-effects, rather than functions which return a result).
This blog post has some details about integrating AngularJS and StratifiedJS, which may clarify the relation between them:
http://onilabs.com/blog/megazine
The short version is that your JavaScript syntax is invalid.
The medium version is that
functionCall() {
// stuff here
}
...is invalid. You'd need a ; after functionCall(), and if you had one, the block following it would have no purpose.
Given that your linked documentation suggests that it is valid, there must be some pre-processing step that turns the code described into valid JavaScript that you've skipped. So your code is being processed as normal JavaScript, in which this construct is not valid.
The solution is to ensure that this code is pre-processed and/or run in the right environment (I think it's by their Conductance server).

Angular translate extend existing translations

I am trying to have external modules change my $translateProvider.translation on the main module. see this as a "tranlation plugin" for my app.
it seems like changing translations from the $translate service is not possible.
mymodule.service('MyService', function ($translateProvider) {
var lib = function () {
//EDITED FOR BREVITY
this._registerTranslations = function (ctrl) {
if (!ctrl.i18n) return;
for (var name in ctrl.i18n) {
/////////////////////////////
// THIS IS THE PLACE, OBVIOUSLY PROVIDER IS NOT AVAILABLE!!!!
$translateProvider.translations(name, ctrl.i18n[name]);
//////////////////////////////
}
};
//EDITED FOR BREVITY
};
return new lib();
});
anyone with a bright idea?
So, to answer your question: there's no way to extend existing translations during runtime with $translate service without using asynchronous loading. I wonder why you want to do that anyway, because adding translations in such a way means that they are already there (otherwise you would obviously use asynchronous loading).
Have a look at the Asynchronous loading page. You can create a factory that will load a translation from wherever you want.
I created an Angular constant to hold new translations. If I want to add a new translation, I add it to the constant. Then in my custom loader, I first check the constant to see if the translation exists (either a new one, or an updated one). If so, I load it from the constant. If not, I load it from a .json file (or wherever you load your initial translations from). Use $translate.refresh() to force translations to be reloaded and reevaluated.
Demo here
The demo is pretty simple. You would need to do a little more work if you wanted to just change a subset of the translations, but you get the general idea.
From the AngularJS docs (https://docs.angularjs.org/guide/providers):
You should use the Provider recipe only when you want to expose an API for application-wide configuration that must be made before the application starts. This is usually interesting only for reusable services whose behavior might need to vary slightly between applications.
Providers are to be used with the application's .config function. $translateProvider for configuration, $translate for other services and controllers.

Resources