I was looking at the sample code for the tutorial at https://forge.autodesk.com/blog/custom-window-selection-forge-viewer-part-iii which is located at https://github.com/Autodesk-Forge/forge-rcdb.nodejs/blob/master/src/client/viewer.components/Viewer.Extensions.Dynamic/Viewing.Extension.SelectionWindow/Viewing.Extension.SelectionWindow.Tool.js as well as the documentation at https://developer.autodesk.com/en/docs/viewer/v2/reference/javascript/toolinterface/ --- Most of these functions are getting called properly in my tool such as handleSingleClick, handleMouseMove, handleKeyDown, and so on, but two of them are not getting hit -- handleButtonDown and handleButtonUp. I was using viewer version 3.3.x but I have updated to use 4.0.x thinking that that might help to resolve the problem, but the same issue occurs in both versions. Thanks for any help.
The following code block from theAutodesk.Viewing.ToolController#__invokeStack(), _toolStack stands for activated tools in the ToolController, the method stands for callback functions started with handle, i.e. handleSingleClick, handleMouseMove, handleKeyDown, handleButtonDown, handleButtonUp, etc.
for( var n = _toolStack.length; --n >= 0; )
{
var tool = _toolStack[n];
if( tool[method] && tool[method](arg1, arg2) )
{
return true;
}
}
Based on my experience, if there is a handle function such as handleButtonDown or handleButtonUp executed before your custom tools' and returned true, then your handles will never be called.
Fortunately, Forge Viewer (v3.2) starts invoking a priority mechanism for custom tools registered in ToolController. ToolController will use the priority number to sort the tools in it, and the priority number of each tool is 0 by default. You can override the priority to make your tools be hit before other tools like this way, to add a function getPriority() to return a number greater than 0:
this.getPriority = function() {
return 100;
};
I found out that when using ES6 and the class syntax, extending your tool from Autodesk.Viewing.ToolInterface will prevent the overrides to work properly, probably because it is not implemented using prototype in the viewer source code.
You can simply create a class and implement the methods that are of interest for your tool:
// KO: not working!
class MyTool extends Autodesk.Viewing.ToolInterface {
getName () {
return 'MyTool'
}
getNames () {
return ['MyTool']
}
handleButtonDown (event, button) {
return false
}
}
// OK
class MyTool {
getName () {
return 'MyTool'
}
getNames () {
return ['MyTool']
}
handleButtonDown (event, button) {
return false
}
}
Related
I am using Kotlin's html library kotlinx.html for dynamic html building.
I want to create a button which triggers a function when clicked. This is my current code:
class TempStackOverflow(): Template<FlowContent> {
var counter: Int = 1
override fun FlowContent.apply() {
div {
button(type = ButtonType.button) {
onClick = "${clicked()}"
}
}
}
fun clicked() {
counter++
}
}
This results in the following source code:
<button type="button" onclick="kotlin.Unit">testkotlin.Unit</button>
Which gives this error when clicked (from Chrome developer console):
Uncaught ReferenceError: kotlin is not defined at HTMLButtonElement.onclick
I have tried several approves, and search for a solution - but could not find the proper documentation.
I am not a Kotlin expert, but it's perfectly possible to write event handlers using kotlinx. Rather than:
onClick = "${clicked()}"
have you tried using this?
onClickFunction = { clicked() }
If you really need a bit on Javascript here, you can type this:
unsafe {
+"<button onClick = console.log('!')>Test</button>"
}
Good for debugging and tests, but not very nice for production code.
Unfortunately, you're completely missing the point of the kotlinx.html library. It can only render HTML for you, it's not supposed to be dynamic kotlin->js bridge, like Vaadin or GWT. So, you just set result of clicked function converted to String to onClick button's property, which is effective kotlin.Unit, because kotlin.Unit is default return value of a function if you not specify another type directly.
fun clicked() {
counter++
}
is the same as
fun clicked():Unit {
counter++
}
and same as
fun clicked():Kotlin.Unit {
counter++
}
So, when you set "${clicked()}" to some property it actually exec function (your counter is incremented here) and return Koltin.Unit value, which is becomes "Kotlin.Unit" string when it rendered inside "${}" template
Consider a following simple example, file country.gs
class Country { }
and file file subcountry.gs
class SubCountry extends Country{ }
function test(){}
Trying to run test() I get
ReferenceError: Country is not defined
If I join files or change loading order, it works fine.
Apparently, I don't want to be dependent on file load order, also clasp changes on push(sorting alphabetically), so it's definitely not a good way to rename files in order they should be compiled.
Is there an appropriate solution for this?
Example:
https://script.google.com/d/1Pipt3YN1FBGkbRRT2PyCHhugd-Xrv3zctIWYwX-cGnAjXfDckwOk7bJh/edit?usp=sharing
As written in the documentation,
This arrangement is identical to how browsers handle multiple tags in one HTML file.
Each file is like a new <script>file content </script> tag and they're added in the order they appear in Apps script editor. This is a problem only when you're using global variables. It's explicitly discouraged to use global variables.
Caution: It's not best practice to rely on a specific file parse order to avoid this issue. The sequence of script file parsing can change if script files are copied, removed, renamed, or otherwise rearranged. It's better to remove any global variable dependency on function calls if possible.
Classes are infact "special functions". You can always enclose the Class in a local scope and call, when needed as recommended in the documentation.
Snippet:
Just moving the calling function to local scope should work
/*subcountry.gs*/
function test(){
/*local scope*/class SubCountry extends Country{ }
}
To avoid declaring class in global scope as well:
/*country.gs*/
var Country;
function main(){
if (Country == undefined) Country = class Country { }
return Country;
}
/*subcountry.gs*/
function test(){
/*initialize class Country*/main()
/*local scope*/class SubCountry extends Country{ }
}
Building off the answer posted by TheMaster and the Bruce Mcpherson article shared by Alan Wells, you could try implementing your own require() function.
/* Code.gs */
function test() {
const SubCountry = require("SubCountry");
const x = new SubCountry();
}
/* SubCountry.gs */
function SubCountry() {
const Country = require("Country");
return class SubCountry extends Country {};
}
/* Country.gs */
function Country() {
return class Country {};
}
/* Require.gs */
function require(moduleName) {
const modules = {
Country: Country,
SubCountry: SubCountry,
};
return modules[moduleName]();
}
Alternatively, you could apply a more direct approach without the use of require(), but I find this to be slightly less intuitive.
/* Code.gs */
function test() {
const x = new (SubCountryClass())();
}
/* SubCountry.gs */
function SubCountryClass() {
return class SubCountry extends CountryClass() {};
}
/* Country.gs */
function CountryClass() {
return class Country {};
}
All files above, for both approaches, are intentionally presented and loaded in an order that would cause a ReferenceError if declaring the classes globally. So this should be fully independent of load order.
I'll probably go with one of solutions described here
TypeScript classes order in Google AppScript Project
using clasp and it's filePushOrder option
{
"scriptId":"1Pipt3YN1FBGkbRRT2PyCHhugd-Xrv3zctIWYwX-cGnAjXfDckwOk7bJh",
"filePushOrder": [
"country.gs",
"subcountry.gs"
]
}
Author example
https://github.com/PopGoesTheWza/clasp-filePushOrder
I enforces me to use clasp, but at least it's easy to maintain.
I'm looking to hook-up sort events performed on ng2-smart-table. Followed https://akveo.github.io/ng2-smart-table/#/documentation, I see bunch of events that are exposed like rowSelect, mouseover etc but I don't see sort events published/emitted by the library. I'm thinking of changing Ng2SmartTableComponent and emit an event when (sort) is called internally. May I know if anyone did it already or is there a hack I can rely upon.
The source of the sort in ng2-smart-table is shown on GitHub (link to code).
If you want to change the compare-Function (as used by default) you can add your own custom function in your ng2-smart-table-configuration:
columns: {
group_name: {
title: 'Groupname',
compareFunction(direction: any, a: any, b: any) => {
//your code
}
}
}
I was searching for an event to sort my data remotely and I have found a solution. Also I have some logic for page change event (remote paging). Here is what works for me.
ts
source: LocalDataSource = new LocalDataSource();
ngOnInit() {
this.source.onChanged().subscribe((change) => {
if (change.action === 'sort') {
this.sortingChange(change.sort);
}
else if (change.action === 'page') {
this.pageChange(change.paging.page);
}
});
}
html
<ng2-smart-table [settings]="settings" [source]="source"></ng2-smart-table>
This solution won't replace custom logic but it might help you solve your problem.
Consider this element (minimal for the purpose of the question) :
class MyCountDown extends Polymer.Element
{
static get is () { return 'my-count-down'; }
static get properties ()
{
return {
time: { /* time in seconds */
type: Number,
observer: '_startCountDown'
},
remains: Number
}
}
_startCountDown ()
{
this.remains = this.time;
this.tickInterval = window.setInterval(() => {
this.remains--;
if (this.remains == 0) {
console.log('countdown!');
this._stopCountDown();
}
}, 1000);
}
_stopCountDown () {
if (this.tickInterval) {
window.clearInterval(this.tickInterval);
}
}
}
customElements.define(MyCountDown.is, MyCountDown);
If I get one instance and set the property time,
let MyCountDown = customElements.get('my-count-down');
let cd = new MyCountDown();
cd.time = 5;
the property time changes but the observer and the _startCountDown() function is not called. I believe Polymer is waiting for the Instance to be attached to the DOM because in fact when I appendChild() this element to the document the count down starts and after 5 seconds the console logs 'countdown!' as expected.
My goal is to execute this lifecycle without attaching anything to the document because the instances of MyCountDown are not always attached to the view but/and they need to be live-code between the different components of my web application.
One solution is to attach the new MyCountDown instances to an hidden element of the dom to force the Polymer lifecycle but I think this is not so intuitive.
I don't know the exact place to call, but the problem you have is that the property assessors are not in place.
I think you might get a clue from this talk https://www.youtube.com/watch?v=assSM3rlvZ8 at google i/o
call this._enableProperties() in a constructor callback?
I am developing NPAPI Plugin for Firefox on windows. here is the my java script:
document.addEventListener('load', documentLoad, true);
function loadPlugin(doc)
{
var objWebMon = doc.getElementById("my_firefox");
if(!objWebMon)
{
var objWebMonEmbed = doc.createElement('embed');
objWebMonEmbed.setAttribute('id', 'my_firefox');
objWebMonEmbed.setAttribute('type', 'application/npplugin');
objWebMonEmbed.setAttribute('style', 'height: 10px; width:10px; display:block;');
if(doc.body)
{
doc.body.insertBefore(objWebMonEmbed, doc.body.firstChild);
}
}
}
function documentLoad(event) {
try
{
var doc = event.originalTarget; // doc is document that triggered "onload" event
loadPlugin(doc);
var myplugin = doc.getElementById('my_firefox');
if(myplugin)
{
myplugin();
myplugin.myAction();
}
} catch(err)
{
}
}
as I am calling myplugin()
bool ScriptablePluginObject::InvokeDefault(const NPVariant *args, uint32_t argCount, NPVariant *result)
gets called sucessfully but on calling function myplugin.myAction()
bool ScriptablePluginObject::Invoke(NPIdentifier name, const NPVariant *args,
uint32_t argCount, NPVariant *result)
function doesn't called. I have declared myAction inside ScriptablePluginObject::HasProperty(NPIdentifier name) even HasProperty method is not getting called.
Inside catch block i am getting this error. TypeError: fasso.myAction is not a function.
Here are a couple of things to try:
Use an object tag instead of an embed -- I've had more consistent success with object tags, despite the wide popularity of using embed
Never ever ever set the type of an object or embed tag before you add it to the DOM -- doing so causes it to instantiate the plugin and then puts it in a kinda weird state when it gets moved. I don't think this is causing your issue this time, but it's worth trying.
You may need a slight delay between inserting hte plugin into the DOM and using it. Try adding a setTimeout with a delay of 50ms and accessing the plugin in the callback function.
Honestly, #3 is the one I think most likely will make a difference, but I present the other two as they have bitten me on weird things in the past. Good luck!