using EJS with requirejs - requirejs

I had a problem include ejs into requirejs. I put <script data-main="js/app" src="js/require.js"></script> in my and inside of body create EJS object.
In my app.js,
require.config({
paths: {
//library
jquery: 'lib/jquery-1.11.1.min',
jquerymobile: "lib/jquery.mobile-1.4.2.min",
text: 'text',
ejs: 'ejs_0.9_alpha_1_production'
},
shim: {
"ejs": {
exports: 'ejs'
}
}
});
require(['jquery', 'jquerymobile','text','ejs'], function ($, mobile) {
console.log('jQuery version:', $.fn.jquery); // 1.9.0
});
when it is running, it throws EJS is not defined error. However, if I include
<script type="text/javascript" src="js/ejs_0.9_alpha_1_production.js"></script>
in the head, everything goes well.
Regards
Hammer

Lately I just get through similar trouble using ejs from the browser. I post the answer as it could save somebody's time.
I suggest you double check your ejs library is indeed coming from https://github.com/visionmedia/ejs. There is quite some tuned version of ejs around right now because it is becoming very popular. Unfortunatly most of thoses versions target specific needs and return different object to the window (eg. EJS instead of ejs) or don't even return anything usefull for requirejs.
=> In both case this would expalin why your shim exports return undefined.
Once you get it to load properly, let me also spot on an awesome requirejs-ejs plugin at https://github.com/whitcomb/requirejs-ejs . It could help you preload and render your template in a nice requirejs way.

Related

Typesetting MathJax in a string of HTML server-side

I was pretty easily able to get npm's mathjax-full working, for parsing TeX to CommonHTML:
const MathJax = await require("mathjax-full").init({
options: {
enableAssistiveMml: true
},
loader: {
source: {},
load: ["adaptors/liteDOM", "tex-chtml"]
},
tex: {
packages: ["base", "ams", "newcommand"]
},
chtml: {
fontURL: "https://cdn.jsdelivr.net/npm/mathjax#3/es5/output/chtml/fonts/woff-v2"
},
startup: {
typeset: false
}
});
MathJax.tex2chtmlPromise("x^2-2x+1", {
display: true,
em: 16,
ex: 8
}).then((node) => {
var adaptor = MathJax.startup.adaptor;
console.log(adaptor.outerHTML(node));
});
However, unlike typeset/typesetPromise, rather than a DOM node or string of HTML, this works with the TeX directly. Of course I could parse the page myself, finding MathJax delimiters (outside of code blocks) and passing the contents to tex2chtmlPromise, but this would have the potential of bugs or differences in behavior between a client-side preview using MathJax's typeset and the server-side rendered version.
I've looked around in the internals of the liteDOM adaptor quite a bit, but can't seem to find any way of setting the innerHTML of its body, if that would be the correct approach (which would allow me to just use typesetPromise normally).
Is there a recommended way to do what I'm trying to do, namely, take some HTML, and typeset it with MathJax without parsing for the delimiters myself?
The MathJax node demos repository includes examples of how to process an HTML page that should give you what you need. There are several different ways to load and call mathJax, so there are separate directories that illustrate each of them. You are using the "simple" approach, but may also want to look at the "component" and "direct" approaches. Look for files that end in -page.
The main idea for the simple case is to use the document option in the startup section of your MathJax configuration. This allows you to provide a serialized HTML string to be used as the document to be processed. So in your case, you could change
startup: {
typeset: false
}
to
startup: {
typeset: false,
document: html
}
where html is the HTML string to be processed. E.g.,
html = `
<!DOCTYPE html>
<html>
<head>
<title>My Document</title>
</head>
<body>
<p>
Some math: \(E = mc^2\).
</p>
</body>
</html>
`;
If you want the same invocation of your node app to be able to process multiple pages, then you will need to use the "direct" approach, as illustrated in direct/tex2chtml-page, and do these lines for each html file you want to process. You can reuse the same output jax, but should create a new InputJax and MathDocument for each page you process.

Using 3rd party jquery plugins in Apostrophe cms

In my Apostrophe cms I have a portion in the header like this (in the outerLayout.html file):
<div id="sticky-header">
...
</div>
In the footer I have done the following:
<script src="/thirdparty/jquery/jquery-3.2.1.min.js"></script>
<script src="/thirdparty/sticky/jquery.sticky.js"></script>
I understand that apostrophe somehow includes jQuery, but if I do not include it myself I get an error in the console:
jquery.sticky.js:22 Uncaught ReferenceError: jQuery is not defined
at jquery.sticky.js:22
at jquery.sticky.js:24
I also have the following in one of the always.js files
$("#sticky-header").sticky({
topSpacing:0,
zIndex:1000
});
And that generates the error:
always.js:109 Uncaught TypeError: $(...).sticky is not a function
at always.js:109
How can I solve this?
In your case, the reason you need to push your own copy of jQuery is that including files from outerLayout is running front-end javascript OUTSIDE of Apostrophe's front-end asset pipeline.
To include your 3rd party / custom javascript INSIDE Apostrophe's asset pipeline (which is recommended and where jQuery is initially run) you need to push the javascript files from an Apostrophe module.
The quickest path forward is to push the asset from the apostrophe-assets module which should already be in your project.
in app.js
...
'apostrophe-assets': {
scripts: [
{
name: 'yourFile'
}
]
},
...
This will load lib/modules/apostrophe-assets/public/js/yourFile.js
More on pushing assets to the browser here http://apostrophecms.org/docs/tutorials/getting-started/pushing-assets.html
Down the road you may want to organize front-end assets by their appropriate module instead of pushing them all in a heap, this would be a good reference
http://apostrophecms.org/docs/tutorials/getting-started/custom-widgets.html#adding-a-java-script-widget-player-on-the-browser-side
Also, what you can expect to be there when you do push javascript
http://apostrophecms.org/docs/tutorials/getting-started/custom-widgets.html#what-39-s-available-in-the-browser
Thanx a lot Stuart - that definitely pushed me in the right direction :)
However what I ended up doing to make it work was to first put the files in lib/modules/apostrophe-assets/public/js/ like you suggested, and then edit the lib/modules/apostrophe-assets/index.js file:
module.exports = {
stylesheets: [
{
name: 'site'
}
],
scripts: [
{
name: 'bootstrap/js/bootstrap.min'
},
{
name: 'sticky/jquery.sticky'
},
{
name: 'scrollto/jquery.scrollTo.min'
}
]
};

How does the text! plugin use the baseUrl?

I'm having an issue getting the text! plugin to work in my requirejs site. It's always including lib/ in the request url, however all of the other files (not using text!) are being successfully found and loaded. Here is my directory structure:
WebContent/
|--backbone/
|--Bunch of folders and files
|--config/
|--config.js
|--lib/
|--jquery.js
|--text.js
|--require.js
|--index.html
my index.html file is:
<body>
<div id="siteLayoutContainer"></div>
<script data-main='config/config' src="lib/require.js"></script>
</body>
The config file is:
requirejs.config({
baseUrl: './',
paths: {
jquery: 'lib/jquery.js',
backbone: 'lib/backbone.js',
text: 'lib/text',
application: 'backbone/application'
},
text: {
env: 'xhr'
}
});
require(['application'], function(App) {
App.start();
});
I'm using the text! plugin like so:
define([
'jquery',
'text!backbone/templates/SomeTemplate.html'
], function(jQuery, NotFoundHtml) {
//Some code here
}
So, in the above script, the url being used for the template is:
http://localhost/lib/backbone/templates/SomeTemplate.html
and I am expecting it to be:
http://localhost/backbone/templates/SomeTemplate.html
I've tried the following:
Moving the text.js and require.js files out into the WebContent
directory but I get the same results. Also something interesting is
if I put a space after text! and then the path, that works fine and
doesn't include the lib/ directory in the request to get the html
template. However the optimizer includes the space and can't find the
template.
Not defining a baseUrl - same results.
Moved the
require config.js content into index.html in it's own script tag that runs
before the require.js script tag - same results.
Getting rid of the the text options in the config file
Oh yeah, forgot I've also tried 'text!../backbone/templates/SomeTemplate.html - same results
So I'm stuck and can't figure out what I'm missing. I'm obviously not understanding how the text! plugin uses the baseUrl or how it determines the url it's going to use to fetch the defined file.
After your edits to your question, it now contains all the information to diagnose the problem. As you guessed in one of your comments, the issue is indeed that this path:
backbone: 'lib/backbone.js',
is throwing off the resolution of the template you give to the text plugin. When the text plugin loads what you give to it, it takes the path after the ! symbol and treats it as if it were a module name, and it goes through the module resolution process. The way module resolution works is that it checks if there is a prefix that matches any of the keys in paths and will change the prefix with the value associated with the key, which gives the result you obtained. One way to fix the issue would be to add this to your paths configuration:
"backbone/templates": "backbone/templates"
This will make it so that anything you request under backbone/templates won't get messed up by the backbone path.
Note: it is preferable to avoid putting extensions in module names so you should remove it from the values you have for jQuery and Backbone.

Angular.JS + Jade = angular not processing jade's embedding iframe

So I am trying to embed a video using jade and angular.js. I try using the following code in jade, but angular didn't process it, while when i type p {{video.updated_time}} there is no problem, but the following code does not work
iframe(ng-src="{{video.embedLink}}")
I also tried writing an angular directive. This is the code of the directive
app.directive('embedDirective', function() {
return {
restrict: 'A',
template: '<div style="height:315px;width:560px;"><iframe class="video" style="overflow:hidden;height:100%;width:100%" width="100%" height="100%" src="{{url}}" frameborder="0" allowfullscreen></iframe></div>'
};
});
And I use this in my jade file: div(embed-directive, url="video.embedLink") . None of the solutions seem to work for me. Only difference is that when I use a directive, no error for failed embed processing is shown in the console, but the src in the iframe is empty. No brackets no equation mark, just src in the middle of the iframe. Also in the iframe there seem to be html, head and body tags, which DO NOT exist in my directive. I am pretty confused
It seems like angular is trying to protect the application from foreign link, therefore one will need to 'trustify' a link. This is what I found the easiest. Simply add this function in the controller
$scope.trustSrc = function(src) {
return $sce.trustAsResourceUrl(src);
}
and then this is how one should use a <iframe></iframe> with jade
iframe(ng-src="{{trustSrc(video.embedLink)}}", src="", widht="560", height="315", frameborder="0", allowfullscreen)
Of course, $sce should be in the controller dependencies.

Chrome Extension: Using addEventListener()

In the tutorial for migrating a Google Chrome Extension to Manifest Version 2, I am directed to Remove inline event handlers (like onclick, etc) from the HTML code, move them into an external JS file and use addEventListener() instead.
OK, I currently have a background.html page that looks like this…
<html>
<script type="text/javascript">
// Lots of script code here, snipped
…
</script>
<body onload="checkInMyNPAPIPlugin('pluginId');">
<object type="application/x-mynpapiplugin" id="pluginId">
</body>
</html>
Following another directive, I've moved that Lots of script code into a separate .js file, and following this directive, I need to remove the onload= from the body tag, and instead cal addEventListener() in my script code. I've tried several approaches, but am apparently guessing wrong. What will that code look like? In particular, upon what object do I invoke addEventListener()?
Thanks!
I normally use this for body onload event...
document.addEventListener('DOMContentLoaded', function () {
// My code here.. ( Your code here )
});
For somethings it is working.. but really, I think we should use..
window.addEventListener('load', function () {
document.getElementById("#Our_DOM_Element").addEventListener('change - or - click..', function(){
// code..
});
});

Resources