The top and left properties in windows.create does not position the window. It is always positioned in center.
Two questions:
I can add a procedure to position the window after it is open by using browser.windows.update() but was wondering why it does not work as it is.
I would like to have it non-modal. Is this possible? I notice the popup returned has alwaysOnTop: false, but it is always on top. I am porting an old Chrome project - in Chrome the window is not modal.
On a side note: There also seems to be some inconsistency related to the popup returned from the create call.
type is "popup" when using both popup and panel.
left and top are sometimes 0 and other times has the actual value. Seems like when using popup as type the position is more often 0.
Example:
manfiest.json:
{
"name" : "Test winpos",
"short_name" : "ext_name_short",
"version" : "0.0.0.1",
"author" : "NW",
"manifest_version" : 2,
"browser_action" : {
"default_title" : "Test winpos"
},
"background" : {
"scripts" : ["./bg.js"]
}
}
bg.js:
"use strict";
var dbg = 1;
var popup_id = -1;
// normal, popup, panel, detatched_panel
var type = "panel";
browser.windows.onRemoved.addListener(function(w_id) {
if (dbg) console.log(';; onRemoved, (w_id, popup_id):', w_id, popup_id);
if (w_id === popup_id)
popup_id = -1;
});
browser.browserAction.onClicked.addListener(function (/* tab */) {
if (dbg) console.log(';; onClicked, (popup_id):', popup_id);
if (popup_id < 0) {
browser.windows.create({
allowScriptsToClose: true, // FF Only
url : browser.extension.getURL("popup.html"),
type : type,
// FF NOT SUPPORTED focused : true,
width : 450,
height : 320,
left : 50,
top : 50
}, function (pop) {
if (dbg) console.log(`;; windows.create-> ${type}:`, pop);
popup_id = pop.id;
// I can add positioning call here
});
} else {
if (dbg) console.log(';; window.update, (pupup_id)', popup_id);
browser.windows.update(popup_id, { "focused": true });
}
});
popup.html:
<!DOCTYPE html>
<html lang="en-US" dir="ltr">
<head>
<meta charset="utf-8">
<title>Test FF Popup</title>
</head>
<body>
<p>Hello</p>
</body>
</html>
Related
There are 2 examples in the Matterport SDK for Embeds documentation to show how to place Mattertags in a scene:
The Intersection Inspector which only allows you to see coordinates for placing a Mattertag where the cursor is if you wait a little bit ... Not very user friendly, you need to copy the coordinates manually in your program.
The Transient Tags Editor which enable you to interactively place multiple Mattertags visually, edit them and then to extract them easily in a JSON file ...
I was wondering how to reproduce the Transient Tags Editor visual UX as I would like to use it in an application.
Insert Mattertags into the model visually
The source code of the app of the Transient Tags Editor is privately hosted on github (Maybe because it doesn't run perfectly on Firefox?), unlike the source code of the Intersection Inspector which is publicly hosted on JSFiddle.
But the user friendliness of the Transient Tags Editor intrigued me and I wanted to understand the difference between the two tools Matterport SDK provides to find out Mattertags coordinates.
How the Intersection Inspector works
The Intersection Inspector uses a timer to display a button at the position of the Pointer when the user does not move the pointer for more than one second. The user can then click the button to see the Mattertag coordinates and copy them manually ...
To achieve that, it needs the current Camera position, which it obtains by observing the camera's pose property:
var poseCache;
mpSdk.Camera.pose.subscribe(function(pose) {
poseCache = pose;
});
Also, it needs the current Pointer position, which it obtains by observing the pointer's intersection property:
var intersectionCache;
mpSdk.Pointer.intersection.subscribe(function(intersection) {
intersectionCache = intersection;
intersectionCache.time = new Date().getTime();
button.style.display = 'none';
buttonDisplayed = false;
});
※ An intersection event is triggered the user moves the pointer, so we hide the button to make sure it is not displayed before the one second delay is over.
Then, a timer is set up using setInterval() to display the button at the right time:
setInterval(() => {
// ...
}, 16);
In the timer callback, we check wether all the conditions to display the button are met ...
First, check we have the information we need:
setInterval(() => {
if (!intersectionCache || !poseCache) {
return;
}
// ...
}, 16);
Then, check one second has elapsed since the last intersection event was received, or we wait the next tick to check again:
var delayBeforeShow = 1000;
setInterval(() => {
if (!intersectionCache || !poseCache) {
return;
}
const nextShow = intersectionCache.time + delayBeforeShow;
if (new Date().getTime() > nextShow) {
// ...
}
}, 16);
Finally, we check the button is not already being displayed:
var delayBeforeShow = 1000;
var buttonDisplayed = false;
setInterval(() => {
if (!intersectionCache || !poseCache) {
return;
}
const nextShow = intersectionCache.time + delayBeforeShow;
if (new Date().getTime() > nextShow) {
if (buttonDisplayed) {
return;
}
// ...
}
}, 16);
Once the conditions are met, we can display the button using Conversion.worldToScreen() to get the screen coordinate of the pointer :
// ...
setInterval(() => {
// ...
if (new Date().getTime() > nextShow) {
// ...
var size = {
w: iframe.clientWidth,
h: iframe.clientHeight,
};
var coord = mpSdk.Conversion.worldToScreen(intersectionCache.position, poseCache, size);
button.style.left = `${coord.x - 25}px`;
button.style.top = `${coord.y - 22}px`;
button.style.display = 'block';
buttonDisplayed = true;
}
}, 16);
The button is a simple HTML button hidden by default using display: none; and positioned relative to the body with position: absolute;.
When the user clicks the button, the current coordinates of the pointer are displayed in a <div> tag above the <iframe> and the button is hidden:
button.addEventListener('click', function() {
text.innerHTML = `position: ${pointToString(intersectionCache.position)}\nnormal: ${pointToString(intersectionCache.normal)}\nfloorId: ${intersectionCache.floorId}`;
button.style.display = 'none';
iframe.focus();
});
The coordinates are formatted using the following function:
function pointToString(point) {
var x = point.x.toFixed(3);
var y = point.y.toFixed(3);
var z = point.z.toFixed(3);
return `{ x: ${x}, y: ${y}, z: ${z} }`;
}
Now, let's see how the easier-to-use and user-friendlier Transient Tags Editor interface works ...
How the Transient Tag Editor works
The Intersection Inspector is enough if you just have a few __Mattertag__s to set permanently in a few models in your application. But if you need your users to set tags interactively in models, something like the Transient Tags Editor's GUI is a better starting point.
The main advantage of using the Transient Tags Editor is that you can see how the Mattertag will be displayed before creating it and! That allows you to place the tag precisely without trial and error ...
To add a tag, you must click on the "Place New Tag" button to enter the "add tag" mode, then you can place one new tag anywhere you want.
We will only focus on that aspect of the editor and produce a simplified code sample that only add tags:
As the user move a tag along the pointer when in "add tag" mode, the first step is to create a new tag and place it. Let's create a function for that using Mattertag.add():
function addTag() {
if(!tag){
mpSdk.Mattertag.add([{
label: "Matterport Tag",
description: "",
anchorPosition: {x: 0, y: 0, z: 0},
stemVector: {x:0, y: 0, z: 0},
color: {r: 1, g: 0, b: 0},
}])
.then((sid) => {
tag = sid[0];
})
.catch( (e) => {
console.error(e);
})
}
}
Then we will have to place the tag at a position near the pointer, and update its position as the user moves the pointer, so let's create a function for that using Mattertag.editPosition():
function updateTagPos(newPos, newNorm=undefined, scale=undefined){
if(!newPos) return;
if(!scale) scale = .33;
if(!newNorm) newNorm = {x: 0, y: 1, z: 0};
mpSdk.Mattertag.editPosition(tag, {
anchorPosition: newPos,
stemVector: {
x: scale * newNorm.x,
y: scale * newNorm.y,
z: scale * newNorm.z,
}
})
.catch(e =>{
console.error(e);
tag = null;
});
}
As you can see the updateTagPos() function takes 3 parameters:
newPos: the new anchor position for the Mattertag.
newNorm: an optional new stem vector for the Mattertag.
scale: an optional new scale for the stem of the Mattertag.
To update the tag position as the user moves the pointer, let's observe the pointer's intersection property to call updateTagPos():
mpSdk.Pointer.intersection.subscribe(intersectionData => {
if(tag){
if(intersectionData.object === 'intersectedobject.model' || intersectionData.object === 'intersectedobject.sweep'){
updateTagPos(intersectionData.position, intersectionData.normal);
}
}
});
To place the new tag, the user simply clicks their mouse button, the Transient Tags Editor provides its own version of the document.activeElement method for intercepting clicks on the <iframe> (but does not work with Firefox so the editor use a quite complex workaround):
function focusDetect(){
const eventListener = window.addEventListener('blur', function() {
if (document.activeElement === iframe) {
placeTag(); //function you want to call on click
setTimeout(function(){ window.focus(); }, 0);
}
window.removeEventListener('blur', eventListener );
});
}
But, we will use our version which works better with Firefox (But still stop working after the first click in Firefox for whatever reason):
window.addEventListener('blur',function(){
window.setTimeout(function () {
if (document.activeElement === iframe) {
placeTag(); //function you want to call on click
window.focus()
addTag();
}
}, 0);
});
Finally, let's the function that navigates to the new tag and opens its billboard, usingMattertag.navigateToTag()
function placeTag(){
if(tag) mpSdk.Mattertag.navigateToTag(tag, mpSdk.Mattertag.Transition.INSTANT);
tag = null;
}
Simple Editor Code Sample
First, the complete JavaScript source code:
"use strict";
const sdkKey = "aaaaXaaaXaaaXaaaXaaaXaaa"
const modelSid = "iL4RdJqi2yK";
let iframe;
let tag;
document.addEventListener("DOMContentLoaded", () => {
iframe = document.querySelector('.showcase');
iframe.setAttribute('src', `https://my.matterport.com/show/?m=${modelSid}&help=0&play=1&qs=1>=0&hr=0`);
iframe.addEventListener('load', () => showcaseLoader(iframe));
});
function showcaseLoader(iframe){
try{
window.MP_SDK.connect(iframe, sdkKey, '3.10')
.then(loadedShowcaseHandler)
.catch(console.error);
} catch(e){
console.error(e);
}
}
function loadedShowcaseHandler(mpSdk){
addTag()
function placeTag(){
if(tag) mpSdk.Mattertag.navigateToTag(tag, mpSdk.Mattertag.Transition.INSTANT);
tag = null;
}
window.addEventListener('blur',function(){
window.setTimeout(function () {
if (document.activeElement === iframe) {
placeTag(); //function you want to call on click
window.focus()
addTag();
}
}, 0);
});
function updateTagPos(newPos, newNorm=undefined, scale=undefined){
if(!newPos) return;
if(!scale) scale = .33;
if(!newNorm) newNorm = {x: 0, y: 1, z: 0};
mpSdk.Mattertag.editPosition(tag, {
anchorPosition: newPos,
stemVector: {
x: scale * newNorm.x,
y: scale * newNorm.y,
z: scale * newNorm.z,
}
})
.catch(e =>{
console.error(e);
tag = null;
});
}
mpSdk.Pointer.intersection.subscribe(intersectionData => {
if(tag){
if(intersectionData.object === 'intersectedobject.model' || intersectionData.object === 'intersectedobject.sweep'){
updateTagPos(intersectionData.position, intersectionData.normal);
}
}
});
function addTag() {
if(!tag){
mpSdk.Mattertag.add([{
label: "Matterport Tag",
description: "",
anchorPosition: {x: 0, y: 0, z: 0},
stemVector: {x:0, y: 0, z: 0},
color: {r: 1, g: 0, b: 0},
}])
.then((sid) => {
tag = sid[0];
})
.catch( (e) => {
console.error(e);
})
}
}
} // loadedShowcaseHandler
The HTML source code:
<!DOCTYPE html>
<html>
<head>
<title>Transient Tag Editor</title>
<style>
#showcase {
width: 100%;
height: 100vh;
}
</style>
<script src="https://static.matterport.com/showcase-sdk/2.0.1-0-g64e7e88/sdk.js"></script>
<script src="/js/app-editor.js" type="text/javascript" defer></script>
</head>
<body>
<iframe id="showcase" frameborder="0" allowFullScreen allow="xr-spatial-tracking"></iframe>
</body>
</html>
It works!
Complete Code
The complete code for this sample and others is available on github:
github.com/loic-meister-guild/pj-matterport-sdk-2021-tutorial
See Also
Matterport SDK 2021 Tutorial
Node.js + Express Tutorial for 2021
How to detect a click event on a cross domain iframe
Im using this cordova plugin:
https://github.com/katzer/cordova-plugin-printer
$ionicPlatform.ready().then(function () {
var printerId = $scope.printData.printerUrl;
alert('print to: ' + printerId);
cordova.plugins.printer.pick(function(printerId) {
cordova.plugins.printer.print("<p>TEST PRINT</p>", { printerId: printerId, bounds:[20, 20, 0, 0] });
});
});
I want to know how to print directly to printer without any popup.
Using ng-cordova printer, this code prints directly to printer.
Make sure you have correct printer url. something like "ipp://192.168.../ipp"
var htmlContent = "<!DOCTYPE html><html><head><meta charset='UTF-8'><title>Title</title> <link href='css/print.css' rel='stylesheet' /></head><body><div>test print</div></body></html>"
var options = {
name: 'print-job', // printjob name
printerId: $scope.PrinterUrl, // network url of the printer to use (iOS only)
//duplex: false, // default true (double sided) (iOS only)
landscape: false, // default false (portrait)
graystyle: true, // prints black and white (default), but true has better performance
bounds: {left:0, top:0, width:0, height:0}, // size and position of the print view (iPad only)
hidePaperFormat: true,
border: false,
hidePageRange: true
};
$cordovaPrinter.print(htmlContent, options).then(function(msg){
console.log('Print Ok: ' + msg);
});
I'm trying to get recapatcha v2 working in my ASP MVC project. The client's computers have IE10/IE11 and shows all intranet pages in compatibility view which causes the recaptcha not to show as it is intended.
The problem is it rarely accepts my answer even though it's right. It just shows a new image, but every once in awhile I get it right. Anyone else experience this?
If you enable compability view for google.com in IE and visit the demo site you can try it out.
reCAPTCHA requires that compatibility view is not enabled in order to work, see:
https://support.google.com/recaptcha/?hl=en-GB#6223838
You are seeing reCaptcha's fallback. It seems that you have to get two correct answers in a row. It then gives you a response code that you copy and paste into a <textarea> element.
So what you are possibly experiencing is that you aren't getting two consecutive reCaptchas correct.
You can test the fallback recaptcha by adding the fallback=true parameter to the JavaScript resource:
<script src="https://www.google.com/recaptcha/api.js?fallback=true" async defer></script>
As explained by #Coulton recaptcha does not support IE compatibility mode.
reCaptcha uses a function in javascript named querySelectorAll, and querySelector. IE 11 in compatibility mode renders as IE 7.0, and IE 7.0 and below don't seem to have the functions querySelectorAll and querySelector and that is why it fails.
Now, I am using .net webforms so you may have to adapt it a little for MVC, but here it is:
<%# Page Language="C#" AutoEventWireup="true" CodeBehind="reCaptcha.aspx.cs" Inherits="reCaptcha" %>
<!DOCTYPE html>
<script type="text/javascript">
// this defines these functions if they don't exist.
if (!document.querySelectorAll) {
document.querySelectorAll = function (selectors) {
var style = document.createElement('style'), elements = [], element;
document.documentElement.firstChild.appendChild(style);
document._qsa = [];
style.styleSheet.cssText = selectors +
'{x-qsa:expression(document._qsa && document._qsa.push(this))}';
window.scrollBy(0, 0);
style.parentNode.removeChild(style);
while (document._qsa.length) {
element = document._qsa.shift();
element.style.removeAttribute('x-qsa');
elements.push(element);
}
document._qsa = null;
return elements;
};
}
if (!document.querySelector) {
document.querySelector = function (selectors) {
var elements = document.querySelectorAll(selectors);
return (elements.length) ? elements[0] : null;
};
}
</script>
<script src='https://cdnjs.cloudflare.com/ajax/libs/modernizr/2.8.3/modernizr.js' type="text/javascript"></script>
<script src='https://www.google.com/recaptcha/api.js' type="text/javascript"></script>
<script src='https://cdnjs.cloudflare.com/ajax/libs/jquery/1.3.2/jquery.min.js' type="text/javascript"></script>
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title>reCaptcha Test</title>
</head>
<body>
<h1>reCaptcha Test</h1>
<form id="frmResult" runat="server">
<div class="g-recaptcha" data-sitekey=""></div>
<script type="text/javascript">
function IeVersion() {
//Set defaults
var value = {
IsIE: false,
TrueVersion: 0,
ActingVersion: 0,
CompatibilityMode: false
};
//Try to find the Trident version number
var trident = navigator.userAgent.match(/Trident\/(\d+)/);
if (trident) {
value.IsIE = true;
//Convert from the Trident version number to the IE version number
value.TrueVersion = parseInt(trident[1], 10) + 4;
}
//Try to find the MSIE number
var msie = navigator.userAgent.match(/MSIE (\d+)/);
if (msie) {
value.IsIE = true;
//Find the IE version number from the user agent string
value.ActingVersion = parseInt(msie[1]);
} else {
//Must be IE 11 in "edge" mode
value.ActingVersion = value.TrueVersion;
}
//If we have both a Trident and MSIE version number, see if they're different
if (value.IsIE && value.TrueVersion > 0 && value.ActingVersion > 0) {
//In compatibility mode if the trident number doesn't match up with the MSIE number
value.CompatibilityMode = value.TrueVersion != value.ActingVersion;
}
return value;
}
var ie = IeVersion();
var reCaptchaApi = "";
$(document).ready(function () {
$('.g-recaptcha').each(function (index, obj) {
grecaptcha.render(obj, { 'sitekey': reCaptchaApi });
});
if (ie.CompatibilityMode) {
// loading it twice makes it load in compatibility mode.
$('.g-recaptcha').each(function (index, obj) {
grecaptcha.render(obj, { 'sitekey': reCaptchaApi });
});
}
});
</script>
</form>
</body>
</html>
This allows reCaptcha V-2 to load in compatibility mode.
$("#ddl").change(function () {
var strSelected = "";
$("#ddl option:selected").each(function () {
strSelected += $(this)[0].value;
});
if (strSelected.length != 0) {
var url = "/Reseller/MailPartial/?resellerId=" + strSelected;
$("#mail").empty();
$("#mail").load(url);
}
this is code I use to load partial in my View (partial is only 1 label and 1 editorfor,the one that should load tinymce). I have [UIHint("tinymce_jquery_full"), AllowHtml] in my model and tinymce editor loads perfectly normal in other views. But when I use partial views it comes back as plain text area. How to fix this?
thanks
EDIT:
I figured it out,ijaz was almost correct ;)
I needed to reinit tinymce like ijaz said but even when I called INitTinyMCE like ijaz said it wouldn't have mattered because the element hasn't loaded yet to html and I have no idea why. Solution was to call initTinyMce after the element has loaded to the page.
I tried to use
$("#mail").load(url, InitTinyMCE());
but it didn't work.
Any ideas how to call InitTinyMCE() after the element has loaded? It's working now but it's relying on pressing another button to trigger InitTinyMCE()
EDIT again
I changed code to pure ajax,no more .load()
sorry for being so messy :)
In the above code,it seems that [UIHint] is not applied properly. so get things work, kindly initialize the TinyMCE manualy, i mean change you code as ,
$("#ddl").change(function () {
var strSelected = "";
$("#ddl option:selected").each(function () {
strSelected += $(this)[0].value;
});
if (strSelected.length != 0) {
var url = "/Reseller/MailPartial/?resellerId=" + strSelected;
$("#mail").empty();
$("#mail").load(url);
**Re-Init TinyMCE**
InitTinyMCE();
}
function InitTinyMCE()
{
$('##ViewData.TemplateInfo.GetFullHtmlFieldName(string.Empty)').tinymce({
// Location of TinyMCE script
script_url: '#Url.Content("~/Scripts/tinymce/tiny_mce.js")',
theme: "advanced",
height: "170",
width: "240",
verify_html : false,
plugins : "pagebreak,style,layer,table,save,advhr,advimage,advlink,emotions,iespell,inlinepopups,insertdatetime,preview,media,searchreplace,print,contextmenu,paste,directionality,fullscreen,noneditable,visualchars,nonbreaking,xhtmlxtras,template,wordcount,advlist,autosave",
// Theme options
theme_advanced_buttons1: "undo, redo,pasteword,|, bold, italic, underline,|,justifyleft,justifycenter,justifyright,justifyfull,|,image, emotions" ,
theme_advanced_buttons2: "charmap, bullist, numlist,|,formatselect,fontselect,fontsizeselectcode, |,tiny_mce_wiris_formulaEditor, fullscreen",
theme_advanced_buttons3: "",
theme_advanced_toolbar_location: "top",
theme_advanced_toolbar_align : "left",
theme_advanced_statusbar_location : "bottom",
theme_advanced_resizing : true,
// Example content CSS (should be your site CSS)
content_css : "#Url.Content("~/Scripts/tinymce/css/content.css")",
convert_urls : false,
// Drop lists for link/image/media/template dialogs
template_external_list_url : "lists/template_list.js",
external_link_list_url : "lists/link_list.js",
external_image_list_url : "lists/image_list.js",
media_external_list_url : "lists/media_list.js"
});
}
I am using a UI dialog box to display a message.
It works well in Firefox and Google Chrome. However, when I test in IE versions the dialog box doesn't open.
Can any one tell what real problem is?
I had pasted my code below:
function check_selected(c) {
var count = c - 1;
var radios = document.getElementsByName('plan');
for ( var i = 0; i < radios.length; i++) {
if (radios[i].disabled) {
if (radios[i].checked) { // checked
$('#planalert').dialog({
modal : true,
autoOpen : true,
title : "Plan",
width : 650,
height : 150,
show : "blind",
hide : "scale",
});
var c = 0;
} else {
var c = 1;
}
}
}
;
if (c == 0) {
return false;
} else {
return true;
}
}
Try removing the trailing comma from the options object you're passing dialog:
$('#planalert').dialog({
modal:true,
autoOpen: true,
title:"Plan",
width:650,
height:150,
show: "blind",
hide: "scale" // <-----
});
Internet Explorer will choke on the extra comma, while other browsers may not.
Also, remove the semicolon (;) at the end of the for loop ending brace:
for (var i = 0; i < radios.length; i++) {
/* snip */
} // <--- semicolon not necessary
Additionally, you are attempting to define c multiple times inside of your function. You are passing it in to the function so your var c = ... statements actually aren't having the effect you intend. You should either use another variable (which I would recommend rather than mutating the parameter you are passed), or remove the var statements from inside the if block.
Additionally, your loop is really only setting c for the last, disabled radio button. Is this what you intended?
Anyway, here is how I would re-write it (without attempting to fix the logical error above). Be sure to use tools like JsHint to check your JavaScript for probems:
function check_selected(c){
var count=c-1;
var radios = document.getElementsByName('plan');
var isChecked = 0;
for (var i = 0; i < radios.length; i++) {
if (radios[i].disabled) {
if (radios[i].checked){ // checked
$('#planalert').dialog({
modal:true,
autoOpen: true,
title:"Plan",
width:650,
height:150,
show: "blind",
hide: "scale"
});
isChecked = 0;
} else{
isChecked = 1;
}
}
}
return isChecked;
}