Hi, Is there any chance to make Titanium JavaScript (Ti.App.fireEvent, Ti.API.log) available in WebViews for non local URLs?
2 Answers
Accepted Answer
Here is my own solution after going deep into Titanium source code! It's for iOS only(I'm working for an Android version):
You first need to load a local page into the WebView in order to get the Ti.pageToken for that WebView. Then you have to send this pageToken and your ApplicationID in the request for your remote page.
Then in your remote page, you will include the following script, replace TI_PAGE_TOKEN and TI_APPLICATION_ID with the values you sent from your app:
<script type="text/javascript"> window.Titanium={}; window.Ti=Titanium; Ti.pageToken = TI_PAGE_TOKEN; Ti.appId = 'TI_APPLICATION_ID'; </script>and also the following script (this is the main Ti JS script from TiUIWebView.m, which I've modified Ti._broker function to make it cross-domain compatible):
<script type="text/javascript"> Ti.App={};Ti.API={};Ti.App._listeners={};Ti.App._listener_id=1;Ti.App.id=Ti.appId;Ti.App._xhr=XMLHttpRequest; Ti._broker=function(module,method,data){try{var url='app://'+Ti.appId+'/_TiA0_'+Ti.pageToken+'/'+module+'/'+method+'?'+Ti.App._JSON(data,1); (new Image()).src = url;}catch(X){}}; Ti._hexish=function(a){var r='';var e=a.length;var c=0;var h;while(c<e){h=a.charCodeAt(c++).toString(16);r+='\\u';var l=4-h.length;while(l-->0){r+='0'};r+=h}return r}; Ti._bridgeEnc=function(o){return'<'+Ti._hexish(o)+'>'}; Ti.App._JSON=function(object,bridge){var type=typeof object;switch(type){case'undefined':case'function':case'unknown':return undefined;case'number':case'boolean':return object; case'string':if(bridge===1)return Ti._bridgeEnc(object);return'\"'+object.replace(/\"/g,'\\\\\"').replace(/\\n/g,'\\\\n').replace(/\\r/g,'\\\\r')+'\"'} if((object===null)||(object.nodeType==1))return'null';if(object.constructor.toString().indexOf('Date')!=-1){return'new Date('+object.getTime()+')'} if(object.constructor.toString().indexOf('Array')!=-1){var res='[';var pre='';var len=object.length;for(var i=0;i<len;i++){var value=object[i]; if(value!==undefined)value=Ti.App._JSON(value,bridge);if(value!==undefined){res+=pre+value;pre=', '}}return res+']'}var objects=[]; for(var prop in object){var value=object[prop];if(value!==undefined){value=Ti.App._JSON(value,bridge)} if(value!==undefined){objects.push(Ti.App._JSON(prop,bridge)+': '+value)}}return'{'+objects.join(',')+'}'}; Ti.App._dispatchEvent=function(type,evtid,evt){var listeners=Ti.App._listeners[type];if(listeners){for(var c=0;c<listeners.length;c++){var entry=listeners[c];if(entry.id==evtid){entry.callback.call(entry.callback,evt)}}}};Ti.App.fireEvent=function(name,evt){Ti._broker('App','fireEvent',{name:name,event:evt})};Ti.API.log=function(a,b){Ti._broker('API','log',{level:a,message:b})};Ti.API.debug=function(e){Ti._broker('API','log',{level:'debug',message:e})};Ti.API.error=function(e){Ti._broker('API','log',{level:'error',message:e})};Ti.API.info=function(e){Ti._broker('API','log',{level:'info',message:e})};Ti.API.fatal=function(e){Ti._broker('API','log',{level:'fatal',message:e})};Ti.API.warn=function(e){Ti._broker('API','log',{level:'warn',message:e})};Ti.App.addEventListener=function(name,fn){var listeners=Ti.App._listeners[name];if(typeof(listeners)=='undefined'){listeners=[];Ti.App._listeners[name]=listeners}var newid=Ti.pageToken+Ti.App._listener_id++;listeners.push({callback:fn,id:newid});Ti._broker('App','addEventListener',{name:name,id:newid})};Ti.App.removeEventListener=function(name,fn){var listeners=Ti.App._listeners[name];if(listeners){for(var c=0;c<listeners.length;c++){var entry=listeners[c];if(entry.callback==fn){listeners.splice(c,1);Ti._broker('App','removeEventListener',{name:name,id:entry.id});break}}}}; </script>you may have the above script (second one) in your Resources folder named 'Ti.iOS.js' and then include it in your remote page as this:
<script type="text/javascript" src="app://Resources/Ti.iOS.js"></script>Tested with Ti SDK 1.4.
Parham
I have run into this issue before, and have found a type of work around, although it's not pretty.
What I did was load the remote site, but instead of just showing the remote site directly in a webview, save the contents of the webview as a local file, then open the local file in the webview. You can then use fireEvent to run the TI code.
I have to say I've found the non ability to run JavaScript on remote web views in Titanium especially frustrating, as I now need to do it on both IOS and Android. The work around above works ONLY when you can hard code the page you are loading into the app, as if you click a link in the webview, it just loads the link contents directly. You also have to modify the HTML code you are importing, as it won't work if it's relative. Their may be a way to use an event to save the remote content locally when clicking a remote link, but I haven't tried too hard to do that.
I SURE would love the ability to turn on JS for remotely loaded webviews. If anyone out there knows how to modify the TI source code for that, I'd be very interested.
Thanks,
Peter
Your Answer
Think you can help? Login to answer this question!