Some problems with execute_script

Good morning to all of you.
Can someone explain to me why example 1 below does not work when example 2, below, works?
To run these examples; install them in the Sketchup.temp_dir directory under file names:
For example 1 “right gears.rb” and “engrenagesdroits.html”,

require 'sketchup.rb'

html_dialog = UI::HtmlDialog.new 
path = Sketchup.temp_dir + "/engrenagesdroits.html"
 puts "path = #{path.inspect}"
html_dialog.set_file path 
@label = "un autre label"
js_command = "loadlabel("+@label+")"
html_dialog.execute_script(js_command)
html_dialog.show
<!DOCTYPE html>

<html>
    <head>
        <title>Engrenages droits</title>
        <meta http-equiv="content-type" content="text/html; charset=UTF-8"/>
        <meta http-equiv="MSThemeCompatible" content="Yes"/>
        <meta http-equiv="X-UA-Compatible" content="IE=edge"/>
<link href="style/style.css" rel="stylesheet">

    </head>
     <body > 
       
 
<p></p>
<form >
    <script type="text/javascript">
        function init_dialog(){alert ("Amorce du dialogue")};
        function loadlabel(label){   
            alert ("label transmis " + label + " ");
           document.getElementById('label1').innerHTML = label; 
           document.getElementById('label2').innerHTML = 'Module';  
           document.getElementById('label3').innerHTML = 'Nombre de dents';  
          }

    </script>
 <label id="label1" >label1</label>  
    <br>
  <input type="text" id="theinput" name="theinput" value ="20°"/>

<select name="thelist"   onChange="combo(this, 'theinput')" onMouseOut="comboInit(this, 'theinput')" >
  <option>20°</option>
  <option>25°</option>
  <option>14°30</option>
</select>

<br>

<p></p>
 <label id="label2" for="part">Label2</label> 
  <br>
  <input type="text" id="part" name="part" required pattern="[0-9]{1,5}"
       title="Part numbers consist of 3 uppercase letters followed by 4 digits."/>
  <br>
  <p></p>
  <label id="label3" for="part">Label3</label> 
  <br>
  <input type="text" id="part" name="part" required pattern="[0-9]{5}"
       title="Part numbers consist of 3 uppercase letters followed by 4 digits."/>
  <br>
  <p></p>
<input type="submit" formnovalidate value="Save">
<input type="submit" value="Submit">
</form>
<script src="scripts/main.js">
</script>
<button onclick="javascript:loadlabel('Angle de pressions');">Cliquer pour changer les labels ci-dessous</button>

  </body>
    
</html>

For example 2 “point_checker.rb” and “point_checker2.html”.

# Create a class that extends EntityObserver 
class EntObserver < Sketchup::EntityObserver

  # Invoquerd when the Entity changes
  def onChangeEntity(entity)
    f_count = 1

   # Execute the script with the vertex locations
   for v in entity.vertices
     args = "'" + v.position.x.to_s + "', '" + 
      v.position.y.to_s + "', '" + v.position.z.to_s + "'"
     $wd.execute_script("setPoint" + f_count.to_s + "(" +
       args + ")")
     f_count = f_count + 1
    end
  end

  # Invoquerd when the Entity is deleted
  def onEraseEntity(entity)
    $wd.execute_script("faceDeleted()")
  end

end

# Add an Edge and a Face to the model 
ents = Sketchup.active_model.entities 
face = ents.add_face [0,0,0], [10,0,0], [10,10,0], [0,10,0]

# Associate a new observer with the face and edge 
obs = EntObserver.new 
face.add_observer obs

# Create a WebDialog and set its HTML 
$wd = UI::WebDialog.new "Point Checker" 
path = Sketchup.temp_dir + "/point_checker2.html"

$wd.set_file path 
$wd.show
<html>
  <head>
    <script type="text/javascript">
     function setPoint1(c1, c2, c3)
     {
       document.getElementById("pt1").innerHTML = c1 + ", " + c2 +
        ", " + c3
     }

     function setPoint2(c1, c2, c3)
     {
       document.getElementById("pt2").innerHTML = c1 + ", " + c2 +
        ", " + c3
     }

     function setPoint3(c1, c2, c3)
     {
       document.getElementById("pt3").innerHTML = c1 + ", " + c2 +  
        ", " + c3
     }

     function setPoint4(c1, c2, c3)
     {
       document.getElementById("pt4").innerHTML = c1 + ", " + c2 +
       ", " + c3
     }
     function faceDeleted()
     {
       document.getElementById("pt1").innerHTML = "Face deleted"
       document.getElementById("pt2").innerHTML = "Face deleted"
       document.getElementById("pt3").innerHTML = "Face deleted"
       document.getElementById("pt4").innerHTML = "Face deleted"
     }
   </script>
  </head>
  
  <body>
   <!-- Location of the first vertex -->
    Point 1:
   <b id="pt1">Initial position</b>
   <br />
   
   <!-- Location of the second vertex -->
    Point 2:
   <b id="pt2">Initial position</b>
   <br />
   <!-- Location of the third vertex -->
    Point 3:
   <b id="pt3">Initial position</b>
   <br />
   <!-- Location of the fourth vertex -->
    Point 4:
   <b id="pt4">Initial position</b>
   <br />   
  </body>
</html>

If it is possible to operate example 1, how do you do it?
Thank you
On SkectchUp Make Version 17.2.2555 64 Bits
Windows 10 Family Version 8:2 PM

The code samples turned out incomplete (and haaard to read). Can you click the :pencil2: (Edit) button on your post and edit your post, delete the broken code samples, click the </> button to create a “code block” and paste each code sample again into a code block?

Thanks!

1 Like

The statement html_dialog.show loads the dialog window and the CEF instance then loads the HTML into it.

You are calling execute_script upon something that has not yet loaded.

Secondly, use a JS onload event to signal the Rubyside that the HTML page is loaded and ready to receive data via execute_script.

Thirdly, your HTML file should be within your extension folder. Putting it in the temp folder just adds complexity.

OK Thanks Dan

will then become js_command = "loadlabel(un autre label)" while, what you need is: js_command = "loadlabel(\"un autre label\")"

So you should do:

@label = "un autre label"
js_command = "loadlabel(\""+@label+"\")"
1 Like

Sorry kengey, your suggestion doesn’t change anything. Thank you anyway.

Open the developer console (right click somewhere and press Inspect Element) in the HtmlDialog then enter loadlabel("un autre label") in the javascript console.
What output do you get?

Also, I see you are calling execute_script right after opening the dialog. The JS code in that dialog might not be loaded yet when ruby already tries to call it.

Hello Kengey.
Here are actually the sources that are being tested, until the unsuccessful:

require 'sketchup.rb'

html_dialog = UI::HtmlDialog.new 
path = Sketchup.temp_dir + "/engrenagesdroits.html"
puts path
html_dialog.set_file path 
@label = "un autre label"
@js_command = "loadlabel(\""+@label+"\")"

html_dialog.show
#=begin
html_dialog.add_action_callback("say_arg") {|dialog, arg|
puts "The callback argument is: " + arg.to_s 
html_dialog.execute_script(@js_command)
}
#=end
#html_dialog.execute_script(@js_command) ; puts "17"
puts "18"
<!DOCTYPE html>

<html>
    <head>
        <title>Engrenages droits</title>
        <meta http-equiv="content-type" content="text/html; charset=UTF-8"/>
        <meta http-equiv="MSThemeCompatible" content="Yes"/>
        <meta http-equiv="X-UA-Compatible" content="IE=edge"/>
<link href="style/style.css" rel="stylesheet">

    </head>
     <body > 
       
 
<p></p>
<form onload="javascript:init_dialog">
    <script type="text/javascript">
       // window.location = "skp:say_arg@Hello world!";
        function init_dialog(){window.location = "skp:say_arg@Hello world!";alert ("Amorce du dialogue")};
        function loadlabel(label){   
            alert ("label transmis " + label + " ");
           document.getElementById('label1').innerHTML = label; 
           document.getElementById('label2').innerHTML = 'Module';  
           document.getElementById('label3').innerHTML = 'Nombre de dents';  
          }

    </script>
 <label id="label1" >label1</label>  
    <br>
  <input type="text" id="theinput" name="theinput" value ="20°"/>

<select name="thelist"   onChange="combo(this, 'theinput')" onMouseOut="comboInit(this, 'theinput')" >
  <option>20°</option>
  <option>25°</option>
  <option>14°30</option>
</select>

<br>

<p></p>
 <label id="label2" for="part">Label2</label> 
  <br>
  <input type="text" id="part" name="part" required pattern="[0-9]{1,5}"
       title="Part numbers consist of 3 uppercase letters followed by 4 digits."/>
  <br>
  <p></p>
  <label id="label3" for="part">Label3</label> 
  <br>
  <input type="text" id="part" name="part" required pattern="[0-9]{5}"
       title="Part numbers consist of 3 uppercase letters followed by 4 digits."/>
  <br>
  <p></p>
<input type="submit" formnovalidate value="Save">
<input type="submit" value="Submit">
</form>
<script src="scripts/main.js">
</script>
<button onclick="javascript:loadlabel('Angle de pressions');">Cliquer pour changer les labels ci-dessous</button>

  </body>
    
</html>

The “Cliquer pour changer les labels ci-dessous” button works well, as does running live via the JavaScript console. But impossible to run directly loadlabel right after loading the web dialogue.
The Javascript console tells me all the time the following message: "Failed to clear temp storage: It was determined that certain files are unsafe for access within a web application, or that too many calls are being made on file resources. “SecurityError”

NOTES:

(1)

These two meta elements are MS Internet Explorer specific.
(They would only be used on Windows platform and using the old deprecated UI::WebDialog class windows, which should only be used for SketchUp 2016 and older.)

The UI::HtmlDialog class uses a Chromium browser window.
So, I would expect that these elements will just be ignored.


(2)

The UI::HtmlDialog class should use the predefined sketchup JavaScript object to communicate back to SketchUp Ruby. Example:

        function init_dialog(){
            sketchup.say_arg("Hello world!");
            alert("Amorce du dialogue");
        };

(3)

You should learn to leverage Ruby string interpolation rather than clunky string concatenation calls.
Each call to String#+ creates another new String object, so your statement begins with 3 Strings, and creates 2 more.

See the primer on String literals:

Ex:

js_command = %[loadlabel("#{@label}");]

(Ie, % and %Q are both double-quoted strings. %q is a single-quoted string. Any set of brackets or braces can be used as the delimiters. Since the JS function call uses parenthesis, and Ruby string interpolation uses #{}, I avoid them and usually use square bracket delimeters for building JS command strings.)


(4)

Rather than mix your JavaScript within HTML body elements, it is more usual to put one <script> element within the <head> of the document.

Sometimes, when it is necessary, there can also be a <script> element that follows the <body> element when some JavaScript must run or attach listeners to body elements after they have been defined.


(5)

As I had said previously …

But … I do not see the code properly setup as a SketchupExtension.

The code is all evaluating within the toplevel Ruby ObjectSpace.

ALL of your code must be within your top level module namespace, and then within an extension specific submodule of this. (Ie, defining an instance variable at the top level of Ruby is a “no-no”.)

Good morning, Dan.
Thanks for the comments. Unfortunately despite all the changes in agreement with them it still does not work.
Obviously, I don’t plan to install my future app in Sketchup.temp_dir. These are just bits and pieces of testing programs.
In the end the files being tested are:

require 'sketchup.rb'

html_dialog = UI::HtmlDialog.new 
path = Sketchup.temp_dir + "/engrenagesdroits.html"

html_dialog.set_file path 
@label = "un autre label"
@js_command = "loadlabel(\""+@label+"\")"
@js_command = %[loadlabel("#{@label}");]
html_dialog.show
#=begin
html_dialog.add_action_callback("say_arg") {|dialog, arg|
  puts "The callback argument is: " + arg.to_s 
#  html_dialog.execute_script(@js_command)
}
#=end
html_dialog.execute_script(@js_command) ; puts "17"
puts "18"
<!DOCTYPE html>

<html>
    <head>
        <title>Engrenages droits</title>
 <meta http-equiv="content-type" content="text/html; charset=UTF-8"/>
<link href="style/style.css" rel="stylesheet">

    </head>
     <body > 
       
 
<p></p>
<form onload="javascript:init_dialog">
    <script type="text/javascript">
           function init_dialog(){
            sketchup.say_arg("Hello world!");
            alert("Amorce du dialogue");
        };
        function loadlabel(label){   
            alert ("label transmis " + label + " ");
           document.getElementById('label1').innerHTML = label; 
           document.getElementById('label2').innerHTML = 'Module';  
           document.getElementById('label3').innerHTML = 'Nombre de dents';  
          }

    </script>
 <label id="label1" >label1</label>  
    <br>
  <input type="text" id="theinput" name="theinput" value ="20°"/>

<select name="thelist"   onChange="combo(this, 'theinput')" onMouseOut="comboInit(this, 'theinput')" >
  <option>20°</option>
  <option>25°</option>
  <option>14°30</option>
</select>

<br>

<p></p>
 <label id="label2" for="part">Label2</label> 
  <br>
  <input type="text" id="part" name="part" required pattern="[0-9]{1,5}"
       title="Part numbers consist of 3 uppercase letters followed by 4 digits."/>
  <br>
  <p></p>
  <label id="label3" for="part">Label3</label> 
  <br>
  <input type="text" id="part" name="part" required pattern="[0-9]{5}"
       title="Part numbers consist of 3 uppercase letters followed by 4 digits."/>
  <br>
  <p></p>
<input type="submit" formnovalidate value="Save">
<input type="submit" value="Submit">
</form>
<script src="scripts/main.js">
</script>
<button onclick="javascript:loadlabel('Angle de pressions');">Cliquer pour changer les labels ci-dessous</button>

  </body>
    
</html>

(6)

I would suggest to attach callbacks before creating the window via dialog.show.

This is because the links between Ruby and the CEF window are done with the #show call.


(7)

Try this alternative to the <form> onload event …
change:

<form onload="javascript:init_dialog">

to:

<form>

At the bottom of your JavaScript file, add:

    </body>

    <script>
        // onReadyStateChange event is an alternative to load event.
        document.onreadystatechange = function () {
            if (document.readyState === 'complete') {
                // Call the dialog's ready callback:
                sketchup.ready();
            }
        }
    </script>
</html>

And in your Ruby file, before showing the dialog, add:

html_dialog.add_action_callback("ready") { |dialog|
  html_dialog.execute_script(@js_command)
}

You should test code in the proper environment for how it will load and run normally, not abnormally.

How are you loading this test ?

Good morning, Dan.
OK. This version works. Thank you very much.:

require 'sketchup.rb'

html_dialog = UI::HtmlDialog.new 
path = Sketchup.temp_dir + "/engrenagesdroits.html"

html_dialog.set_file path 
@label = "un autre label"
js_command = %[loadlabel("#{@label}");]
html_dialog.add_action_callback("ready") { |dialog|
  html_dialog.execute_script(js_command)
}
html_dialog.show

<!DOCTYPE html>

<html>
    <head>
        <title>Engrenages droits</title>
 <meta http-equiv="content-type" content="text/html; charset=UTF-8"/>
<link href="style/style.css" rel="stylesheet">

    </head>
     <body > 
       
 
<p></p>
<form>
    <script type="text/javascript">
  
        function loadlabel(label){   
            
           document.getElementById('label1').innerHTML = label; 
           document.getElementById('label2').innerHTML = 'Module';  
           document.getElementById('label3').innerHTML = 'Nombre de dents';  
          }

    </script>
 <label id="label1" >label1</label>  
    <br>
  <input type="text" id="theinput" name="theinput" value ="20°"/>

<select name="thelist"   onChange="combo(this, 'theinput')" onMouseOut="comboInit(this, 'theinput')" >
  <option>20°</option>
  <option>25°</option>
  <option>14°30</option>
</select>

<br>

<p></p>
 <label id="label2" for="part">Label2</label> 
  <br>
  <input type="text" id="part" name="part" required pattern="[0-9]{1,5}"
       title="Part numbers consist of 3 uppercase letters followed by 4 digits."/>
  <br>
  <p></p>
  <label id="label3" for="part">Label3</label> 
  <br>
  <input type="text" id="part" name="part" required pattern="[0-9]{5}"
       title="Part numbers consist of 3 uppercase letters followed by 4 digits."/>
  <br>
  <p></p>
<input type="submit" formnovalidate value="Save">
<input type="submit" value="Submit">
</form>
<script src="scripts/main.js"></script>
<script>
        // onReadyStateChange event is an alternative to load event.
        document.onreadystatechange = function () {
            if (document.readyState === 'complete') {
                // Call the dialog's ready callback:
                sketchup.ready();
            }
        }
    </script>

<button onclick="javascript:loadlabel('Angle de pressions');">Cliquer pour changer les labels ci-dessous</button>

  </body>
    
</html>

For tests I just save the html file in the directory Sketchup.temp_dir and I run the ruby file in my test directory.
Thank you again. :grinning: