XPages 9 with Esri maps dojo conflict: 'defineAlreadyDefined' - dojo

With our update to XPages version 9 and the Esri ARcgis javascript api v 3.5, we're having problems with our dojo namespaces resulting in a defineAlreadyDefined error. There are a few similar problems listed here (Using Durandal dojoConfig and ESRI Maps, How can I fix this AMD path conflict?), but even with that help we are unable to get it working. I believe the issue is the dojoConfig syntax - any thoughts or help would be appreciated!
Here is a simple version of our xpage source code with js:
<xp:view xmlns:xp="http://www.ibm.com/xsp/core">
<xp:this.resources>
<xp:styleSheet
href="http://serverapi.arcgisonline.com/jsapi/arcgis/3.5/js/dojo/dijit/themes/claro/claro.css">
</xp:styleSheet>
<xp:styleSheet
href="http://serverapi.arcgisonline.com/jsapi/arcgis/3.5/js/esri/css/esri.css">
</xp:styleSheet>
<xp:script clientSide="true">
// dojo.registerModulePath("esri","http://serverapi.arcgisonline.com/jsapi/arcgis/3.5/js/esri");
dojoConfig = {
baseUrl: "http://serverapi.arcgisonline.com/jsapi/arcgis/3.5/js/esri",
packages: [
{
name: 'dojo',
location: "http://serverapi.arcgisonline.com/jsapi/arcgis/3.5/js/dojo/dojo/"
},
{
name: 'dojox',
location: "http://serverapi.arcgisonline.com/jsapi/arcgis/3.5/js/dojo/dojox"
},
{
name: 'esri',
location: "http://serverapi.arcgisonline.com/jsapi/arcgis/3.5/js/esri"
}
]};
</xp:script>
<xp:script src="http://serverapi.arcgisonline.com/jsapi/arcgis/3.5/"
clientSide="true">
</xp:script>
<xp:dojoModule name="esri.map"></xp:dojoModule>
</xp:this.resources>
<xp:eventHandler event="onClientLoad" submit="false">
<xp:this.script><![CDATA[var map;
function init(){
var map = new esri.Map("mapDiv", {
center: [-56.049, 38.485],
zoom: 3,
basemap: "streets"
});
}
dojo.ready(init);
]]></xp:this.script>
If we include the dojo.registerModulePath command, the map does load (at least in FF), but with the error. Without it, the esri dojo doesn't load - it's looking in the wrong place for the esri files.

There are few things you need to keep in mind, and probably change the code accordingly:
xPages already makes use of dojoconfig, either through xsp-config
file options or using xPages parameters.
As mentioned by Per, dojo is already used in xPages, so you don't need to load it from somewhere else (same applies to the CSS).
What you can do is following:
Option 1: Use offline copies of JS library for maps. You can add them
to your xPages app as JS resources. You will only have to specify
them in your xPage, and load dojo module as you do already;
Option 2: See below how to inject more dojoConfig options before a xPage will load
Code:
<xp:this.properties>
<xp:parameter name="xsp.client.script.dojo.djConfig" value="packages: exPackages" />
</xp:this.properties>
<xp:this.beforePageLoad>
<![CDATA[#{javascript:
var exCon = facesContext.getExternalContext();
var response = exCon.getResponse();
var writer = response.getWriter();
writer.write("<script>\n");
writer.write("var exPackages=[{name:'esri',location:'http://serverapi.arcgisonline.com/jsapi/arcgis/3.5/js/esri'}]\n");
writer.write("</script>\n");
}]]>
</xp:this.beforePageLoad>
<xp:this.resources>
<xp:dojoModule name="esri.map"></xp:dojoModule>
</xp:this.resources>
Update: Small correction of code.
Update2: After briefly checking ArcGis website, it seems they choose to provide Dojo together with their API (which is wrong in my opinion). See https://developers.arcgis.com/en/javascript/jshelp/inside_dojoversion.html (part2), although it will not help you much as they don't provide a feasible solution for Dojo 1.8.x
Seeing that their API is not free, I think the best way would be to contact them, ask for ESRI part of the API as separate download, host it on your own servers and follow either Option 1 or 2. Moreover, the version of API you try to use is based on Dojo 1.8.3, while Domino 9 has Dojo 1.8.

OK, this was a problem for me too!
I've been able to get it working by using the following code:
<xp:view xmlns:xp="http://www.ibm.com/xsp/core">
<xp:this.resources>
<xp:script clientSide="true">
dojo.registerModulePath("esri","http://js.arcgis.com/3.8/js/esri");
dojoConfig = { baseUrl: "http://js.arcgis.com/3.8/js/esri",
packages: [ { name: 'esri', location: "http://js.arcgis.com/3.8/js/esri" } ]};
</xp:script>
<xp:styleSheet href="http://js.arcgis.com/3.8/js/esri/css/esri.css" />
<xp:styleSheet href="http://js.arcgis.com/3.8/js/esri/dijit/css/Popup.css" />
<xp:dojoModule name="esri.map"></xp:dojoModule>
</xp:this.resources>
<xp:eventHandler event="onClientLoad" submit="false">
<xp:this.script><![CDATA[var map;
function init(){
var map = new esri.Map("mapDiv", {
center: [0,53],
zoom: 10,
basemap: "streets"
});
}
dojo.ready(init);
]]></xp:this.script>
</xp:eventHandler>
<div id="mapDiv" style="width:1000px;height:600px"></div>
</xp:view>
I think the order of scripts is important and you don't need to include the esri.map script twice.

Related

How can I use Offline map in open layer v6

I have a project in asp.net core and I want to use offline map, Does open layer v6 support offline map?
I can't find an example, any one can help?
You need to download the ol.js and ol.css or use the link from the document.
Then you need to use bigemap to download the map file(png).
Put them in wwwroot, then reference them in layout.
<link href="./ol.css" rel="stylesheet" />
<script src="./ol.js"></script>
Select a place to put the map, and adjust the size.
<div id="map" class="map"></div>
Use js to draw this map.
<script type="text/javascript">
var map = new ol.Map({
target: 'map', //the id of map
layers: [
new ol.layer.Tile({
source: new ol.source.OSM()
})
],
view: new ol.View({
center: ol.proj.fromLonLat([37.41, 8.82]),//Set the map center
zoom: 4 //Default load level
})
});
var offLineMap = new ol.layer.Tile({
source: new ol.source.XYZ({
url: 'tile/a.png' // the map has been downloaded
})
});
map.addLayer(offLineMap);
</script>

Custom widget js doesn't recognize template from qweb

I try to test custom widget from js reference and I get error in debugger:
Error: QWeb2: Template 'some.template' not found
qweb.xml was properly set in manifest, because when I extend ListController and use another template, it works correctly.
Here is template definition, which I use in qweb.xml:
<?xml version="1.0" encoding="UTF-8"?>
<template>
<div t-name="some.template">
<span class="val"><t t-esc="widget.count"/></span>
<button>Increment</button>
</div>
</template>
I tried to change <template> -> <templates>, totally removed tag "template" but still get the same error message.
JS:
odoo.define('working.test', function (require) {
var Widget = require('web.Widget');
var Counter = Widget.extend({
template: 'some.template',
events: {
'click button': '_onClick',
},
init: function (parent, value) {
this._super(parent);
this.count = value;
},
_onClick: function () {
this.count++;
this.$('.val').text(this.count);
},
});
// Create the instance
var counter = new Counter(this, 4);
// Render and insert into DOM
counter.appendTo(".o_nocontent_help");
})
Manifest:
# -*- coding: utf-8 -*-
{
'name': "testwidget",
'summary': """
Short (1 phrase/line) summary of the module's purpose, used as
subtitle on modules listing or apps.openerp.com""",
'description': """
Long description of module's purpose
""",
'author': "My Company",
'website': "http://www.yourcompany.com",
# Categories can be used to filter modules in modules listing
# Check https://github.com/odoo/odoo/blob/12.0/odoo/addons/base/data/ir_module_category_data.xml
# for the full list
'category': 'Uncategorized',
'version': '0.1',
# any module necessary for this one to work correctly
'depends': ['base'],
'qweb': ['static/qweb.xml'],
# always loaded
'data': [
# 'security/ir.model.access.csv',
'views/views.xml',
'views/web_asset.xml',
],
# only loaded in demonstration mode
'demo': [
'demo/demo.xml',
],
}
Any idea how I need to modify this template to make the widget working correctly and in which table in db odoo stores these templates?
I was running into this same issue and needed to put my QWeb code into static/src/xml/base.xml in order for Odoo to recognize it.
You can check to see if Odoo is loading the QWeb by going to this URL on your Odoo instance:
<odoo_instance>/web/webclient/qweb?mods=<my_module_name>
Such as:
localhost:8069/web/webclient/qweb?mods=test
For comparison, you can see a successful output by using mods=web to load the QWeb assets for the web module.
You can try changing
'qweb': ['static/qweb.xml'],
to
'qweb': ['static/*.xml'],
It happens with me sometimes, by specifying static xml file name, it does not render that template. But by just loading all .xml files by using *, templates are loaded.
To solve this issue I used as workaround Widget.xmlDependencies:
xmlDependencies: ['/test/static/qweb.xml']
but the main reason I think was cache in PyCharm which I didn't invalidate.
After having done some code reading, IMO, I realized the official documentation might not have pointed out clearly how to use templates in frontend.
To summarize my understanding:
The 'qweb' field in manifest is mainly designed for webclient (i.e. the backoffice), not the website. When entering webclient, a request to /web/webclient/qweb is made to retrieve all the templates of installed modules.
In order to use templates in website (i.e. frontend), synchronous and asynchronous ways both exist.
Synchronous way: Use qweb.add_template. When parameter is template content itself or a DOM node, template is loaded in a synchronous way. (While param is a URL, then it fires up an ajax request to server to fetch content.)
qweb.add_template is mentioned in https://www.odoo.com/documentation/13.0/reference/qweb.html
Asynchronous way:
Use ajax.loadXML which you can use anywhere you want to start loading template from a URL.
Use xmlDependencies which you specify in widget definition. And if you dig into the code in widget.js, you can see ajax.loadXML is being used in willStart.
There are discussions regarding qweb.add_template vs ajax.loadXML
See https://github.com/OCA/pylint-odoo/issues/186 and https://github.com/odoo/odoo/issues/20821
FYI.
I guess you may need to make sure that the js definition refers to the module name correctly
odoo.define('MODULE TECHNICAL NAME SHOULD BE HERE.test', function (require) {});
you should also register your js function with something like:
core.action_registry.add("module_name.name", Widget_Extend);
for more info https://www.odoo.com/documentation/11.0/reference/javascript_reference.html#registries
In Odoo 14 make sure
dashboard.js
odoo.define('library_managment.dashboard', function(require) {
"use strict";
// alert("hello odoo...............")
console.log("Hello My Module........!!")
var widgetRegistry = require('web.widget_registry');
var Widget = require('web.Widget');
var Counter = Widget.extend({
template: 'library_managment.template',
xmlDependencies: ['/library_managment/static/src/xml/template.xml'],
events: {
'click button': '_onClick',
},
init: function (parent, value) {
this._super(parent);
this.count = 4*9+5;
console.log("parent is", parent)
console.log("counter is..", this.count)
},
_onClick: function () {
this.count++;
this.$('.val').text(this.count);
},
});
widgetRegistry.add('library_counter', Counter);
return Counter;
});
template.xml
add this
<?xml version="1.0" encoding="UTF-8"?>
<odoo>
<div t-name="library_managment.template">
<span class="val">
<t t-esc="widget.count"/>
</span>
<button class="bg-danger">Increment</button>
</div>
</odoo>
then add js file in assets.xml inside youe views
<odoo>
<template id="assets_backend" name="Library assets" inherit_id="web.assets_backend">
<xpath expr="." position="inside">
<script type="text/javascript" src="/library_managment/static/src/js/dashboard.js"></script>
</xpath>
</template>
</odoo>
then add in manifest like this:
'js': ['/static/src/js/dashboard.js'],
'qweb': ['/static/src/xml/template.xml']
then inside form view add this line
<widget="library_counter"/>
I had the same problem but with "hr_org_chart" template idk why everything works fine in another computer but in mine it returned this problem, I solved it by installing this module hr-org-chart

Set common key prefix for S3 bucket per CKFinder 3 instance

How can I make the CKFinder ASP.net S3 integration load content from a dynamic key prefix rather than just a root location?
I'm using CKEditor 5 and CKFinder 3 with the ASP.net Connector to allow image upload directly to an S3 bucket. The web application we are connecting this all to is not an ASP.net application.
Setting is up was simple enough by following the documentation.
However, our product is SaaS, so each time the CKFinder is launched, I need it to target a different key prefix in our bucket. Multiple websites run off the same app and each should be able to have their own gallery of images loaded via the CKFinder without being able to see the images belonging to other apps.
Our CKFinder Web.config:
<backend name="s3Bucket" adapter="s3">
<option name="bucket" value="myBucket" />
<option name="key" value="KEYHERE" />
<option name="secret" value="SECRETHERE" />
<option name="region" value="us-east-1" />
<option name="root" value="images" />
</backend>
This config gets content into the /images/ common key prefix "folder" just great, but for each app that uses the CKFinder, I want it to read from a different "root":
/images/app1Id/
/images/app2Id/
/images/app3Id/
Ideally, I want to set this when invoking the Editor/Finder instance; something like:
ClassicEditor.create( document.querySelector( '#textareaId' ), {
ckfinder: {
uploadUrl: '/ckfinder/connector?command=QuickUpload&type=Images&responseType=json',
connectorRoot: '/images/app1Id/'
},
toolbar: [ 'heading', '|', 'bold', 'italic', 'link', 'bulletedList', 'numberedList', 'blockQuote', 'ckfinder' ],
heading: {
options: [
{ model: 'paragraph', title: 'Paragraph', class: 'ck-heading_paragraph' },
{ model: 'heading1', view: 'h1', title: 'Heading 1', class: 'ck-heading_heading1' },
{ model: 'heading2', view: 'h2', title: 'Heading 2', class: 'ck-heading_heading2' }
]
}
});
Here I added connectorRoot: '/images/app1Id/' as an example of what I would like to pass.
Is there some way to do something like this? I've read through the ASP.net Connector docs and see that you can build your own connector and use pass to send it data, but having to compile and maintain a custom connector does not sound very fun. The S3 connectivity here is so great and easy... if only it let me be a little more specific.
The solution we came to was to modify and customize the CKFinder ASP Connector. Big thanks to the CKSource team for helping us to get this running.
ConnectorConfig.cs
namespace CKSource.CKFinder.Connector.WebApp
{
using System.Configuration;
using System.Linq;
using CKSource.CKFinder.Connector.Config;
using CKSource.CKFinder.Connector.Core.Acl;
using CKSource.CKFinder.Connector.Core.Builders;
using CKSource.CKFinder.Connector.Host.Owin;
using CKSource.CKFinder.Connector.KeyValue.FileSystem;
using CKSource.FileSystem.Amazon;
//using CKSource.FileSystem.Azure;
//using CKSource.FileSystem.Dropbox;
//using CKSource.FileSystem.Ftp;
using CKSource.FileSystem.Local;
using Owin;
public class ConnectorConfig
{
public static void RegisterFileSystems()
{
FileSystemFactory.RegisterFileSystem<LocalStorage>();
//FileSystemFactory.RegisterFileSystem<DropboxStorage>();
FileSystemFactory.RegisterFileSystem<AmazonStorage>();
//FileSystemFactory.RegisterFileSystem<AzureStorage>();
//FileSystemFactory.RegisterFileSystem<FtpStorage>();
}
public static void SetupConnector(IAppBuilder builder)
{
var allowedRoleMatcherTemplate = ConfigurationManager.AppSettings["ckfinderAllowedRole"];
var authenticator = new RoleBasedAuthenticator(allowedRoleMatcherTemplate);
var connectorFactory = new OwinConnectorFactory();
var connectorBuilder = new ConnectorBuilder();
var connector = connectorBuilder
.LoadConfig()
.SetAuthenticator(authenticator)
.SetRequestConfiguration(
(request, config) =>
{
config.LoadConfig();
var defaultBackend = config.GetBackend("default");
var keyValueStoreProvider = new FileSystemKeyValueStoreProvider(defaultBackend);
config.SetKeyValueStoreProvider(keyValueStoreProvider);
// Remove dummy resource type
config.RemoveResourceType("dummy");
var queryParameters = request.QueryParameters;
// This code lacks some input validation - make sure the user is allowed to access passed appId
string appId = queryParameters.ContainsKey("appId") ? Enumerable.FirstOrDefault(queryParameters["appId"]) : string.Empty;
// set up an array of StringMatchers for folder to hide!
StringMatcher[] hideFoldersMatcher = new StringMatcher[] { new StringMatcher(".*"), new StringMatcher("CVS"), new StringMatcher("thumbs"), new StringMatcher("__thumbs") };
// image type resource setup
var fileSystem_Images = new AmazonStorage(secret: "SECRET-HERE",
key: "KEY-HERE",
bucket: "BUCKET-HERE",
region: "us-east-1",
root: string.Format("images/{0}/userimages/", appId),
signatureVersion: "4");
string[] allowedExtentions_Images = new string[] {"gif","jpeg","jpg","png"};
config.AddBackend("s3Images", fileSystem_Images, string.Format("CDNURL-HERE/images/{0}/userimages/", appId), false);
config.AddResourceType("Images", resourceBuilder => {
resourceBuilder.SetBackend("s3Images", "/")
.SetAllowedExtensions(allowedExtentions_Images)
.SetHideFoldersMatchers(hideFoldersMatcher)
.SetMaxFileSize( 5242880 );
});
// file type resource setup
var fileSystem_Files = new AmazonStorage(secret: "SECRET-HERE",
key: "KEY-HERE",
bucket: "BUCKET-HERE",
region: "us-east-1",
root: string.Format("docs/{0}/userfiles/", appId),
signatureVersion: "4");
string[] allowedExtentions_Files = new string[] {"csv","doc","docx","gif","jpeg","jpg","ods","odt","pdf","png","ppt","pptx","rtf","txt","xls","xlsx"};
config.AddBackend("s3Files", fileSystem_Files, string.Format("CDNURL-HERE/docs/{0}/userfiles/", appId), false);
config.AddResourceType("Files", resourceBuilder => {
resourceBuilder.SetBackend("s3Files", "/")
.SetAllowedExtensions(allowedExtentions_Files)
.SetHideFoldersMatchers(hideFoldersMatcher)
.SetMaxFileSize( 10485760 );
});
})
.Build(connectorFactory);
builder.UseConnector(connector);
}
}
}
Items of note:
Added using System.Linq; so that FirstOrDefault works when getting the appId
We removed some of the fileSystems (Azure,Dropbox,Ftp) because we do not use those in our integration
In the CKFinder web.config file, we create a 'dummy' resource type because the Finder requires at least one to be present, but we then remove it during connector config and replace it with our desired resource types <resourceTypes><resourceType name="dummy" backend="default"></resourceType>resourceTypes>
Please note and take care that you're placing some sensitive information in this file. Please consider how you version control this (or not) and you may want to take additional actions to make this more secure
Initializing a CKEditor4/CKFinder3 instance
<script src="/js/ckeditor/ckeditor.js"></script>
<script src="/js/ckfinder3/ckfinder.js"></script>
<script type="text/javascript">
var myEditor = CKEDITOR.replace( 'bodyContent', {
toolbar: 'Default',
width: '100%',
startupMode: 'wysiwyg',
filebrowserBrowseUrl: '/js/ckfinder3/ckfinder.html?type=Files&appId=12345',
filebrowserUploadUrl: '/js/ckfinder3/connector?command=QuickUpload&type=Files&appId=12345',
filebrowserImageBrowseUrl: '/js/ckfinder3/ckfinder.html?type=Images&appId=12345',
filebrowserImageUploadUrl: '/js/ckfinder3/connector?command=QuickUpload&type=Images&appId=12345',
uploadUrl: '/js/ckfinder3/connector?command=QuickUpload&type=Images&responseType=json&appId=12345'
});
</script>
Items of note:
Due to other integration requirements, are using the Manual Integration method here, which requires us to manually define our filebrowserUrls
Currently, adding &pass=appId to your filebrowserUrls or adding config.pass = 'appId'; to your config.js file does not properly pass the desired value through to the editor
I believe this only fails when using the Manual Integration method (it should work correctly if you're using CKFinder.setupCKEditor())
ckfinder.html
<!DOCTYPE html>
<!--
Copyright (c) 2007-2019, CKSource - Frederico Knabben. All rights reserved.
For licensing, see LICENSE.html or https://ckeditor.com/sales/license/ckfinder
-->
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1,user-scalable=no">
<title>CKFinder 3 - File Browser</title>
</head>
<body>
<script src="ckfinder.js"></script>
<script>
var urlParams = new URLSearchParams( window.location.search );
var myAppId = ( urlParams.has( 'appId' ) ) ? urlParams.get( 'appId' ) : '';
if ( myAppId !== '' ) {
CKFinder.start( { pass: 'appId', appId: myAppId } );
} else {
document.write( 'Error loading configuration.' );
}
</script>
</body>
</html>
Items of note:
This all seems to work much more smoothly when integrating into CKEditor5, but when integrating into CKEditor4, we experience a lot of issues getting the appId value to pass properly into the editor when utilizing the Manual Integration method for CKFinder
We modify the ckfinder.html file here to look for the desired url params and pass them into the CKFinder instance as it's started. This ensures they are passed through the entirety of the Finder instance
Check out this question for some great further details about this process as well as a more generic method of passing n params into your Finder instances: How do I pass custom values to CKFinder3 when instantiating a CKEditor4 instance?

Arcgis conflict in Liferay 7.0

I try to use Arcgis javascript API in Liferay 7.0 but it's fail. I think because Liferay 7.0 using requireJS for using javascript which conflict with dojo of Arcgis javascript API. I am using this code for implement Arcgis API:
<script type="text/javascript" src="<%=request.getContextPath() %>/js/arcgis_js_api/library/3.14/3.14/init.js"></script>
<script>
require([
"esri/map", "dojo/dom"
], function(Map, dom) {
var map = new esri.Map("map", {
basemap: "topo", //For full list of pre-defined basemaps, navigate to http://arcg.is/1JVo6Wd
center: [-122.45, 37.75], // longitude, latitude
zoom: 13
});
});
This is console log:
java.lang.IllegalArgumentException: Path esri/map.js does not start with a "/" character
java.lang.IllegalArgumentException: Path dojo.js does not start with a "/" character
This is javascript error:
Error: defineAlreadyDefined
Anyone has solution help me please. Thanks!
As you mention Liferay7 and your script tag makes use of <%=request.getContextPath()%>, you most likely have a mismatch between your portlet and the servlet API: request is not aware of any portlet context (but is still there, because JSPs have been designed for servlets and it's a mandatory object that's around, irritating a lot of people)
The URLs for your own Javascript files are just not relative to the context root, but Liferay makes them available on a different path. The easiest way, without messing around with this location, is to just mention the file you'd like to include in your portlet's configuration. In Liferay 7 OSGi portlet modules, it works like this (pseudocode, simplified)
#Component(
immediate = true,
property = {
"com.liferay.portlet.footer-portlet-javascript=/js/main.js",
"javax.portlet.display-name=My Portlet",
},
service = Portlet.class
)
In JSR-286 portlets packaged in WARs, you'd use the footer-portlet-javascript value in WEB-INF/liferay-portlet.xml.
Alternatively, use the header-* variant.
I personally prefer this way over memorizing how the path of resources is being constructed.
I use this code before implement arcgis (javascript):
if(typeof define !== "undefined" && typeof require !== "undefined"){
window.__define = window.define;
window.__require = window.require;
window.define = undefined;
window.require = undefined;}
It will be resolved.

Sometime getting file not found error for http://js.arcgis.com/3.9/js/dojo/jquery.js and ESRI map doesn't load

I'm using "js.arcgis.com/3.7" to implement esri maps. Sometime when i try to load page, I get below error and map doesn't load.
Error:
http://js.arcgis.com/3.9/js/dojo/jquery.js Failed to load resource: the server responded with a status of 404 (Not Found)
scriptError(…)(anonymous function) # init.js:40 (console.error(a))
Below is my code:
require([
"esri/map",
"esri/toolbars/draw",
"dojo/parser",
"esri/dijit/LocateButton",
"dijit/layout/BorderContainer",
"dijit/layout/ContentPane",
"dijit/form/Button",
"dijit/WidgetSet",
"dojo/domReady!"
], function (
Map,
Draw,
parser,
LocateButton
) {
parser.parse();
var opts = {
basemap: "topo",
center: [-103.08, 44.05],
zoom: 17
};
map = new Map("mapDiv", opts);
geoLocate = new LocateButton({
map: map
}, "LocateButton");
geoLocate.startup();
}
});
To clarify, you're using the 3.7 JS API, but are getting an error referencing the 3.9 API?
Moreover, is there a reason you're not using the newest (3.18) API?
I'd have to see a full stack trace, but it's difficult to see why Esri would be referencing a module from a newer version of its API. They've provided a resource for their 3.18 API on using jQuery with Dojo, if you decide to switch to the newest version:
https://developers.arcgis.com/javascript/3/jssamples/framework_jquery.html
That said, I don't see any references to jQuery in your code, so without more information I'd imagine the issue is a broken internal reference in their 3.7 API.