How to render embedded Elm module synchronously? - elm

I am trying to embed "HelloWorld" module into existing HTML page.
I found that module is rendered asynchronously (i don't get rendered element
immediately after calling "embed").
I want to use Elm in an existing project and rewrite some parts of JavaScript
in Elm. But asynchronous rendering makes things difficult.
Is there a way to render it synchronously?
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="app.js" type="text/javascript"></script>
</head>
<body>
<script type="text/javascript">
var appContainer = document.createElement('div');
Elm.HelloWorld.embed(appContainer);
console.log('Html: ' + appContainer.innerHTML); // Will print an empty string
setTimeout(function() {
console.log('Html: ' + appContainer.innerHTML); // Will print "Hello, World!"
}, 0);
</script>
</body>
</html>
HelloWorld.elm
module HelloWorld exposing (main)
import Html exposing (text)
main =
text "Hello, World!"
UPD: Removed redundant code.

Nowadays, Elm doesn't expose this kind of hook.
You have basically two different approaches, one based on events and the other based on timing (the setTimeout() method you are already using).
The event-driven approach involves the MutationObserver Api.
Creating a new MutationObserver, you can observe the HTML Node onto which you run the Elm module: this way, you have a hook on the DOM update.
A naive check would be checking against the children's list of the appContainer:
<body>
<div id="myDiv"></div>
<script type="text/javascript">
var appContainer = document.getElementById('myDiv');
var mo = new MutationObserver(function(mutationRecords, instance) {
for (var i = 0; i < mutationRecords.length; i += 1) {
if (mutationRecords[i].addedNodes.length > 0) {
// a trivial check
console.log('Html: ' + appContainer.innerHTML);
// stop observing, if not needed anymore
mo.disconnect();
}
}
});
mo.observe(appContainer, { childList: true });
Elm.HelloWorld.embed(appContainer);
</script>
</body>
Note that the above code is assuming that your HelloWorld module is adding children to the appContainer, so you should modify it conveniently:
module HelloWorld exposing (main)
import Html exposing (p, text)
main =
p [] [ text "Hello, World!" ]

Related

Open URL in new safari tab - safari extention

generaly i ask exactly the same question as here:
Safari extension: Event for a completely new tab?
in this answer (case 3) he guide to listen click event inside inject.js.
can someone show this implementation?
Here's an implementation I used for one of my previous extensions:
Create a global.html file, and inside the Extension Builder, select that file under the Extension Global Page section:
Source for global.html:
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>My Extension</title>
</head>
<body>
<script>
function messageHandler(event) {
var newTab = null;
var url = null;
if (event.name === "open_new_tab") {
url = event.message;
newTab = safari.application.activeBrowserWindow.openTab();
newTab.url = url;
}
}
safari.application.addEventListener("message", messageHandler, false);
</script>
</body>
</html>
Create an extension.js file, and add it to the Injected Extension Content -> End Scripts section:
Source for extension.js:
(function() {
// ... rest of you extension code
openNewTabWithUrl("http://google.com")
function openNewTabWithUrl(url) {
safari.self.tab.dispatchMessage("open_new_tab", url);
}
}());
This will send a message containing a URL from your extension.js to global.html, which will pick up the message and open the new tab.

Is there a way to call a function before the dependencies are loaded in Sencha Touch application

Sencha Touch application has requires:[] option to specify the list of controllers and models and stores etc that are loaded that we need for the application to work but is there a way to execute something that we need even before the loading of the dependencies. FOr e.g. I need the Browsers Language even before the loading of all dependencies. So is it possible to do?
Keep in mind: Sencha Touch is nothing but JavaScript.
You can add some script in your index.html in front of the script tag that loads the sencha microloader.
<!DOCTYPE HTML>
<html manifest="" lang="en-US">
<head>
<meta charset="UTF-8">
<title>FNL</title>
<style type="text/css">
<!-- sencha stuff -->
</style>
<script id="myScript" type="text/javascript" src="myScript.js"></script>
<!-- The line below must be kept intact for Sencha Command to build your application -->
<script id="microloader" type="text/javascript" src=".sencha/app/microloader/development.js"></script>
</head>
<body>
<div id="appLoadingIndicator">
<div></div>
<div></div>
<div></div>
</div>
</body>
</html>
I added some lines to the ST Microloader:
this.css = processAssets(manifest.css, 'css');
// Load language strings
var xhr = new XMLHttpRequest();
xhr.open('GET', 'api/Settings', false);
xhr.send(null);
var settings = eval("(" + xhr.responseText + ")");
Lang = settings.Translations[0];
Options = settings.Options[0];
// End Load Language Strings
this.js = processAssets(manifest.js, 'js');
In ExtJS i accomplished this by loading a Dependency class first
Ext.require([
'MyApp.Dependencies',
..
]);
so the Dependecies class is loaded before all controllers which looks like this
Dependencies.js:
Ext.define('MyApp.Dependencies', {
singleton: true,
init: function() {
// load translation data
}
});
MyApp.Dependecies.init();
and for completition my init function looks something like this:
inti: function(){
function loadScriptSync(url) {
var xhrObj = new XMLHttpRequest();
// open and send a synchronous request
xhrObj.open('GET', url, false);
xhrObj.send('');
// add the returned content to a newly created script tag
var se = document.createElement('script');
se.type = "text/javascript";
se.text = xhrObj.responseText;
document.getElementsByTagName('head')[0].appendChild(se);
}
var language = this.getLanguage();
loadScriptSync("resources/locale/locale." + language + ".js");
}

Mocha + ZombieJs, document is not defined

I'm using zombiejs + mocha to write some tests for a library that manipulates the DOM, it uses JavaScript native functions like document.createElement(), my problem is, when I run the tests I get an exception saying that 'document is not defined'.
Is it a problem with zombiejs, or with jsDOM? and how can I fix it?
here is an example of my code:
HTML:
<!DOCTYPE html>
<html>
<head>
<script src="mocha.js"></script>
<script src="expect.js"></script>
<title>XS UI Tests</title>
<script type="text/javascript" charset="utf-8" src="../lib/mylibrary.js"></script>
<script>
mocha.setup('bdd');
</script>
</head>
<body>
<div id="table"></div>
<div id="controls"></div>
<div id="form"></div>
<div id="mocha"></div>
<script src="tests.js"></script>
</body>
</html>
in tests.js:
var expect = require( 'expect.js' )
, Zombie = require( 'zombie' )
, browser = new Zombie( { debug: true } )
;
describe( 'XS UI Tests:', function() {
before( function( done ) {
browser.visit( 'http://localhost:8080/test/ui.html', done );
} );
it( 'expect ui.html to be loaded', function() {
expect( browser.success ).to.be( true );
} );
describe( 'Table Tests:', function() {
it( 'expect div#table ( table container ) to exist', function() {
expect( browser.query( "#table" ) ).to.be.ok();
} );
} );
} );
Thank you for your help
The document object is contained within the zombie browser. If your code doesn't find document, it is likely you are not running it within the zombie sandbox context.
Are you loading your library via zombie.visit('/page/with/library') or are you requiring it directly and trying to use it within the test?
It is best to test complete webpages rather than libraries. You should create a test page that has a few buttons/links on it which test different functions of the library, and pressButton or visit those with zombie.

"Container is not defined" Google chart

I have solved my issue but can't answer it myself since I am too new to this site:
turns out I needed to use the following:
chart = new google.visualization.PieChart(document.getElementById('pie_today_div'));
I was using JQuery to access the element, $('#pie_today_div'). As of now the evidence points to the fact that the PieChart constructor has to have standard JS code, document.getElementById('pie_today_div')
Maybe something else going on, but changing the way I access the container element fixes my code
ORIGINAL ISSUE FOR REFERENCE TO MY SOLUTION
I am getting a "Container is not defined" error when I am trying to instantiate a Google PieChart object.
I validated my page at http://validator.w3.org/ and i get a pretty green banner saying it validates.
I receive no js errors when the page loads. My Ajax call is making a full round trip with the data I want from the DB.
Here is my HTML
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html;charset=UTF-8">
<link href="/css/note.css?10022012" rel="stylesheet" type="text/css" media="screen">
<script type="text/javascript" language="javascript" src="/call/js/jquery-1.7.2.min.js"></script>
<script type="text/javascript" src="https://www.google.com/jsapi"></script>
<script type="text/javascript">google.load('visualization', '1.0', {'packages':['corechart']});</script>
<script type="text/javascript" language="javascript" src="/call/js/init.js?10042012-2"></script>
<title>Call Stats</title>
</head>
<body>
Today Stats
<div id="pie_today_div"></div>
</body>
</html>
here is the js:
function drawPieChartToday() {
$.post('/call/ajax.php5',{
action:'pieToday'
}, function(ticketData) {
var data = new google.visualization.DataTable();
data.addColumn('string', 'Count');
data.addColumn('number', 'Topic');
for (var key in ticketData){
data.addRow([key, ticketData[key]]);
}
options = {
title: 'Issue Topics'
};
alert($('#pie_today_div').attr('id'));
chart = new google.visualization.PieChart($('#pie_today_div'));
chart.draw(data, options);
},'json');
}
here is my debugging code to make sure the element was being found:
alert($('#pie_today_div').attr('id')); <-- alerts "pie_today_div"
I'm not a jquery fan, but I think that $('#pie_today_div') returns a set of matched elements. The attribute computation works because (from jquery documentation) it "gets the value of an attribute for the first element in the set of matched elements".
So try
chart = new google.visualization.PieChart($('#pie_today_div')[0]);
or directly
chart = new google.visualization.PieChart(document.getElementById('pie_today_div'));
A container error is exactly that, It is looking for the ID
example:
var chart = new google.visualization.PieChart(document.getElementById('chart_div'));
You WILL get this error “Container is not defined” for Google Charts if you are missing
that ID
Thus a Div with that chart_div id would fix this
There must be line where you load the types of visualization you want to have in your web page. It looks like this
google.load("visualization", "1", {packages: ["corechart"]});
Here I am loading package corechart. Place this line as the first line after in the <script> tag inside your HTML page like index.html. That should solve he problem.
use $timeout before init, it works well, else you need to destroy the instance
Just wanted to mention, this happened for me after a simple mistake. I changed
var data = new google.visualization.DataTable(huh);
to the following in an effort to change the chart type:
var data = new google.visualization.BarChart(huh);
but this is all wrong, you change the chart where you mention the container:
var chart = new google.visualization.ComboChart(document.getElementById('chart_div'));

Coding with Dojo, received error 'dijit.byId(...)' is null or not an object

I see many references to this error on the web, but they are not helping me. I guess i am new enough to this that i need a specific answer for my problem.
I am attaching the first portion of code on a page that i am running. the last line i show is the line that is creating the error stating in the title. Please let me know if you have any suggestions.
Thank you!
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
<head>
<title>CDI Web Portal</title>
<meta http-equiv="content-type" content="text/html; charset=utf-8"/>
<script src="js\dojo\dojo.js" type="text/javascript"></script>
<script type="text/javascript" src="http://www.google.com/jsapi?key=ABQIAAAA5a4NhilcmrdMQ5e3o22QWRQWrGbhbxAguaJ-a4SLWYiya7Z2NRTDfQBdxmHdf5ydkZYLZTiz1tDXfg"></script>
<script type="text/javascript" src="ge-poly-fit-hack.js"></script>
<script type="text/javascript" src="kmldomwalk.js"></script>
<style type="text/css">
#import "js/dijit/themes/tundra/tundra.css";
#import "js/dojo/resources/dojo.css";
</style>
<style type="text/css">#import "index.css";</style>
<script type="text/javascript"> dojo.ready(function() { dojo.byId("greeting").innerHTML += ", from " + dojo.version; }); </script>
<script type="text/javascript">
// <![CDATA[
djConfig = { parseOnLoad: true };
// google.load("dojo", "1.6.1");
google.load("maps", "2");
google.load("earth", "1");
var g_ge;
var g_earthDisabled = false;
var g_kmlObject;
google.setOnLoadCallback(function() {
dojo.require('dijit.layout.BorderContainer');
dojo.require('dijit.layout.SplitContainer');
dojo.require('dijit.layout.ContentPane');
dojo.require('dijit.Tree');
//dojo.require('CheckboxTree');
dojo.require('dijit.form.CheckBox');
dojo.require('dijit.form.Button');
dojo.require('dijit.form.TextBox');
dojo.require('dojo.data.ItemFileWriteStore');
dojo.require('dojo.parser');
dojo.require('dojo.cookie');
dojo.require('dojo.fx');
dojo.addOnLoad(function() {
// load checkboxtree
var scpt = document.createElement('script');
scpt.src = "dijit.CheckboxTree.js";
document.body.appendChild(scpt);
{ dijit.byId('load-button').setDisabled(true) };
// build earth
google.earth.createInstance(
'map3d',
function(ge) {
g_ge = ge;
g_ge.getWindow().setVisibility(true);
g_ge.getNavigationControl().setVisibility(ge.VISIBILITY_AUTO);
g_ge.getLayerRoot().enableLayerById(g_ge.LAYER_BORDERS, true);
g_ge.getLayerRoot().enableLayerById(g_ge.LAYER_BUILDINGS, true);
dijit.byId('load-button').setDisabled(false);
checkAutoload();
},
function() {
g_earthDisabled = true;
dijit.byId('load-button').setDisabled(true);
From your comment, you used declarative syntax to create the dijit, i.e. <button id="load-button" dojoType="dijit.form.Button" onclick="loadKml();">. If the declarative syntax is used, the dijit is actually created after the page is loaded. So you should put the code to use the dijit in the Dojo's load callback, i.e. inside of dojo.addOnLoad callback.
But your code is bad formatted and mingled with Google Maps load callback, it's not easy to inspect the code. My suggestion would be to wrap the dijit.byId('load-button').setDisabled(true); with dojo.addOnLoad, like below:
dojo.addOnLoad(function() {
dijit.byId('load-button').setDisabled(true);
});
It means that you've got a dojo object rather than a dijit object -- or possibly no object named load-button at all, since it's not clear from this where load-button is being created. Make sure there is an object with id="load-button" that was created with dijit.