Extjs - Generating a unique url for the tabs - extjs

I understand that ExtJS uses AJAX for all server side communication, and that ideally there would be only one page per application. But I am exploring the possibility of generating a unique url for a ExtJS tab which the user can then copy from the address bar for later use(traditional web application approach - making a page bookmarkable). Please let me know if anyone has done anything similar.

You can make use of the "hash". This is the portion of the URL which follows the "#" character.
If you only need to react to the hash at time of page load to support the bookmarking feature then you can get away with something like:
Ext.onReady(function() {
var tabPanel = new Ext.TabPanel({
// Configure for use in viewport as needed.
listeners: {
tabchange: function( tabPanel, tab ) {
window.location.hash = '#'+ tab.itemId;
}
}
});
var token = window.location.hash.substr(1);
if ( token ) {
var tab = tabPanel.get(token);
if ( ! tab ) {
// Create tab or error as necessary.
tab = new Ext.Panel({
itemId: token,
title: 'Tab: '+ token
});
tabPanel.add(tab);
}
tabPanel.setActiveTab(tab);
}
});
You may also choose to go further and employ the hashchange event supported in recent versions of most browsers. This will allow you to react to the hash being changed by either user or programmatic means after the page has finished loading:
if ( 'onhashchange' in window ) {
window.onhashchange = function() {
var token = window.location.hash.substr(1);
// Handle tab creation and activation as above.
}
}
It is worth noting that the Ext.History singleton promises functionality similar to this. However, as of ExtJS 3.3.1 it has not been given support for the hashchange event and is instead wholly dependent on a polling interval and a hidden iframe hack. I was not satisfied with its performance in modern browsers - IE in particular - until I rewrote it to use hashchange where available.

Related

Hide the default Gigya email share popup on click of the Gigya Share bar email icon

I am using the following code to implement the Set up Gigya share bar:
if ($window.gigya) {
// Step 1: Construct a UserAction object and fill it with data
var ua = new $window.gigya.socialize.UserAction();
ua.setLinkBack(articleUrl);
ua.setTitle($scope.title);
// Step 2: Define the Share Bar add-on's params object
var params = {
userAction: ua,
//shareButtons: 'linkedin,twitter,facebook,sina,email', // list of providers
shareButtons: 'linkedin,twitter,facebook,sina,email',
containerID: 'share-bar',
showCounts: 'none',
deviceType: 'auto',
cid: '',
wrap: true,
operationMode:'multiSelect',
onShareButtonClicked:function (e) {
console.log(e);
console.log(e.shareItem.provider);
if (e.shareItem.provider == 'email') {
var mailString = 'mailto:test#example.com'
$window.location.href = mailString;
}
}
};
// Step 3: Load the Share Bar add-on:
$window.gigya.socialize.showShareBarUI(params);
}
The above code displays the share bar provided by Gigya.
Now clicking the email option I am trying to open the default client mail (for example outlook). I see that the default email popup also get opened along with the outlook.
How to stop the default UI from opening in this case. I tried all the options but none are working for me.
Can anyone help me to know how to fix this issue.
I don't believe this behavior is supported. By design, the Share add-on, when sharing via email, constructs the actual message on the server using the UserAction passed in the request and then sends it from there. So, even if you got the default UI to not populate, how would you be getting the actual share data inside the 3rd party email program?

Given a moz-extension:// URL opened by bookmark, how can I switch to tab using extension code?

This is related to some other questions I am working on.
Say, for whatever reason, a user has bookmarked a page (call it pageURL of the pattern moz-extensions://MY-OWN-WEBEXT-ID/*) intended to be opened from a browser action context menu, and opened it in a tab, then opened many other tabs and possibly other windows. User knows the extension tab exists somewhere and doesn't want to keep opening new bookmarks, and so wants to use the add-on's browser action context menu to find the extension page's tab. Likewise, I don't want my add-on to open a duplicate tab.
The problem, since the add-on did not create the tab (a bookmark did), I have no tab ID to pass to browser.tabs.update( WebExtTab.id, { active: true } ) or window ID to pass to browser.windows.update( WebExtWindow.id, { focused: true } ). (WebExtWindow referring to a WebExtensions browser.windows.Window object, not a browser window object.
I can use browser.extension.getViews( ) to generate a list of browser window objects (aka tabs), and checking each window.location.href find that indeed the URL (and thus tab) does exist (somewhere), but I can't use that window object to focus on the tab nor to get a tab ID for browser.tabs.update().
In the case of multiple browser windows, I can't even get the right browser window to raise up given that window object, because the window objects returned by getViews have no id property with which to call browser.windows.update(). Similar to the tabs problem.
Finally, I can't use browser.tabs.query( { 'url': pageURL } ) to find the tab ID, because the url option must conform to match patterns, which FORBID using the moz-extension:// scheme.
What would be exceptionally useful was if the WebExtensions API allowed an extension to find the tabs and windows of all pages that belong to itself, regardless if those pages were opened by the add-on, manually entered, a bookmark or clicking a link.
For example, given a pageURL conforming to moz-extension://MY-OWN-WEBEXT-ID/*, one could do a browser.tabs.query and/or a browser.windows.query on a url matching the above pattern, and return a WebExt tab/window object, respectively. If such a tab/window was not opened by the WebExt API (i.e. bookmark), then generate a new object (i.e. a pseudo-create), to populate with existing data (i.e. location.href, status flags, etc) and generate new data as needed (i.e. the ID numbers), such that the returned object is usable within the context of the API.
This would fill a gap in API coverage where certain methods (i.e. getViews) return dead-end browser objects which have no hooks and no connection with the WebExt API and are thus mostly useless.
The simple answer: ++RTFM. browser.windows.getAll() will allow you to populate the windows objects with tab info. You need the permissions: [ "tabs" ] in manifest.json to get the tab.url property. But other than that, all the windows and tab objects will have an ID so that you can trivially focus window and switch active tab!
Note: This requires Firefox 52.0+ to make use of the async/await feature. Otherwise, you just have to use function generators and promises. Also, I've omitted any error checking, for demonstration purposes, but it might be a good idea to put them back in later.
async function tabCreate ( opts ) {
var pageURL = browser.runtime.getURL( opts.page + '.html' );
var extWins = await browser.windows.getAll( { populate: true, windowTypes: [ 'normal' ] } );
// Look for tab by comparing url, if url matches (i.e. tab exists), then focus window and make tab active.
for ( var extWin of extWins ) {
for ( var extTab of extWin.tabs ) {
if ( pageURL === extTab.url ) {
console.log( `My Extension->tabCreate(): Window ${extWin.id}, Tab ${extTab.id}:\n\t${extTab.url}` );
browser.windows.update( extWin.id, { focused: true } );
browser.tabs.update( extTab.id, { active: true } );
return;
}
}
}
// Otherwise, create tab.
browser.tabs.create( { url: pageURL } );
}
Opinion: I wish I didn't have to give away the tabs permission just for this feature. It would be nice if we always got our own moz-extension://MY-OWN-WEBEXT-ID/* urls, and null URLs for other tabs, without permissions given to access all tabs, but oh well.
Example Usage:
function myWebExt_Options ( ) {
tabCreate( {
'page': 'options',
'panel': 1
} );
}
browser.contextMenus.create( {
title: 'Options',
contexts: [ 'browser_action' ],
onclick: myWebExt_Options
} );
Note: I've implemented this to expect options in an opts object that has a page property, which I use as a shorthand to generate the full page URL. This is because of another question which requires passing a message to the page, which I store in opts.panel. But none of that is necessary. It could be changed to a flat string, or use the full 'getURL' generated elsewhere as a parameter. Change to suit your need and style.

Implementing google custom search in angularjs

I am trying to implement google custom search in an angular js website.
When I click on the search button it does not display me anything, but the url is updated to the url.
I have followed the steps mentioned in the documentation by google.
I am not sure what I am doing wrong?
My search bar is located on the home page as -
<gcse:searchbox-only enableAutoComplete="true" resultsUrl="#/searchresult" lr="lang_en" queryParameterName="search"></gcse:searchbox-only>
my search result has -
<gcse:searchresults-only lr="lang_en"></gcse:searchresults-only>
Any input is much appreciated.
Thanks,
You may have more than one problem happening at the same time...
1. Query Parameter mismatch
Your searchresults-only does not match the queryParameterName specified on gcse:searchbox-only.
Index.html
<gcse:searchresults-only queryParameterName="search"></gcse:searchresults-only>
Search.html
<gcse:searchresults-only queryParameterName="search"></gcse:searchresults-only>
2. Angular.js is blocking the flow of Google CSE
Under normal circumstances, Google Search Element will trigger an HTTP GET with the search parameter. However, since you are dealing with a one-page application, you may not see the query parameter. If that suspicion is true when you target resultsUrl="#/searchresult", then you have two options:
Force a HTTP GET on resultsUrl="http://YOURWEBSITE/searchresult". You may have to match routes, or something along those lines in order to catch the REST request (Ember.js is really easy to do so, but I haven't done in Angular.js yet.)
Use JQuery alongside Angular.js to get the input from the user on Index.html and manually trigger a search on search.html. How would you do it? For the index.html you would do something like below and for the results you would implement something like I answered in another post.
Index.html
<div>GSC SEARCH BUTTON HOOK: <strong><div id="search_button_hook">NOT ACTIVATED.</div></strong></div>
<div>GSC SEARCH TEXT: <strong><div id="search_text_hook"></div></strong></div>
<gcse:search ></gcse:search>
Index.js
//Hook a callback into the rendered Google Search. From my understanding, this is possible because the outermost rendered div has id of "___gcse_0".
window.__gcse = {
callback: googleCSELoaded
};
//When it renders, their initial customized function cseLoaded() is triggered which adds more hooks. I added comments to what each one does:
function googleCSELoaded() {
$(".gsc-search-button").click(function() {
$("#search_button_hook").text('HOOK ACTIVATED');
});
$("#gsc-i-id1").keydown(function(e) {
if (e.which == 13) {
$("#enter_keyboard_hook").text('HOOK ACTIVATED');
}
else{
$("#search_text_hook").text($("#gsc-i-id1").val());
}
});
}
(function() {
var cx = '001386805071419863133:cb1vfab8b4y';
var gcse = document.createElement('script');
gcse.type = 'text/javascript';
gcse.async = true;
gcse.src = 'https://cse.google.com/cse.js?cx=' + cx;
var s = document.getElementsByTagName('script')[0];
s.parentNode.insertBefore(gcse, s);
})();
I have a live version of the index.html code, but I don't make promises that will be permanently live since it is hosted in my NDSU FTP.

Store is loaded twice after data.Model.save()

I have a grid with remote data (php/mysql/json) and use a form to insert records or to edit this data.
I use the api configuration of the proxy/store. I use MVC architecture.
So, all very simple (in pseudo code):
get selected model form grid or create model
frm.loadRecord()
frm.updateRecord()
frm.getRecord().save()
and all works fine, but I noticed in the browser console that after the POST (works fine, calls either the url configured with create or the url configured with update), the store calls (GET) the url configured with retrieve twice. These calls are identical.
So functionally all works fine and I could ignore it, but now I've noticed I want it fixed.
Can anyone help me where to look? Thanks in advance.
Details:
It's all really basic:
In the controller of the gridpanel:
updateRow: function (gridpanel) {
var sm = gridpanel.getSelectionModel();
var record = sm.getLastSelected();
this.showForm(record);
}
and
showForm: function (record) {
...
formpanel.show();
var frm = formpanel.getForm();
frm.loadRecord(record);
}
In the controller of the formpanel:
submit: function(frm) {
frm.updateRecord();
frm.getRecord().save();
}
When I remove the save action the GET requests aren't called, so this seems to trigger them.
In the store:
api: {
create: '../php/api/customers.php?request=create',
read: '../php/api/customers.php?request=retrieve&scope=summary',
update: '../php/api/customers.php?request=update',
destroy: '../php/api/customers.php?request=delete'
}
The screenshot:

WMS GetFeatureInfo; multiple layers, different sources

I'm developing a web application using GeoExt, OpenLayers and having my own GeoServer to serve various maps. Still, I want to let the user add other WMS's if needed, to be able to play around with all desired layers.
Thus, my problem with the GetFeatureInfo request. Right now I have a toolbar button attached to geoext's map panel,
new GeoExt.Action({
iconCls: "feature",
map: map,
toggleGroup: "tools",
tooltip: "Feature",
control: featureControl
})
its control attribute being
var featureControl = new OpenLayers.Control.WMSGetFeatureInfo({
queryVisible: true,
drillDown: true,
infoFormat:"application/vnd.ogc.gml"
});
I've also defined an event listener to do what I really want once I receive the responses, but that is not relevant here. My problem is the following:
Considering the user clicks on a point where there are 2+ visible layers and at least one of them is from a different source, OpenLayers will have to do one AJAX request per different source and, from OpenLayers own documentation,
Triggered when a GetFeatureInfo response is received. The event
object has a text property with the body of the response (String), a
features property with an array of the parsed features, an xy property
with the position of the mouse click or hover event that triggered the
request, and a request property with the request itself. If drillDown
is set to true and multiple requests were issued to collect feature
info from all layers, text and request will only contain the response
body and request object of the last request.
so, yeah, it will obviously wont work like that right away. Having a look at the debugger I can clearly see that, giving two layers from different sources, it actually DOES the request, it's just that it doesn't wait for the first's response and jumps for the next one (obviously, being asynchronous). I've thought about doing the requests one-by-one, meaning doing the first one as stated above and once it's finished and the response saved, go for the next one. But I'm still getting used to the data structure GeoExt uses.
Is there any API (be it GeoExt or OpenLayers) option/method I'm missing? Any nice workarounds?
Thanks for reading :-)
PS: I'm sorry if I've not been clear enough, english is not my mother tongue. Let me know if something stated above was not clear enough :)
i Hope this help to someone else, I realized that: you're rigth this control make the request in asynchronous mode, but this is ok, no problem with that, the real problem is when the control handle the request and trigger the event "getfeatureinfo" so, i modified 2 methods for this control and it works!, so to do this i declare the control first, and then in the savage mode i modified the methods here is de code:
getInfo = new OpenLayers.Control.WMSGetFeatureInfo({ drillDown:true , queryVisible: true , maxFeatures:100 });
//then i declare a variable that help me to handle more than 1 request.....
getInfo.responses = [];
getInfo.handleResponse=function(xy, request) { var doc = request.responseXML;
if(!doc || !doc.documentElement) { doc = request.responseText; }
var features = this.format.read(doc);
if (this.drillDown === false) {
this.triggerGetFeatureInfo(request, xy, features);
} else {
this._requestCount++;
this._features = (this._features || []).concat(features);
if( this._numRequests > 1){
//if the num of RQ, (I mean more than 1 resource ), i put the Request in array, this is for maybe in a future i could be need other properties or methods from RQ, i dont know.
this.responses.push(request);}
else{
this.responses = request;}
if (this._requestCount === this._numRequests) {
//here i change the code....
//this.triggerGetFeatureInfo(request, xy, this._features.concat());
this.triggerGetFeatureInfo(this.responses, xy, this._features.concat());
delete this._features;
delete this._requestCount;
delete this._numRequests;
// I Adding this when the all info is done 4 reboot
this.responses=[];
}
}
}
getInfo.triggerGetFeatureInfo= function( request , xy , features) {
//finally i added this code for get all request.responseText's
if( isArray( request ) ){
text_rq = '';
for(i in request ){
text_rq += request[i].responseText;
}
}
else{
text_rq = request.responseText;
}
this.events.triggerEvent("getfeatureinfo", {
//text: request.responseText,
text : text_rq,
features: features,
request: request,
xy: xy
});
// Reset the cursor.
OpenLayers.Element.removeClass(this.map.viewPortDiv, "olCursorWait");}
Thanks, you bring me a way for discover my problem and here is the way i solved, i hope this can help to somebody else.
saheka's answer was almost perfect! Congratulations and thank you, I had the same problem, and with it I finally managed to solve it.
What I would change in your code:
isArray() does not work, change it like this: if(request instanceof Array) {...} at the first line of getInfo.triggerGetFeatureInfo()
to show the results in a popup this is the way:
My code:
getInfo.addPopup = function(map, text, xy) {
if(map.popups.length > 0) {
map.removePopup(map.popups[0]);
}
var popup = new OpenLayers.Popup.FramedCloud(
"anything",
map.getLonLatFromPixel(xy),
null,
text,
null,
true
);
map.addPopup(popup);
}
and in the getInfo.triggerGetFeatureInfo() function, after the last line, append:
this.addPopup(map, text_rq, xy);
A GetFeatureInfo request is send as a JavaScript Ajax call to the external server. So, the requests are likely blocked for security reasons. You'll have to send the requests to the external servers by a proxy on your own domain.
Then, configure this proxy in openlayers by setting OpenLayers.ProxyHost to the proper path. For example:
OpenLayers.ProxyHost = "/proxy_script";
See http://trac.osgeo.org/openlayers/wiki/FrequentlyAskedQuestions#ProxyHost for more background information.

Resources