krpano: custom plugin action not known / registered? - krpano

I try to test a very basic custom plugin action, but I seem to have made a mistake.
This is my plugin:
function krpanoplugin() {
var local = this; // save the 'this' pointer from the current plugin object
var krpano = null; // the krpano and plugin interface objects
var plugin = null;
var xml_value = 100.0; // the value for a custom xml attribute
// registerplugin - startup point for the plugin (required)
// - krpanointerface = krpano interface object
// - pluginpath = the fully qualified plugin name (e.g. "plugin[name]")
// - pluginobject = the xml plugin object itself
local.registerplugin = function(krpanointerface, pluginpath, pluginobject) {
// get the krpano interface and the plugin object
krpano = krpanointerface;
plugin = pluginobject;
// first - say hello
krpano.trace(1, "Bridge Plugin loaded: [" + plugin.name + "]");
// add plugin action (the attribute needs to be lowercase!)
plugin.jsbmodal = action_jsbmodal;
};
function action_jsbmodal() {
console.log(arguments);
krpano.trace(arguments);
}
}
But if I try to execute the action onClick at a hotspot like this:
<hotspot name="hotspot_171" style="link_hs_sm" url="%CURRENTXML%/add_hotspot/picture/icon-text.png" distorted="true" alpha="1" capture="true" depth="1000" flying="0" handcursor="true" zorder="5" zoom="false"
ath="160.8416" atv="-37.4461" width="50" height="prop" rx="0" ry="0" rz="0" ox="0" oy="0" rotate="0" scale="1" edge="center"
install_onclick="3" name_hs="hotspot_171" scene_EL="scene_8" info_Ah="add_hotspot/text/leipzig-test.html" fov_EL="140" enabled2="true" visible2="true" drag_hs="true"
onclick="jsbmodal('om')"
/>
I only get the following message:
WARNING: Unknown action: jsbmodal
I have registered the plugin like this:
<plugin name="jsbModal" url="../jsbModalPlugin.js" keep="true" preload="true" />
Can you see where I made the mistake?

According to https://krpano.com/forum/wbb/index.php?page=Thread&postID=78841 the probably cleanest solution is to use the plugin action syntax in the xml:
plugin[YOUR_PLUGIN_NAME].yourAction()
Which is in my case:
<hotspot name="hotspot_171" [...]
onclick="plugin[jsbmodal].jsbmodal('om')"
/>

Related

Aurelia: Is there any option / decorator to restrict an attribute to a specific element

Hi I have created a custom element named as 'panel' and a custom attribute 'panel-type'. I want to restrict this 'panel-type' can be used only in 'panel' element. Is there any way to do that?
<panel value.bind='panel' panel-type='default'></panel>
--should work. But no other element can use 'panel-type'. like-
<some-other-tag panel-type='default'></some-other-tag>
--shouldn't work
Without seeing the code of your Custom Attribute - I can't tailor this to your specific needs - but it's easily possible;
In your Custom Attribute you have access to the the Element property - which is the physical element you've attached the attribute to. Therefore, you can simply use native JavaScript to get the elements tag name;
import {inject, LogManager} from "aurelia-framework";
#inject(Element)
export class panelTypeCustomAttribute {
allowedElememt = false;
constructor(Element) {
this.element = Element;
if(this.element.tagName == "PANEL") {
this.allowedElement = true;
} else {
LogManager.getLogger('testCustomAttribute').warn("The panel-type custom attribute can only be used on <panel /> elements");
}
}
foo() {
// Then you can check for the allowedElement flag in any of your methods;
if(this.allowedElement) {
// Do your thing
}
}
}

CRM 2013 custom notification in the entity form

I need to show custom warning message in entity from if specific field is not empty (on change).
I wrote in a different company global JS file the code that shows the message:
addNotification: function (message) { //Adds a warning message on the top of the entity Form
var notificationHTML = '<DIV class="Notification"><TABLE cellSpacing="0" cellPadding="0"><TBODY><TR><TD vAlign="top"><IMG class="ms-crm-Lookup-Item" alt="" src="/_imgs/error/notif_icn_crit16.png" /></TD><TD><SPAN>' + message + '</SPAN></TD></TR></TBODY></TABLE></DIV>';
var notificationsArea = document.getElementById('Notifications');
if (notificationsArea == null) return;
notificationsArea.innerHTML += notificationHTML;
notificationsArea.style.display = 'block';
},
and in my entity JS file I wrote this:
function checkIfAssignToIsEmpty() {
var AssignTo = Xrm.Page.getAttribute('el_assign_to').getValue();
if (AssignTo != null)
newNotification();
}
function newNotification() {
var assignToVal = Xrm.Page.getAttribute("el_assign_to").getValue();
var newNotification = GlobalFunction.addNotification('assiignToVal + The Task will assign to');
}
I get error message after I choose value in my field
Object doesn't support property or method 'addNotification'
Can you please help?
You can use the supported methods for notification
Xrm.Page.ui.setFormNotification(message, level, uniqueId);
and
Xrm.Page.ui.clearFormNotification(uniqueId);
There are also notification for the controls. You can find several examples here:
http://garethtuckercrm.com/2013/10/17/crm-2013-new-features-javascript-notifications/

asp.net mvc spa durandaljs date format not working with Knockout

I'm exploring durandaljs for asp.net mvc SPA. I'm using APS.net MVC4, Durandaljs, Knockoutjs, breeze, moment and other libs found under hottowel SPA sample.
I have a client view which is bound with DOB, DateTime.
<td colspan="2">
<span id="dob" data-bind="text: DOB"></span>
</td>
and my ViewModel contains code
vm.studentProfile().DOB(moment(vm.studentProfile().DOB()).format('L'));
logger.log(vm.studentProfile().DOB(), null, system.getModuleId(vm), false);
Above code actually comes from querySucceeded. i.e
return manager
.executeQuery(query)
.then(querySucceeded)
.fail(queryFailed);
This supposed to be working as I've achieved this already for some other fields but in case of DateTime KnockoutOut doesn't update GUI whereas I can see the UPDATED format date in console log. Can somebody tell me what am I missing here. thanks in advance.
The problem may lie with the fact that DOB is a MomentJs date, not a JavaScript Date or string. You most likely need to add a custom binding handler for displaying these dates, such as for example:
ko.bindingHandlers.moment = {
update: function(element, valueAccessor) {
var value = valueAccessor();
var formattedValue = ko.utils.unwrapObservable(value).format('LLLL');
$(element).text(formattedValue);
}
};
Now, instead of using the "text" binding handler, use the "moment" binding handler like this:
<span id="dob" data-bind="moment: DOB"></span>
Edit: added an example of adding custom plugins using AMD modules with RequireJS:
require(['jquery', 'json2', 'sammy', 'amplify', 'bootstrap', 'moment', 'toastr', 'showdown', 'markdowneditor', 'spin'],
function($){
// Require that plugins be loaded, after the prerequisite libraries
// We load the plugins here and now so that we don't have to
// name them specifically in the modules that use them because
// we don't want those modules to know that they use plugins.
requirejs([
'jquery.ui', // jquery plugin
'jquery.mockjson', // jquery plugin
'jquery.tmpl', // jquery plugin
],
function () {
require(['ko'],
function(ko) {
// ensure KO is in the global namespace ('this')
if (!this.ko) {
this.ko = ko;
};
requirejs([
'libs/knockout.binding.handlers', // Knockout custom binding handlers
'libs/knockout.extenders', // Knockout custom binding handlers
'libs/bootstrap.extenders', // Knockout custom binding handlers
],
// Plugins generally don't return module objects
// so there would be point in passing parameters to the function
function () {
require(['app'], function(App) {
App.initialize();
});
}
);
}
);
}
);
}
);
What about just making a ko.Computed like the following
vm.studentProfileFormatted = ko.computed({
read: function () {
return moment(vm.studentProfile().DOB()).calendar();
},
write: function (value) {
var time = moment(value, "MM-DD-YYYY").toJSON();
vm.studentProfile(time);
},
owner: vm
});
And then calling studentProfileFormatted in your view.

fairytale about mvc, require.js and angular. is there happily ever after?

So. Once upon a time there were four magical creatures: asp.net mvc, require.js and angular. And one wise wizard decided to put them in the same house, and let for every single view of asp.net to have its own "code-behind" javascript file;
first he added to the _Layout.cshtml
<script data-main="/main" src="~/Scripts/require.js"></script>
and then he created main.js in the root:
require.config({
baseUrl: "/Scripts/",
paths: {
'jquery': 'jquery-1.9.1.min',
'jquery-ui': 'jquery-ui-1.10.2.custom.min',
'angular': 'angular.min',
'ng-grid': 'ng-grid-2.0.2.debug'
},
shim: {
'jquery': { exports: "$" },
'underscore': { exports: "_" },
'jquery-ui': ['jquery'],
},
});
// Standard Libs
require(['jquery','jquery-ui','underscore','angular']);
nothing fancy and magical yet. But then he created an html helper as such:
public static MvcHtmlString RequireJs(this HtmlHelper helper)
{
var controllerName = helper.ViewContext.RouteData.Values["Controller"].ToString(); // get the controllername
var viewName = Regex.Match((helper.ViewContext.View as RazorView).ViewPath, #"(?<=" + controllerName + #"\/)(.*)(?=\.cshtml)").Value; //get the ViewName - extract it from ViewPath by running regex - everything between controllerName +slash+.cshtml should be it;
// chek if file exists
var filename = helper.ViewContext.RequestContext.HttpContext.Request.MapPath("/Scripts/views/" + controllerName.ToLower() + "-" +
viewName.ToLower()+".js");
if (File.Exists(filename))
{
return helper.RequireJs(#"views/" + controllerName.ToLower() + "-" + viewName.ToLower());
}
return new MvcHtmlString("");
}
public static MvcHtmlString RequireJs(this HtmlHelper helper, string module)
{
var require = new StringBuilder();
require.AppendLine(" <script type=\"text/javascript\">");
require.AppendLine(" require(['Scripts/ngcommon'], function() {");
require.AppendLine(" require( [ \"" + module + "\"] );");
require.AppendLine(" });");
require.AppendLine(" </script>");
return new MvcHtmlString(require.ToString());
}
and then he could use it in _Layout.cshtml just like that:
#Html.RequireJs()
and if you were listening carefully to the story, you probably noticed that there was also Scripts/ngcommon.js file to manually bootstrap angular.js and have commonly used angular directives and services
require(['angular', 'jquery'], function() {
angular.module("common",[]).directive('blabla', function() {
return {
restrict: 'A',
scope: { value: "#blabla" },
link: function(scope, element, attrs) { }
}
});
//manually bootstrap it to html body
$(function(){
angular.bootstrap(document.getElementsByTagName('body'), ["common"]);
});
});
And here comes the magic: from now on if it was a javascript file in \Scripts\views named as controllerName-viewName.js as home-index.js for Home\Index.cshtml it would be automagically picked up by require.js and loaded. Beautiful isn't it?
But then the magician thought: What If I need to load something else (like ng-grid) and that something should not be injected into common angular module because not all the pages will be using it. Of course he could always manually bootstrap another module into a page element in each code-behind javascript where he needed, but he's not wise enough to find answer to the question:
Is it possible to inject some angular.js component (like ng-grid) directly into a controller, without having it as a part of the app module?
If I understand magician's idea right, then it is possible to go on by splitting your application into sub-modules being defined as a collection of components.
It will work if he sets up dependencies for main myApp module like:
var myApp = angular.module('myApp', ['Constants', 'Filters', 'Services', 'Directives', 'Controllers']);
myApp.Constants = angular.module('Constants', []);
myApp.Controllers = angular.module('Controllers', []);
myApp.Filters = angular.module('Filters', []);
myApp.Services = angular.module('Services', []);
myApp.Directives = angular.module('Directives', []);
Then each of sub-modules: Services etc. - can be extended with single component, like:
myApp.Controllers.controller('MyController', function () {});
myApp.Services.factory('myService', function () {});
myApp.Directives.directive('myDirective', function () {});
myApp.Filters.filter('myFilter', []);
myApp.Constants.constant('myConstant', []);
That way main application module is loaded with several sub-modules, but each structure is not important. It makes possible to include individual controllers, services, directives and filters on each page served from back-end - magician just needs to be sure that all needed dependencies are loaded.
DI is the magic key for having separate angular codebehind in MVC views.
You don't even need the requirejs at all, because angular is a dependency injector and module loader by nature, angular.bootstrap is the magic place to start.
So let wizard became more powerfull with the spell - $inject.
var TmplController = function($scope, $compile, $http... // any module itself
{
this.parts = ['legs','arms','head'];
$scope.dynamicPageTemplate = function($compile)
{
$compile('<div><p ng-repeat="each in parts">{{each}}</p></div>' )( $scope );
}
}
TmplController.$inject = ['$scope','$comple', '$http']; //try legs or head
refer complete annotated source of angular-scenario.js from https://github.com/angular/bower-angular-scenario, and you will find how to inject code with define manner helpers.

Does changing dojo.dnd.Source.checkAcceptance to true affect type checking?

I've got a working DnD implementation however I've run into a snag. It seems that if I set dojo.dnd.Source.checkAcceptance to true, the Source container I do that to stops checking the dndType, it accepts everything.
I'm checking if there is a node present in the dojo.dnd.Source container, if there is I want to disable dropping. I do this twice because if content is already present when the page loads, we want to disable dropping additional content there and only allow the Source container to contain 1 node. Likewise for the onDrop event.
If checkAcceptance = false, then that works and doesn't accept any drops, however if checkAcceptance = true then it accepts everything.
I'm using dojo version 1.4.2.
Here's the offending code:
var contentSourceA = new dojo.dnd.Source("ContentCol",{accept: ["contentItem"]});
if (dojo.query("#ContentCol")[0].children.length > 1) {
contentSourceA.checkAcceptance = function(){return false;}
}else{
contentSourceA.checkAcceptance = function(){return true;}
}
dojo.connect(contentSourceA,'onDrop',function(source,node,copy){
if (dojo.query("#ContentCol")[0].children.length > 1) {
contentSourceA.checkAcceptance = function(){return false;}
}else{
contentSourceA.checkAcceptance = function(){return true;}
}
});
So hence my question: Does changing dojo.dnd.Source.checkAcceptance affect the type checking functionality? If not, what have I done wrong here? Should I do this via one of the Topic events?
The type checking logic is encapsulated in the default implementation of dojo.dnd.Source.checkAcceptance function. If you override this function, the default logic is lost.
You can create your own DnD source class by inheriting dojo.dnd.Source:
dojo.declare("AcceptOneItemSource", dojo.dnd.Source, {
checkAcceptance : function(source, nodes) {
if (this.node.children.length > 1) {
return false;
}
return this.inherited(arguments);
}
});