Getting "jQuery is not defined" when using a library that depends on it - scala.js

I am trying to use the bootstrap input spinner in my scalajs app. But when I try to use it I am getting the error "jQuery is not defined", even though I have included it in my project. Can someone tell me what I am missing?
So if I define a build.sbt:
name := "repro"
version := "1.0"
scalaVersion := "2.12.8"
scalaJSUseMainModuleInitializer := true
mainClass in Compile := Some("App")
enablePlugins(ScalaJSPlugin)
enablePlugins(ScalaJSBundlerPlugin)
webpackBundlingMode := BundlingMode.LibraryOnly()
libraryDependencies ++= Seq(
"org.querki" %%% "jquery-facade" % "1.2"
)
npmDependencies in Compile ++= Seq(
"bootstrap" -> "4.3.1",
"jquery" -> "3.2.1",
"bootstrap-input-spinner" -> "1.11.8",
)
And then try to use it in my app as follows:
#js.native
trait BootstrapInputSpinner extends JQuery {
def inputSpinner(options: js.Object = js.Dynamic.literal()): BootstrapInputSpinner = js.native
}
object BootstrapInputSpinner {
#JSImport("bootstrap-input-spinner", JSImport.Default)
#js.native
object Import extends BootstrapInputSpinner
val _import = Import // explicitly import it
implicit def bisExtensions(query: JQuery): BootstrapInputSpinner =
query.asInstanceOf[BootstrapInputSpinner]
}
object App {
import BootstrapInputSpinner._
def main(args: Array[String]): Unit = {
$(dom.document.getElementById("spinner")).inputSpinner()
}
}
My html file is defined as follows:
<!DOCTYPE html>
<html>
<head>
<title>Test User interface</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body>
<div class="app-container" id="root">
<input id="spinner" type="number"/>
</div>
<script src="repro-fastopt-library.js"></script>
<script src="repro-fastopt-loader.js"></script>
<script src="repro-fastopt.js"></script>
</body>
</html>
Note that if I don't refer to the bootstrap-input-spinner library and try to use jQuery on its own it works fine. For example there are no errors if I change my App object to:
object App {
// import BootstrapInputSpinner._
def main(args: Array[String]): Unit = {
println($(dom.document.getElementById("spinner")))
}
}
Also, I checked the -library.js file and it has the following code:
module.exports = {
"require": (function(x1) {
return {
"jquery": __webpack_require__(2),
"bootstrap-input-spinner": __webpack_require__(3)
}[x1]
})
}
Which tells me that jquery should be imported first?

I could guess from looking at the code, that you might need to add jQuery script too, before the library script that depend on.
Side note: You must check the compatibility of the bootstrap version and the jquery it depends
<script
src="https://code.jquery.com/jquery-3.4.1.min.js"
integrity="sha256-CSXorXvZcTkaix6Yvo6HppcZGetbYMGWSFlBw8HfCJo="
crossorigin="anonymous"></script>
<script src="repro-fastopt-library.js"></script>
<script src="repro-fastopt-loader.js"></script>
<script src="repro-fastopt.js"></script>
Which of the scripts depend on jquery?
repro-fastopt-library or
repro-fastopt-loader or
repro-fastopt
It could be the import order when you bundle them.

Related

Gatling: execute a check on some JSON hidden inside an HTML response

In Gatling, I'd like to perform a check on some JSON included in an HTML response like below:
<!doctype html>
<html lang="fr">
<head>
<script>
var documentLoaded = performance.now();
</script>
<link rel="stylesheet" href="/styles/main.f14d8fab5a7e.css">
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png">
<link rel="manifest" href="/manifest.json">
<link rel="preconnect" href="https://www.gstatic.com">
<title data-react-helmet="true">Asus Discount</title>
<meta data-react-helmet="true" name="description" content="Asus discount”/><meta data-react-helmet="true" name="keywords" content="Asus"/>
</head>
<body>
<div>Some content</div>
<script>
var parseStart = performance.now();
</script>
<script>
window.__INITIAL_STATE__ = {some JSON}; <!-- This is what I need -->
window.__ENV_VARIABLES__ = {some other JSON};
window.renderTime = '76';
window.fetchTime = '349';
</script>
<script type="text/javascript" charset="utf-8" src="/vendor.e33d9940372.js"></script>
<script type="application/ld+json" src="/schema.fr.json"></script>
</body>
</html>
My actual solution (which is working) looks as follow:
def loadPageJsonInHTML(requestName: String, link: String): ChainBuilder ={
exec(
http(requestName)
.get(link)
.check(regex("""window[.]__INITIAL_STATE__ = ([^;]+)""").find.transform(s => parseSToProdList(s)).saveAs("prod_list")
)
)
doIf("${prod_list.size()}" == 0){
exec{session => session.markAsFailed}
}
}
def parseSToProdList(jsonString: String): Seq[String] ={
val jsonMap = jsonStrToMap(jsonString)
val buffer = mutable.Buffer.empty[String]
jsonMap("products").asInstanceOf[Map[String, Any]].foreach{f =>
if(f._2.asInstanceOf[Map[String, Any]].keySet.exists(_ == "code"))
buffer.append(f._2.asInstanceOf[Map[String, Any]]("code").asInstanceOf[String])
}
buffer.toSeq
}
def jsonStrToMap(jsonStr: String): Map[String, Any] = {
implicit val formats = org.json4s.DefaultFormats
parse(jsonStr).extract[Map[String, Any]]
}
However, this solution has several drawbacks:
The check will always succeed as long as the regex is found and doesn't care if there's any product at all in the JSON -> I have to manually check for it later;
Having a function extracting the required data is more difficult to maintain than if I could use a Json Path expression like "$.products.*.code" which could be stored on a centralised paths file for ease of maintenance;
This is the only place where I have to use a transform to check the JSON of a request, making it more difficult to read and understand.
What I'd like to achieve is something that would look a bit like this:
def loadPageJsonInHTML(requestName: String, link: String): ChainBuilder ={
exec(
http(requestName)
.get(link)
.check(jsonPath("""$.products.*.code""").findAll.saveAs("prod_list")
)
Or
def loadPageJsonInHTML(requestName: String, link: String): ChainBuilder ={
exec(
http(requestName)
.get(link)
.check(jsonpJsonPath("""$.products.*.code""").findAll.saveAs("prod_list")
)
Of course, jsonPath doesn't work since most of the answer is HTML. jsonpJsonPath doesn't work either as there's several Json strings in the response.
Any good input as to how could I do this more effectively (and nicely) while avoiding a regex on some HTML? Thanks in advance
So, after some digging I found a workaround using ".transformResponse", in order to extract the string before it's actually checked, giving it a default value that is parsable in Json. Then, to make sure we actually did find the regex, we make sure that it's not our default value :
def loadPageJsonInHTML(requestName: String, link: String): ChainBuilder = {
exec(
http(requestName)
.get(link)
.transformResponse{(session, response) =>
response.copy(body = new StringResponseBody(
(for(m <- """window[.]__INITIAL_STATE__ = ([^;]+)""".r
.findFirstMatchIn(response.body.string)
) yield m.group(1)
).getOrElse("""{"error":"chain not found"}"""),
UTF_8
)
)
}
.check(bodyString.not("""{"error":"chain not found"}"""))
.check(jsonPath("""$.products.*.code""").findAll.saveAs("prod_list")
)
)
}

Problems with i18n with Play App 2.4.6

I have one simple project with Play Framework 2.4.6 that have internationalization configured as documentation on Play describe.
My files are that:
1) Controller:
package controllers
import play.api._
import play.api.mvc._
import play.api.i18n.{I18nSupport,MessagesApi,Messages,Lang}
import play.api.i18n.Messages.Implicits._
import play.api.Play.current
import javax.inject.Inject
class Application #Inject()(val messagesApi: MessagesApi) extends Controller with I18nSupport {
def index = Action { implicit request =>
Ok(views.html.index("Your new application is ready."))
}
}
2) Message resource file:
application.name = Orthoclinic
welcome = Welcome to play!
3) Main template:
#(title: String)(content: Html)(implicit messages: Messages)
<!DOCTYPE html>
<html lang="en">
<head>
<title>#title</title>
<link rel="stylesheet" media="screen" href="#routes.Assets.versioned("/assets/stylesheets/main.css")">
<link rel="shortcut icon" type="image/png" href="#routes.Assets.versioned("/assets/images/favicon.png")">
<script src="#routes.Assets.versioned("/assets/javascripts/hello.js")" type="text/javascript"></script>
</head>
<body>
#Messages("application.name")
#content
</body>
</html>
4) Index template:
#(message: String)
#main("Welcome to Play") {
#play20.welcome(message)
}
5) routes file:
# Home page
GET / controllers.Application.index
# Map static resources from the /public folder to the /assets URL path
GET /assets/*file controllers.Assets.versioned(path="/public", file: Asset)
6) Application conf file:
play.crypto.secret = "k1mIneaPlay_pRoJ"
play.i18n.langs = [ "en", "en-us", "pt-br", "es" ]
Calling ./activator reload compile run my result is that:
[error] /home/josenildo/scala-ide/workspace/orthoclinic-app/app/views/main.scala.html:13: could not find implicit value for para
meter messages: play.api.i18n.Messages
[error] #Messages("application.name")
I have followed the documentation for i18n last version on Play Documentation here.
What's the problem on that implementation?
You just need to add the implicit messages argument to your index.scala.html template:
#(message: String)(implicit messages: Messages)
#main("Welcome to Play") {
#play20.welcome(message)
}
An implicit Messages instance needs to be in-scope whenever you use i18n via #Messages("my.key"), which has a corresponding implicit messages argument that will be supplied by the compiler (see the signature here).
Also, you may want to get rid of import play.api.i18n.Messages.Implicits._, since it shouldn't be required if your controller extends I18NSupport, and indeed may cause an error concerning ambiguous implicit values.

Adding inline images to Mailgun emails using Scala and Play WS

I can succesfully make POST requests to Mailgun and receive the emails as expected. I'm trying to inline an image into an email and can't work out how to do it.
Looking at https://documentation.mailgun.com/user_manual.html#sending-via-api and selecting Java, I can see that the example given constructs a FileDataBodyPart with "inline", the File reference and the MediaType. Looking at the curl example, this seems rather unnecessary as that just references a file.
Here is my method for sending an email:
def send(message:EmailMessage) = {
val postMessage = Map("from" -> Seq(message.from), "to" -> Seq(message.to), "subject" -> Seq(message.subject), "text" -> Seq(message.text), "html" -> Seq(message.html.toString()))
val logo = FileBody(Play.getExistingFile("/public/images/logo.png").get)
WS.url(apiUrl).withAuth("api", myKey, WSAuthScheme.BASIC).withBody(logo).post(postMessage)
}
The message.html.toString looks like the following:
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
</head>
<body style="background-color:#9B59B6; padding:10px">
<img src="cid:logo.png">
<h1 style="color:#FFF">Activate!</h1>
</body>
</html>
The logo.png file is found when sending the email and the email comes through fine, but with no image. This is what the email source looks like once it arrives at gmail:
Mime-Version: 1.0
Content-Type: text/html; charset="ascii"
Content-Transfer-Encoding: 7bit
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
</head>
<body style="background-color:#9B59B6; padding:10px">
<img src="cid:logo.png">
<h1 style="color:#FFF">Activate!</h1>
</body>
</html>
I can't see any base64 encoding of the image in the email. As the curl example appeared to just be passing a file as part of the POST, I though I'd try that. Here is what I did:
def send(message:EmailMessage) = {
val logoFile = Play.getExistingFile("/public/images/logo.png").get
val source = Files.readAllBytes(Paths.get(logoFile.getAbsolutePath))
val logoBase64 = Base64.encodeBase64String(source)
val postMessage = Map("from" -> Seq(message.from), "to" -> Seq(message.to), "subject" -> Seq(message.subject), "text" -> Seq(message.text), "html" -> Seq(message.html.toString()), "inline" -> Seq(logoBase64))
WS.url("https://api.mailgun.net/v2/sandboxaa9afcea1f2e4d5db5e2c080f7784b74.mailgun.org/messages").withAuth("api", "key-f165695d4c72e929ff8215115e648c95", WSAuthScheme.BASIC).post(postMessage)
}
I converted the logo into base64 and POSTed that like the other parameters. Still no joy.
What am I missing here? Do I need to pass this in the body, but somehow specify that this is an "inline" file?
I solved this by using Jersey, as suggested in the section on libraries: https://documentation.mailgun.com/libraries.html#java
I imported Jersey in sbt using the following:
libraryDependencies += "com.sun.jersey" % "jersey-core" % "1.18.3"
libraryDependencies += "com.sun.jersey" % "jersey-client" % "1.18.3"
libraryDependencies += "com.sun.jersey.contribs" % "jersey-multipart" % "1.18.3"
and then created my Email sending object like so:
object Email {
val client = Client.create()
client.addFilter(new HTTPBasicAuthFilter("api", current.configuration.getString("mailgun.api.key").get))
val webResource = client.resource(current.configuration.getString("mailgun.api.url").get)
def send(message:EmailMessage) = {
val form = new FormDataMultiPart
form.field("from", message.from)
form.field("to", message.to)
form.field("subject", message.subject)
form.field("text", message.text)
form.field("html", message.html.toString())
val logo = Play.getExistingFile("/public/images/logo.png").get
form.bodyPart(new FileDataBodyPart("inline", logo, MediaType.APPLICATION_OCTET_STREAM_TYPE))
webResource.`type`(MediaType.MULTIPART_FORM_DATA_TYPE).post(form)
}
}
I hope this helps someone.

what options can be passed to flashing in play framework

Hello there Grammarians,
The documentation only shows:
flashing("success")
Do failures never happen if I use play? I've tried "failure" & "error" they don't do anything
You can either pass in a Flash instance or tuples of Strings. It doesn't have to be a specific String. Important is that you handle whatever you stick into the flash scope.
consider this example (Play 2.3.4):
Application.scala
package controllers
import play.api.mvc._
object Application extends Controller {
def index = Action { implicit req =>
Redirect(routes.Application.flash()).flashing("something" -> "show this text")
}
def flash = Action { implicit req =>
Ok(views.html.index("Flash!"))
}
}
index.scala.html
#(title: String)(implicit flash: Flash)
<!DOCTYPE html>
<html>
<head>
<title>#title</title>
</head>
<body>
<h1>#flash.get("something")</h1>
</body>
</html>
routes
# Home page
GET / controllers.Application.index
GET /flash controllers.Application.flash

Use a "Coffee Script Class" instead of a Method as Angular JS ng-controller

I want to do something that I think will be a good way to use "Coffee Script Class" and Angular JS structures.
<!doctype html>
<html ng-app>
<head>
<meta charset=utf-8>
<meta name=viewport content="width=device-width, initial-scale=1">
<title>Main Test</title>
<link href="bootstrap/css/bootstrap.min.css" rel="stylesheet">
<link href="bootstrap/css/bootstrap-responsive.min.css" rel="stylesheet">
<link href="css/style.css" rel="stylesheet">
<script type="text/javascript" src="js/jquery-1.8.3.min.js"></script>
<script type="text/javascript" src="bootstrap/js/bootstrap.min.js"></script>
<script type="text/javascript" src="js/angular.min.js"></script>
<script type="text/javascript" src="js/coffee.js"></script>
</head>
<body>
<div ng-controller="MainClass" style="margin-left:10px">
<h2>All todos: {{test()}}</h2>
</div>
</body>
</html>
Notice than I setup the DIV ng-controller as MainClass and binding test() method inside a H2 HTML tag.
class AngularJSController
constructor: ($scope, $main) ->
$scope.test = MainClass.prototype.test
MainClass.test = MainClass.prototype.test
$main.test = MainClass.prototype.test
test = MainClass.prototype.test
#test = MainClass.prototype.test
console.log #test
class MainClass extends AngularJSController
constructor: ($scope) ->
super $scope, this
setTimeout (->
console.log test()
), 1000
test();
test: -> 'aloha!'
In AngularJSController constructor I've tried all forms I imagined to setup my super class method TEST on MainClass scope without success.
I'm trying to do it because I want to work with classes just on my Angular JS controllers and components.
Problems I already fell:
If I try to use #test() instead of test() inside setTimeout, jQuery already replaced the this property with a kind of JQuery Window Object.
setTimeout (-> console.log #test()), 1000
I don't know what really the scope of test() calls, if a place this (or # cause Coffee), isn't the same of place anything.
test() != this.test() != #.test() # the first scope isn't the same scope of last two calls
I have used the following syntax:
app = angular.module 'myapp', []
class MySimpleCtrl
#$inject: ['$scope']
constructor: (#scope) ->
#scope.demo = 'demo value'
#scope.clearText = #clearText
clearText: =>
#scope.demo = ""
app.controller 'MySimpleCtrl', MySimpleCtrl
angular.bootstrap document, ['myapp']
Take a look at this jsFiddle:
http://jsfiddle.net/jwcMA/
Here's a generic approach with a base class:
http://www.devign.me/angular-dot-js-coffeescript-controller-base-class/
BaseCtrl.coffee
# dependency - Function.prototype.bind or underscore/lodash
app = angular.module 'someApp'
class #BaseCtrl
#register: (app, name) ->
name ?= #name || #toString().match(/function\s*(.*?)\(/)?[1]
app.controller name, #
#inject: (args...) ->
#$inject = args
constructor: (args...) ->
for key, index in #constructor.$inject
#[key] = args[index]
for key, fn of #constructor.prototype
continue unless typeof fn is 'function'
continue if key in ['constructor', 'initialize'] or key[0] is '_'
#$scope[key] = fn.bind?(#) || _.bind(fn, #)
#initialize?()
BookFormCtrl.coffee
app = angular.module 'someApp'
class BookFormCtrl extends BaseCtrl
#register app
# list of dependencies to be injected
# each will be glued to the instance of the controller as a property
# e.g. #$scope, #Book
#inject '$scope', 'Book'
# initialize the controller
initialize: ->
#$scope.book =
title: "Hello"
# automatically glued to the scope, with the controller instance as the context/this
# so usable as <form ng-submit="submit()">
# methods that start with an underscore are considered as private and won't be glued
submit: ->
#Book.post(#$scope.book).then =>
#$scope.book.title = ""
setTimeout isn't related in any way to jQuery, but function passed to setTimeout is indeed executed in the global(window) context. Use fat arrow to explicitly bind it to the current scope.
http://coffeescript.org/#fat_arrow
setTimeout (=>
console.log #test()
), 1000
Sorry, I'm not sure what are you asking about.
Angular does supports Coffescript classes!, for the most part.
I've found issues with using RequireJs, expecting a function instead of an object.
take a look here :
http://softwareninjaneer.com/blog/writing-angularjs-controllers-coffeescript-classes/
I didn't have luck with #malix's answer although it did lead me to a solution that works. Here's how I'm doing mine:
'use strict'
class ImportsCtrl
constructor: ($scope, Import) ->
Import.query().then (imports) -> $scope.imports = imports
angular.module("greatDealsApp").controller "ImportsCtrl", ImportsCtrl
And here's a contrived example of turning $scope into a property of ImportsCtrl:
'use strict'
class ImportsCtrl
constructor: ($scope, Import) ->
#scope = $scope
#Import = Import
#loadImports()
loadImports: =>
#Import.query().then (imports) => #scope.imports = imports
angular.module("greatDealsApp").controller "ImportsCtrl", ImportsCtrl
And I don't know for sure if this matters, but I'm using ng-annotate.
It's impossible. You can't set a controller as an object (MainClass), because AngularJS ensures that the controller is a function.
You need to customize AngularJS to do it. I think it's not a good idea.
References
https://github.com/angular/angular.js/blob/master/src/ng/controller.js
https://github.com/angular/angular.js/blob/master/src/Angular.js