I am trying to get QML to interact with FB graph API using FB Javascript SDK.
I'm loading this HTML inside a WebView element:
html: "<script>console.log(\"This is in WebKit!\"); window.FB.init();</script>"
and I've also created a JS Window Object named FB inside the WebView:
javaScriptWindowObjects: QtObject {
WebView.windowObjectName: "FB"
}
But as soon as the window.FB.init() is called, it throws out an error:
ReferenceError: Can't find variable: window
Another approach I'm using is load the FB.init() function using Component.onComplete
function startupFunction() {
console.log("This call is in QML!");
FB.init({
appId:'XXXXXXXXXXXXX', cookie:true,
status:true
});
console.log(FB);
}
Component.onCompleted: startupFunction();
But I get the error as :
TypeError: Result of expression 'FB.init' [undefined] is not a function
Here is the complete QML:
import QtQuick 1.0
import "fb.js" as FB
import QtWebKit 1.0
Rectangle {
width: 360
height: 360
Text {
text: "Hello World"
anchors.centerIn: parent
}
MouseArea {
anchors.fill: parent
}
WebView {
preferredWidth: 490
preferredHeight: 400
scale: 0.5
smooth: false
javaScriptWindowObjects: QtObject {
WebView.windowObjectName: "FB"
}
html: "<script>console.log(\"This is in WebKit!\"); window.FB.init();</script>"
function startupFunction() {
console.log("This call is in QML!");
FB.init({
appId:'xxxxxxxxxxxx', cookie:true,
status:true
});
console.log(FB);
}
Component.onCompleted: startupFunction();
}
}
I think the problem is that you are not defining anything in your window-object, your QtObject just contains the windowObjectName but no functions or vars. windowObjectName is really just the name for the new object, qml does not use the "fb.js"-import for that object.
According to the docs it's supposed to look like this:
WebView {
javaScriptWindowObjects: QtObject {
WebView.windowObjectName: "FB"
// the stuff you want in that window-object goes here:
function init() {
console.log("FB.init");
}
}
}
Related
I'd like to be able to create a class to pass to the Polymer function to create elements. The obvious use case is to have a base class that our developers can use to build Polymer elements.
However, Polymer seems to ignore lifecycle methods on the class. The following code does not run #created. Is there a workaround?
//attempt 1
class CustomElement {
is = 'sample-multi-view-polymer-buic'
created() { console.log('created') } // never called
}
export default Polymer(new CustomElement()) // doesn't work (see error below)
Since I'm using Babel to transpile ES6 to ES5, the above is equivalent to the code below. It also does not work.
// attempt2
function CustomElement() {
this.is = 'sample-multi-view-polymer-buic'
}
CustomElement.prototype.created= function() { console.log('hi') } //never called
There's an article for using ES6 with Polymer in the docs
So there's 2 things missing on your end:
You are missing a beforeRegistered() method.
You need to use Polymer() to instantiate your element's Class.
Here's how I do it:
<base href="https://polygit.org/components/">
<script src="webcomponentsjs/webcomponents-lite.min.js"></script>
<link href="polymer/polymer.html" rel="import">
<link href="paper-input/paper-input.html" rel="import">
<dom-module id="x-example">
<template>
<paper-input value="{{name::input}}"></paper-input>
<h4>{{name}}</h4>
</template>
<script>
"use strict";
class xExample {
beforeRegister() {
this.is = "x-example";
this.properties = {
name: {
type: String,
value: "I'm a data-binded prop"
}
}
}
ready() {
console.log("ready");
}
}
Polymer(xExample);
</script>
</dom-module>
<x-example></x-example>
Note:
The above snippet only works on Chrome/FF
For everything else you can of course "babelize" it.
I'm writing an application which should embed specific website into a <webview> and inject some CSS and JS code to adapt this website for viewing on certain touch-sensitive device.
The problem is that I can't find a way to inject my code when page is loaded, instead the code is injected AFTER the page is rendered and, as result, all modifications become visible.
While code injection perfectly works with chrome extensions and content script (by setting run_at attribute to document_end on manifest.json, this is not the case for webviews.
This is my code:
manifest.json
{
"name": "App",
"version": "0.0.1",
"manifest_version": 2,
"app": {
"background": {
"scripts": [ "main.js" ]
}
},
"permissions": [
"webview"
]
}
main.js
chrome.app.runtime.onLaunched.addListener(function() {
chrome.app.window.create('index.html', { state: "normal" },
function(win) {
win.contentWindow.onload = function() {
var wv = this.document.querySelector('webview');
wv.addEventListener('contentload', function(e) {
this.insertCSS({ code: "body { background: red !important; }" });
});
}
}
);
});
index.html
<webview src="https://developer.chrome.com/apps/tags/webview" style="width: 100%; height: 100%;"></webview>
The same on the Gist: https://gist.github.com/OnkelTem/ae6877d2d7b2bdfea5ae
If you try this app, you will see that only after the webview is loaded and fully rendered my CSS rule is applied and the page background becomes red. In this example I use contentload webview event, but I also tried all other webview events: loadstart, loadstop, loadcommit - with no any difference.
I tried also using webview.contentWindow, but this is object is EMPTY all the time, despite documentation states it should be used.
Any ideas? Is it possible at all?
First of all, use the loadcommit event instead of the contentload event.
Second, add runAt: 'document_start' to the webview.insertCSS call (this also applies to webview.executeScript, if you ever want to use it). The implementation of executeScript is shared with the extension's executeScript implementation, but unfortunately the app documentation is incomplete. Take a look at chrome.tabs.insertCSS until the app documentation is fixed.
Here is an example that works as desired:
chrome.app.runtime.onLaunched.addListener(function() {
chrome.app.window.create('index.html', { state: 'normal' },
function(win) {
win.contentWindow.onload = function() {
var wv = this.document.querySelector('webview');
// loadcommit instead of contentload:
wv.addEventListener('loadcommit', function(e) {
this.insertCSS({
code: 'body { background: red !important; }',
runAt: 'document_start' // and added this
});
});
}
}
);
});
Note: Although the previous works, I recommend to put the script that manipulates the webview in index.html, because the resulting code is much neater.
// main.js
chrome.app.runtime.onLaunched.addListener(function() {
chrome.app.window.create('index.html', { state: 'normal' });
});
<!-- index.html -->
<webview src="https://developer.chrome.com/apps/tags/webview" style="width: 100%; height: 100%;"></webview>
<script src="index.js"></script>
// index.js
var wv = document.querySelector('webview');
wv.addEventListener('loadcommit', function() {
wv.insertCSS({
code: 'body { background: red !important; }',
runAt: 'document_start'
});
});
Facebook claims its embedded posts are adjusted automatically based on the screen sizes.
Please see Can I customize the width of Embedded Posts? section in the below link.
https://developers.facebook.com/docs/plugins/embedded-posts
However, the embed doesn't seem to be responsive. Please see my test here,
http://colombowebs.com/test/fb/
Is there anything I have to do in addition to make it responsive?
You have to use javascript/jquery to obtain the desired result. I have taken help from responsive function and created the following which works almost for all widths.
(function ($) {
jQuery.fn.autoResizeFbPost = function () {
var fixWidth = function ($container, $clonedContainer, doParse) {
// default parameter.
doParse = typeof doParse == 'undefined' ? true : doParse;
var updatedWidth = $container.width();
// update all div.fb-post with correct width of container
var isMobile = window.matchMedia("only screen and (max-width: 600px)");
if (isMobile.matches) {
//Conditional script here
if (window.matchMedia("(orientation: portrait)").matches) {
// you're in PORTRAIT mode
updatedWidth = $(window).width();
}
if (window.matchMedia("(orientation: landscape)").matches) {
// you're in LANDSCAPE mode
updatedWidth = $(window).height();
}
}
$clonedContainer
.find('div.fb-post')
.each(function () {
$(this).attr('data-width', updatedWidth);
});
$('div.embedded', $clonedContainer).each(function () {
$(this).attr('max-width', updatedWidth);
});
// update page with adjusted markup
$container.html($clonedContainer.html());
//should we call FB.XFBML.parse? we don't want to do this at page load because it will happen automatically
if (doParse && FB && FB.XFBML && FB.XFBML.parse)
FB.XFBML.parse();
};
return this.each(function () {
var $container = $(this),
$clonedContainer = $container.clone();
// make sure there is a .fb-post element before we do anything.
if (!$container.find('div.fb-post').length) {
return false;
}
// execute once (document.ready) and do not call FB.XFBML.parse()
fixWidth($container, $clonedContainer, false);
$(window).bind('resize', function () {
fixWidth($container, $clonedContainer);
}).trigger('resize');
});
};
})(jQuery);
(function ($) {
$(document).ready(function () {
$('#post').autoResizeFbPost();
});
})(jQuery);
And your HTML should be like
<div style="background-color: white;" id="post">
<div class="fb-post" data-href="your-facebook-post-url" mobile="false"></div>
Hope this helps you. Feel free to post your comments.
I am using sage cell to convert html to math stuff
Template.home.rendered = function(){
\\ apply sagecell and mathjax
}
However, the content that are rendered comes from mongo, so it's sometimes loaded after sage cell is applied to it. I can do something like this
Template.home.rendered = function(){
Deps.autorun(function(){
if (Content.findOne({_id: ...})){
\\ apply sagecell and mathjax
}
});
}
It's better but still doesn't work all the time. Is there other things I can use to detect the content is completely rendered?
Edited with new response:
<template name='pendingAnswer'>
The answer to your question, coming back whenever, is:
{{>answer}}
</template>
<template name='answer'>
{{fromSage}}
</template>
Template.answer.helpers({
fromSage: function () {
Session.get('fromSage');
}
});
Invoked whenever - from a button, from navigating to the page, on blur...
function GetAnswerFromSage(data) {
callHTTP(website,data, callbackFromSage)
}
function callbackFromSage(err, data) {
if (err) then log(err);
Session.set('fromSage', data);
}
Earlier: try transform upon retrieval of mongo:
From Meteor Doc
// An Animal class that takes a document in its constructor
Animal = function (doc) {
_.extend(this, doc);
};
_.extend(Animal.prototype, {
makeNoise: function () {
console.log(this.sound);
}
});
// Define a Collection that uses Animal as its document
Animals = new Meteor.Collection("Animals", {
transform: function (doc) { return new Animal(doc); }
});
// Create an Animal and call its makeNoise method
Animals.insert({name: "raptor", sound: "roar"});
Animals.findOne({name: "raptor"}).makeNoise(); // prints "roar"
The script
<script type='text/javascript' src="http://sagecell.sagemath.org/static/embedded_sagecell.js"></script>
that is supposed to be in the head needs to be removed and instead be loaded after the content is completely loaded like so:
Template.content.rendered = function(){
// sage
Deps.autorun(function(){
if (Session.get('contentChanged')){
// loading this script causes mathjax to run
$.getScript("http://sagecell.sagemath.org/static/embedded_sagecell.js", function(d, textStatus){
if (textStatus=='success'){
// this converts <div class='compute'> to a sage cell
sagecell.makeSagecell({
inputLocation: 'div.compute',
evalButtonText: 'Evaluate',
hide: ['editorToggle']
});
}
})
}
})
and if I go from 1 content template to another content template, it seems that nothing is rerendered and so the mathjax was not applied. The only fix I can think is to force a page reload:
Template.content.events({
'click a': function(evt){
evt.preventDefault();
location.href = evt.currentTarget.href;
}
})
which makes the site much slower, unfortunately.
I implemented infinite scroll along with masonry on this tumblr: [check revision for link]
The audio player does not appear in posts loaded through infinite scroll, instead it displays the text "[Flash 9 is required to listen to audio.]"
The Inspire Well tumblr theme (I can't post another hyperlink but you can easily google it) seems to have solved this problem through this code:
if(InspireWell.infiniteScrolling && InspireWell.indexPage){
$masonedColumn.infinitescroll({
navSelector : 'ul.page_nav', // selector for the paged navigation
nextSelector : 'ul.page_nav li.page_next a', // selector for the NEXT link (to page 2)
itemSelector : '.post', // selector for all items you'll retrieve
loadingImg : '',
donetext : 'No more pages to load.',
errorCallback: function() {
// fade out the error message after 2 seconds
//$('#infscr-loading').animate({opacity: .8},2000).fadeOut('normal');
}
},
// call masonry as a callback
function( newElements ) {
$(newElements).css({ visibility: 'hidden' });
$(newElements).each(function() {
if($(this).hasClass("audio")){
var audioID = $(this).attr("id");
var $audioPost = $(this);
$audioPost.find(".player span").css({ visibility: 'hidden' });
var script=document.createElement('script');
script.type='text/javascript';
script.src="http://assets.tumblr.com/javascript/tumblelog.js?16";
$("body").append(script);
$.ajax({
url: "http://thetestinggrounds.tumblr.com/api/read/json?id=" + audioID,
dataType: "jsonp",
timeout: 5000,
success: function(data){
$audioPost.find(".player span").css({ visibility: 'visible' });
$audioPost.find("span:first").append('<script type="text/javascript">replaceIfFlash(9,"audio_player_' + audioID + '",\'\x3cdiv class=\x22audio_player\x22\x3e' + data.posts[0]['audio-player'] +'\x3c/div\x3e\')</script>');
}
});
}
});
I tried to adapt this for my tumblr (with placeholder text to see if it was finding the correct element):
$(window).load(function(){
$('#allposts').masonry({
singleMode: true,
itemSelector: '.box'
});
$('#allposts').infinitescroll({
navSelector : "div.navigation",
nextSelector : "div.navigation a:first",
itemSelector : ".box",
debug : true
},
function( newElements ) {
$(this).masonry({ appendedContent: $( newElements ) });
$(newElements).each(function(){
if($(this).hasClass("audio")){
var audioID = $(this).attr("id");
var $audioPost = $(this);
$audioPost.find(".audio span");
var script=document.createElement('script');
script.type='text/javascript';
script.src="http://assets.tumblr.com/javascript/tumblelog.js?16";
$("body").append(script);
$.ajax({
url: "http://fuckyeahempathy.tumblr.com/api/read/json?id=" + audioID,
dataType: "jsonp",
timeout: 5000,
success: function(data){
$audioPost.find(".audio span");
$audioPost.find("span:first").append("<p>audio player not working</p>");
}
});
}
});
}
);
});
But there is no sign of the text. Any help would be greatly appreciated.
Here is a solution I came up with when I needed to implement the same functionality in the template I was creating.
In your HTML, include your AudioPlayer Tumblr tag between comments. This is to prevent loaded scripts from being called. Also add a class "unloaded" to keep track whether or not we've loaded the audio player for this post or not.
...
{block:AudioPlayer}
<div class="audio-player unloaded">
<!--{AudioPlayerBlack}-->
</div>
{/block:AudioPlayer}
...
If you look at the commented code after the page is loaded, you will notice an embed tag being passed to one of the Tumblr javascript functions. Since we commented it, it will not execute. Instead we will want to extract this string and replace the div contents with it.
Create a javascript function which will do this. This can be done with regular javascript, but to save time I will do it with jQuery since this is how I did it for my template:
function loadAudioPosts() {
// For each div with classes "audio-player" and "unloaded"
$(".audio-player.unloaded").each(function() {
// Extract the <embed> element from the commented {AudioPlayer...} tag.
var new_html = $(this).html().substring(
$(this).html().indexOf("<e"), // Start at "<e", for "<embed ..."
$(this).html().indexOf("d>")+2 // End at "d>", for "...</embed>"
);
// Replace the commented HTML with our new HTML
$(this).html(new_html);
// Remove the "unloaded" class, to avoid reprocessing
$(this).removeClass("unloaded");
});
}
Call loadAudioPosts() once on page load, then every time your infinite scrolling loads additional posts.
html
<div class="audio" id="{postID}">{AudioPlayerBlack}</div>
css
.audio {
height:30px;
overflow-y: hidden;
}
.audio span {
display:none;
}
java
setTimeout(function() {
$wall.masonry({ appendedContent: $(newElements) });
/* repair audio players*/
$('.audio').each(function(){
var audioID = $(this).attr("id");
var $audioPost = $(this);
$.ajax({
url: 'http://yoolk.tumblr.com/api/read/json?id=' + audioID,
dataType: 'jsonp',
timeout: 50000,
success: function(data){
$audioPost.append('\x3cdiv style=\x22background-color:white;height:30px\x22 class=\x22audio_player\x22\x3e' + data.posts[0]['audio-player'] +'\x3c/div\x3e');
}
});
});
}, 2000);