Attach lift.js to something else than the body element

79 views
Skip to first unread message

sebastian....@gmail.com

unread,
Sep 22, 2017, 7:25:33 AM9/22/17
to Lift
Hi,
i am currently developing an application which needs to be put into some other website.
I don't want to use an iframe, so the plan is to have some js opening a bootstrap modal on the site and deliver the the modals content from my lift app.
That in itself should not be that much of a problem but i need to attach the lift session.id and lift.js to a div since the modal does not contain a new body element.

How do i do that? 
I could not find any docs on how that js is generated the first place. 

Best regards
Sebastian

Antonio Salazar Cardozo

unread,
Sep 24, 2017, 9:23:05 PM9/24/17
to Lift
`lift.js` is not generated since Lift 3.0.0, but rather is bundled with the lift-webkit JAR.

The HTML element that attaches it is generated based on the LiftRules.autoIncludeAjaxCalc
setting, which is a FactoryMaker. You can default it to false by using
LiftRules.autoIncludeAjaxCalc.default.set(() => false), or, in your case, you probably want to set
it on a per-request basis. For that, you can do:

  LiftRules.autoIncludeAjaxCalc.request.set(() => false)

In the context of your request. If you want to set that decision up during boot, you can look at

LiftRules.autoIncludeComet decides whether or not the session's unique id will be attached to
the HTML. It can only make decisions based on the current session, however.

-----------------------

Now of course the above answers the "wrong" half of the question :) The real question is how
you get those two things into a page that doesn't have a `body` element. The unique id is not
a secret (the session id is, but it's not the same thing as the unique id that is sent down to the
client), so you can just access it as `uniqueId` on your LiftSession instance. Likewise, the Lift
JS doesn't change locations; you'll always find it (for Lift 3.0.0+) at:

  contextPath + "/" + LiftRules.resourceServerPath + "/lift.js"

Note also that lift.js needs to be initialized. You can get the JS that initializes it by invoking:

      LiftRules.javaScriptSettings.vend().map { settingsFn =>
        LiftJavaScript.initCmd(settingsFn(this))
      }

That will give you a `Box[JsCmd]` with the initialization JS.

lift.js looks for the Lift session unique id in the body's data-lift-session-id attribute, so you'll
want to set that before you call the initialization JS.

I hope this helps... Most of this isn't really set up or structured for consumption outside of
the framework at the moment, which is why it's not exactly a 1-step process.

Let us know if anything in there is hard to follow!
Thanks,
Antonio

sebastian....@gmail.com

unread,
Sep 25, 2017, 11:24:49 AM9/25/17
to Lift
Hi thanks for your help.

Well it is somewhat hard to follow ;)

Attaching the lift.js via a snippet was not that hard but the rest is quite puzzling.
I set the session-id via js but i don't know if that is done correctly.

My (maybe totally wrong) idea was a setup like:

Some surround with, which should surround the modal with the lift stuff:

<div  class="row" data-lift="surround?with=default_empty;at=content">
   <div data-lift="embed?what=templates-hidden/modal"></div>
</div>


The surrounding default_empty.html

<div data-lift="ExternalEmbed">

    <div id="content"></div>

    <script id="lift_js_set_id"></script>
   <script type="text/javascript" id="lift_js" src=""></script>
   <script id="lift_js_init"></script>
</div>


The snippet looks like 
object ExternalEmbed {

  def getInitCs =  LiftRules.javaScriptSettings.vend().map { settingsFn =>
    LiftJavaScript.initCmd(settingsFn(S.session.openOrThrowException("session fail")))
  }


  def render: CssSel = {
    val sessionId: String = S.session.map(_.uniqueId).openOrThrowException("fail")

    "#lift_js_set_id" #> <script>document.body.setAttribute("data-lift-session-id", "{sessionId}")</script> &
    "#lift_js [src]" #> (S.hostAndPath +  "/" + LiftRules.resourceServerPath + "/lift.js") &
    "#lift_js_init" #> <script>{getInitCs.openOrThrowException("bleh")}</script>
  }


The modal is just some standard html with snippes, some ajaxButtons, idMemoize and what not, which works fine inside the app itself.

Also there gets <script type="text/javascript" src="/lift/page/F700971713328Y3CA35.js"></script> attached to the page at the end.

Where does that happen?
It would probably work if the path was absolute(?).

I have no clue if i need that session stuff you linked and how to set it up.
It would be nice to have the modal be in a session as long it is opened.

Best regards
Sebastian

Antonio Salazar Cardozo

unread,
Sep 25, 2017, 2:55:44 PM9/25/17
to Lift
So, if you're serving this up and wanting to push it into a different page, you may
want to set `LiftRules.liftPath` to include the absolute URL to your server. That would
allow the generated scripts and such to have the full URL.

There aren't many public hooks into that page-scoped script file atm (the one that is
at /lift/page/...). We could probably improve that. It's generated as long as page-specific
JS is needed, which includes:

 - S.appendJs stuff
 - The JS init command (which you're getting from getInitCs right now)
 - Any extracted event handlers.

More likely than not, only the middle one is applicable to you. If that's true, you can
also, in your render call, do LiftRules.javaScriptSettings.request.set(() => Empty). That
will remove the JS init command from Lift's list of concerns, and should suppress the
page-scoped JS file from being attached at all.

From what I can see, that's really the only thing that's left in your case. The JS init
command should kick off Lift's GC process, which will keep the session alive. Not
100% sure off the top of my head if this will smash into cross-origin request limitations
though… It may very well do so.
Thanks,
Antonio

sebastian....@gmail.com

unread,
Oct 12, 2017, 8:25:22 AM10/12/17
to Lift

Hi,
finally i got some time to have a look into this.

So i set up a simple page loading the modal and all the lift js stuff.
I also put the content of lift.js and the site js stuff into the file itself since i don't use any S.attachJs.
Also i tried to attach the data-lift-gc attribute in order to have the lift gc running.
However i was unable to find the gc id so i just used the normal id.

This works fine in Chrome but not in Firefox.
Firefox immediately loses its session.
Looking at the Req i found that Chome supplies the JSESSIONID in the cookies while Firefox does not.

My guess is, that i somehow need to set a valid session/gc id and or some cookies but i have no clue how to  get those. Sifting through the lift sources i could not even find where those ids are generated.

Without the data-lift-gc attribute it does not work at all. 
Disabling lift gc in the Boot.scala does not help either (i want that anyway i guess?).

By the way there seems to be no way to set the LiftRules.liftPath i found 
LiftRules.calculateContextPath = () => Full("http://localhost:8080")
But that causes every resource (like fobo.jquery) to throw exceptions.

Is there a way to manually set a session up and get all the ids needed for that?

Best regards
Sebastian

Antonio Salazar Cardozo

unread,
Oct 13, 2017, 11:31:22 AM10/13/17
to Lift
The cookies thing is unfortunately independent from the gc id. The gc id
is solely used to make sure any SHtml-bound fields and functions remain
available. But if you're communicating without the JSESSIONID cookie things
will get problematic no matter what on the session front.


If you're using CORS, this might also be related: https://quickleft.com/blog/cookies-with-my-cors/

I haven't done much in this area in some time, so I'm a bit fuzzy.
Thanks,
Antonio
Reply all
Reply to author
Forward
0 new messages