How to 'import' a third party Javascript file into IntelliJ so that I can refer to it in a Typescript file - node.js

I am trying to write Typescript in IntelliJ and do not know how to tell IntelliJ to 'import' some third party Javascript files. IntelliJ (or is it Node.JS?) gives the following complaint:
C:/Temp/Typescript Example Project/ts/FinancialService.ts(2,17): error TS2095: Could not find symbol 'com'.
C:/Temp/Typescript Example Project/ts/FinancialService.ts(4,31): error TS2095: Could not find symbol 'com'.
I would like to 'import' Thirdparty.Calculator.js:
var com = com || {};
com.thirdparty = com.thirdparty || {};
com.thirdparty.Calculator = function() {
this.add = function(a, b) {
return a + b;
};
this.square = function(n) {
return n*n;
};
};
This is what FinancialService.ts looks like:
class FinancialService {
calculator: com.thirdparty.Calculator;
constructor() {
this.calculator = new com.thirdparty.Calculator();
}
calculateStuff(a: number) {
return this.calculator.square(a);
}
}
IntelliJ appears to transpile the Typescript as the following works and the correct values are logged to the console:
<html>
<head>
<script src="js/Thirdparty.Calculator.js"></script>
<script src="ts/FinancialService.js"></script>
<script>
var cal = new com.thirdparty.Calculator();
console.log("Calculator.square() is " + cal.square(9));
var fs = new FinancialService();
console.log("FinancialService.calculateStuff() is " + fs.calculateStuff(4));
</script>
</head>
<body>
</body>
</html>
How can I configure my project so that IntelliJ knows about Thirdparty.Calculator.js?

You could add Thirdparty.Calculator.d.ts to your project for TypeScript compilation purposes:
declare module com.thirdparty {
export class Calculator {
add(a: number, b: number) : number;
square(n: number) : number;
}
}
This would obviously need to grow with the third party library.
For very little extra effort, you could just convert it to TypeScript...
module com.thirdparty {
export class Calculator {
add = function(a: number, b: number) {
return a + b;
};
square(n: number) : number {
return n*n;
}
}
}

Related

Can't get html element using js file in SPFX

I am trying to build dynamic content from a SharePoint list using SPFX. I'd like to use jQuery to build an accordion view of the data. The issue is that I can't even seem to get the element once the page is rendered.
In my code I am requiring a file called ota.js with the following code:
console.log('Start');
function otaExpand(){
console.log('otaExpand Function Called');
let spListContainer = document.getElementById('spListContainer');
console.log(spListContainer);
}
window.addEventListener("load", otaExpand());
In my ts file this is my render method:
public render(): void {
this.domElement.innerHTML = `
<div>
<div id="spListContainer">TEST</div>
</div>
`;
//this._renderListAsync();
//($('.accordion', this.domElement) as any).accordion();
}
When I review the console, I get my messages, but the element itself comes back as null.
console.log
I am using SharePoint 2019 on premise with the following configuration.
+-- #microsoft/generator-sharepoint#1.10.0
+-- gulp-cli#2.3.0
`-- yo#2.0.6
node --version
v8.17.0
I should also mention I am using TypeScript with no JavaScript framework.
Does anyone know why I can't access this element from my js file?
Thanks!
My overall goal is to call list data and apply an accordion style to it (https://jqueryui.com/accordion), but I can't even get passed capturing the element to change it.
I've tried calling my code from a js file as well as trying to put the code directly in the html. Neither worked.
OK, I finally figured out what I was doing wrong. I was calling my jQuery in the render() method rather than in _renderList where this.domElement actually makes sense.
Here's my code in case anyone wants to avoid the pain I put myself through. This allows you to specify a list in the site and you just need to add the fields you want to display.
import { Version } from '#microsoft/sp-core-library';
import {
BaseClientSideWebPart,
IPropertyPaneChoiceGroupOption,
IPropertyPaneConfiguration,
PropertyPaneChoiceGroup,
PropertyPaneCustomField,
PropertyPaneTextField
} from '#microsoft/sp-webpart-base';
import { escape } from '#microsoft/sp-lodash-subset';
import styles from './GetSpListItemsWebPart.module.scss';
import * as strings from 'GetSpListItemsWebPartStrings';
import {
SPHttpClient,
SPHttpClientResponse
} from '#microsoft/sp-http';
import * as jQuery from 'jquery';
import 'jqueryui';
import { SPComponentLoader } from '#microsoft/sp-loader';
import PropertyPane from '#microsoft/sp-webpart-base/lib/propertyPane/propertyPane/PropertyPane';
export interface IGetSpListItemsWebPartProps {
title: string;
description: string;
listField: string;
}
export interface ISPLists {
value: ISPList[];
}
export interface ISPList {
ID: string;
Title: string;
Website: {
Description : string,
Url : string
};
Description : string;
}
export default class GetSpListItemsWebPart extends BaseClientSideWebPart<IGetSpListItemsWebPartProps> {
private _getListData(): Promise<ISPLists> {
return this.context.spHttpClient.get(this.context.pageContext.web.absoluteUrl + "/_api/web/lists/GetByTitle('" + this.properties.listField + "')/Items",SPHttpClient.configurations.v1)
.then((response: SPHttpClientResponse) => {
return response.json();
});
}
private _renderListAsync(): void {
this._getListData()
.then((response) => {
this._renderList(response.value);
})
.catch(() => {});
}
private _renderList(items: ISPList[]): void {
let listData = `
<h1>${this.properties.title}</h1>
<h2>${this.properties.description}</h2>
<div class="accordion">
`;
items.forEach((item: ISPList) => {
let Description : string;
item.Description ? Description = item.Description : Description = "";
listData += `
<h3> ${item.Title}</h3>
<div>
<table>
<tr>
<td>OTA URL</td>
<td>${item.Website.Description}</td>
</tr>
<tr>
<td>Description</td>
<td>${Description}</td>
</tr>
</table>
</div>
`;
});
listData += '</div>';
this.domElement.innerHTML = listData;
const accordionOptions: JQueryUI.AccordionOptions = {
animate: true,
collapsible: true,
icons: {
header: 'ui-icon-circle-arrow-e',
activeHeader: 'ui-icon-circle-arrow-s'
}
};
jQuery('.accordion', this.domElement).accordion(accordionOptions);
}
public render(): void {
this._renderListAsync();
}
protected getPropertyPaneConfiguration(): IPropertyPaneConfiguration {
return {
pages: [
{
header: {
description: strings.PropertyPaneDescription
},
groups: [
{
groupName: strings.BasicGroupName,
groupFields: [
PropertyPaneTextField('title',{
label: strings.TitleFieldLabel
}),
PropertyPaneTextField('description', {
label: strings.DescriptionFieldLabel
}),
PropertyPaneTextField('listField', {
label: strings.ListFieldLabel
})
]
}
]
}
]
};
}
public constructor() {
super();
SPComponentLoader.loadCss('//code.jquery.com/ui/1.11.4/themes/smoothness/jquery-ui.css');
}
}
Your code from the "ota.js" file is probably called before your HTML is initialized (i.e. before the "render()" function is executed). To make sure this is the case, you could add log to the "render()" function to see when it's called.
In other words, "window.load" event happens long before "render()" function is called. This is how web parts are loaded - dynamically after full load of the page. Or "window.load" does not happen at all - web parts may be loaded by the user when using the page designer, i.e. without page reload.
To fix the issue, you should get the element after it's created, i.e. after the "render()" function creates the element you are trying to get.

appending exports to a library

I am trying to append modules to the THREE.js library for example...
import * as THREE from 'three'// append the following routine to three when I import this file into app.js
export THREE.RenderPass = function (scene, camera, overrideMaterial, clearColor, clearAlpha) {
THREE.Pass.call(this)
this.scene = scene
this.camera = camera
this.overrideMaterial = overrideMaterial
this.clearColor = clearColor
this.clearAlpha = (clearAlpha !== undefined) ? clearAlpha : 0
this.clear = true
this.needsSwap = false
}
export THREE.RenderPass.prototype = Object.assign(Object.create(THREE.Pass.prototype), {
constructor: THREE.RenderPass,
render: function (renderer, writeBuffer, readBuffer, delta, maskActive) {
var oldAutoClear = renderer.autoClear
renderer.autoClear = false
this.scene.overrideMaterial = this.overrideMaterial
var oldClearColor, oldClearAlpha
if (this.clearColor) {
oldClearColor = renderer.getClearColor().getHex()
oldClearAlpha = renderer.getClearAlpha()
renderer.setClearColor(this.clearColor, this.clearAlpha)
}
renderer.render(this.scene, this.camera, this.renderToScreen ? null : readBuffer, this.clear)
if (this.clearColor) {
renderer.setClearColor(oldClearColor, oldClearAlpha)
}
this.scene.overrideMaterial = null
renderer.autoClear = oldAutoClear
}
})
these routines have been ingrained into my solution which was previously raw java-script. I have moved it to webpack which bundles and parses it all requiring imports and exports. Previously I would just call these files after importing the library through a cdn and it would append to it. It appears its done differently in nodejs, and I'm unsure how to do it.
I need these modules to be accessible from the main library, they are used to enable post processing in three.js.
previously I did...
<!-- scripts appended based on the order I placed the script tags in the html, I bundle wiht webpack so this seems like a rather bad solution to continue when its already being 'bundled'-->
<script src="EffectComposer.js"></script>
<script src="CopyShader.js"></script>
<script src="RenderPass.js"></script>
<script src="MaskPass.js"></script>
<script src="ShaderPass.js"></script>

How to get char code of fontawesome icon?

I'd like to use fontawesome icons in SVG scope. I cannot achieve it in common way, but I can add <text> element containing corresponding UTF-8 char and with font set to fontawesome, like that:
<text style="font-family: FontAwesome;">\uf0ac</text>
To make it clear I wrote a switch for getting useful icons:
getFontAwesomeIcon(name) {
switch (name) {
case 'fa-globe':
return '\uf0ac'
case 'fa-lock':
return '\uf023'
case 'fa-users':
return '\uf0c0'
case 'fa-ellipsis-h':
return '\uf141'
default:
throw '# Wrong fontawesome icon name.'
}
}
But of course that's ugly, because I must write it myself im my code. How can I get these values just from fontawesome library?
You can avoid producing such a list and extract the information from the font-awesome stylesheet on the fly. Include the stylesheet and set the classes like usual, i. e.
<tspan class="fa fa-globe"></tspan>
and you can do the following:
var icons = document.querySelectorAll(".fa");
var stylesheet = Array.from(document.styleSheets).find(function (s) {
return s.href.endsWith("font-awesome.css");
});
var rules = Array.from(stylesheet.cssRules);
icons.forEach(function (icon) {
// extract the class name for the icon
var name = Array.from(icon.classList).find(function (c) {
return c.startsWith('fa-');
});
// get the ::before styles for that class
var style = rules.find(function (r) {
return r.selectorText && r.selectorText.endsWith(name + "::before");
}).style;
// insert the content into the element
// style.content returns '"\uf0ac"'
icon.textContent = style.content.substr(1,1);
});
My two answers for two approaches to the problem (both developed thanks to ccprog):
1. Setting char by class definition:
In that approach we can define element that way:
<text class="fa fa-globe"></text>
And next run that code:
var icons = document.querySelectorAll("text.fa");
// I want to modify only icons in SVG text elements
var stylesheets = Array.from(document.styleSheets);
// In my project FontAwesome styles are compiled with other file,
// so I search for rules in all CSS files
// Getting rules from stylesheets is slightly more complicated:
var rules = stylesheets.map(function(ss) {
return ss && ss.cssRules ? Array.from(ss.cssRules) : [];
})
rules = [].concat.apply([], rules);
// Rest the same:
icons.forEach(function (icon) {
var name = Array.from(icon.classList).find(function (c) {
return c.startsWith('fa-');
});
var style = rules.find(function (r) {
return r.selectorText && r.selectorText.endsWith(name + "::before");
}).style;
icon.textContent = style.content.substr(1,1);
});
But I had some problems with that approach, so I developed the second one.
2. Getting char with function:
const getFontAwesomeIconChar = (name) => {
var stylesheets = Array.from(document.styleSheets);
var rules = stylesheets.map(function(ss) {
return ss && ss.cssRules ? Array.from(ss.cssRules) : [];
})
rules = [].concat.apply([], rules);
var style = rules.find(function (r) {
return r.selectorText && r.selectorText.endsWith(name + "::before");
}).style;
return style.content.substr(1,1);
}
Having that funcion defined we can do something like this (example with React syntax):
<text>{getFontAwesomeIconChar('fa-globe')}</text>

can't test ember component that appends a div to the dom

I have a ember-cli-addon that adds a component which appends a div with a specific class to the consuming application. I'm trying to test this integration and having difficulty to setup the test.
I have tried to unit test the component as well but that doesn't work quite as expected. Here's what I've tried:
I've copied the component from my addon directory to tests/dummy/components/jquery-backstretch.js to make it available to the dummy test application:
jquery-backstretch.js
import Ember from 'ember';
export default Ember.Component.extend({
tagName: 'jquery-backstretch',
image: null,
selector: 'body',
fade: 0,
duration: 5000,
centeredX: true,
centeredY: true,
setupJquerybackstretch: function() {
var image = this.get('image');
if (! Ember.isEmpty(image)) {
var options = {
fade: this.get('fade'),
centeredX: this.get('centeredX'),
centeredY: this.get('centeredY')
};
var jqbsImage;
if (Ember.typeOf(image) === 'string') {
jqbsImage = 'assets/' + image;
} else if (Ember.isArray(image)) {
options.duration = this.get('duration');
jqbsImage = image.map(function(img) {return 'assets/' + img;});
} else {
Ember.Logger.error('Ember JQuery-Backstretch: Unsupported "image" format.');
}
Ember.$(this.get('selector')).backstretch(jqbsImage, options);
} else {
Ember.Logger.error('Ember JQuery-Backstretch: image not supplied.');
}
}.on('didInsertElement'),
teardownJquerybackstretch: function() {
Ember.$(this.get('selector')).backstretch('destroy');
}.on('willDestroyElement')
});
this causes the component to append the img to the body of the test page and not to #ember-testing-container, changing the selector to #ember-testingn-container puts the img in the right place but the test can't find it:
tests/acceptance/jquery-backstretch.js
import Ember from 'ember';
import {
module,
test
} from 'qunit';
import startApp from '../../tests/helpers/start-app';
var application;
module('Acceptance: JqueryBackstretch', {
beforeEach: function() {
application = startApp();
},
afterEach: function() {
// Ember.run(application, 'destroy');
}
});
test('backstretch added to body tag', function(assert) {
visit('/');
andThen(function() {
assert.equal(find('.backstretch > img').length, 1, 'Backstretch found');
});
});
application.hbs
<h2 id="title">Welcome to Ember.js</h2>
{{jquery-backstretch image="img/emberjs.png"}}
{{outlet}}
the test is not passing, it can't find the image, I also tried to test the component and append it to the DOM then test to see if it's in the DOM but that didn't yield better results.
How can I test this please?

Toastr is not displaying the way it should

toastr is showing an odd behavior -- it's being displayed in a rather ugly way, and I am not overriding anything. No options are given on how to style, but still I am getting this ugly notification.
This is what it looks like:
I am pulling toastr through requireJS; I don't know if that even matters.
logger.js
define(['durandal/system', 'toastr'], function (system, toastr) {
var logger = {
log: log,
logError: logError
};
return logger;
function log(message, data, source, showToast) {
logIt(message, data, source, showToast, 'info');
}
function logError(message, data, source, showToast) {
logIt(message, data, source, showToast, 'error');
}
function logIt(message, data, source, showToast, toastType) {
source = source ? '[' + source + '] ' : '';
if (data) {
system.log(source, message, data);
} else {
system.log(source, message);
}
if (showToast) {
if (toastType === 'error') {
toastr.error(message);
} else {
toastr.info(message);
}
}
}
});
main.js
requirejs.config({
baseUrl: '../Scripts',
paths: {
'services': '../App/services',
'viewmodels': '../App/viewmodels',
'views': '../App/views',
'config': '../App/config',
'durandal': 'durandal',
'plugins': 'durandal/plugins',
'transitions': 'durandal/transitions',
'text': 'text',
'toastr': 'toastr'
}
});
define('jquery', function () { return jQuery; });
define('knockout', ko);
define('main', ['durandal/system', 'durandal/app', 'durandal/viewLocator', 'plugins/router', 'services/logger'], function (system, app, viewLocator, router, logger) {
//>>excludeStart("build", true);
system.debug(true);
//>>excludeEnd("build");
app.title = 'Prepare to die';
app.configurePlugins({
router: true,
dialog: true,
widget: true
});
app.start().then(function () {
// Router will use conventions for modules
// assuming viewmodels/views folder structure
router.makeRelative({ moduleId: 'viewmodels' });
// Replace 'viewmodels' in the moduleId with 'views' to locate the view.
// look for partial views in a 'views' folder in the root.
viewLocator.useConvention();
// Show the app by setting the root view model for our application with a transition.
app.setRoot('viewmodels/shell', 'entrance');
// Override bad route behavior to write to
// console log and show error toast
router.handleInvalidRoute = function (route, params) {
logger.logError('No route found', route, 'main', true);
};
});
});
shell.js
define(['durandal/system', 'services/logger', 'plugins/router', 'config'],
function (system, logger, router, config) {
var shell = {
activate: activate,
router: router
};
return shell;
function activate() {
logger.log('Application is Loaded!', null, system.getModuleId(shell), true);
router.map(config.routes).buildNavigationModel();
return router.activate();
}
});
shell.html
<div>
<header>
<!-- ko compose: {view: 'navigation'} -->
<!-- /ko -->
</header>
<section id="content" class="main container-fluid">
<!-- ko compose: {model: router.activeItem, afterCompose: router.afterCompose} -->
<!-- /ko -->
</section>
</div>
Just as a sidebar, we use toastr under Durandal and I know from John Papa's writings that he feels that third-party frameworks should be loaded globally, while our own modules should be loaded modularly. Just food for thought. I can tell that switching to a global model for third-party frameworks eliminated a lot of esoteric issues.
A quick work-around fix is to do the following:
toastr.options.toastClass = 'toastr';

Resources