Ext.ns("Ext.ux.grid");
Ext.ux.grid.GridMouseEvents = (function() {
function onMouseOver(e) {
processMouseOver.call(this, 'mouseenter', e);
}
function onMouseOut(e) {
processMouseOver.call(this, 'mouseleave', e);
}
function processMouseOver(name, e){
var t = e.getTarget(),
r = e.getRelatedTarget(),
v = this.view,
header = v.findHeaderIndex(t),
hdEl, row, rowEl, cell, fromCell, col;
if(header !== false){
hdEl = Ext.get(v.getHeaderCell(header));
if (!((hdEl.dom === r) || hdEl.contains(r))) {
this.fireEvent('header' + name, this, header, e, hdEl);
}
}else{
row = v.findRowIndex(t);
if(row !== false) {
rowEl = Ext.get(v.getRow(row));
if (!((rowEl.dom === r) || rowEl.contains(r))) {
this.fireEvent('row' + name, this, row, e, rowEl);
}
cell = v.findCellIndex(t);
if(cell !== false){
cellEl = Ext.get(v.getCell(row, cell));
if (!((cellEl.dom === r) || cellEl.contains(r))){
this.fireEvent('cell' + name, this, row, cell, e, cellEl);
}
if (cell != v.findCellIndex(r)) {
col = this.colModel.getColumnAt(cell);
if (col.processEvent) {
col.processEvent(name, e, this, row, cell);
}
this.fireEvent('column' + name, this, cell, e);
}
}
}
}
}
function onGridRender() {
this.mon(this.getGridEl(), {
scope: this,
mouseover: onMouseOver,
mouseout: onMouseOut
});
}
return {
init: function(g) {
g.onRender = g.onRender.createSequence(onGridRender);
}
};
})();
/* Overlay and window dialog fix (unneccessary horizontal scrollbar)
* TODO: This bug affects Ext 3.2.0. This fix may be merged into head in a
* later release and then this file can be removed.
*
* */
Ext.lib.Dom.getViewWidth = function(){
var width = self.innerWidth; // Safari
var mode = document.compatMode;
if (mode || Ext.isIE) { // IE, Gecko, Opera
width = (mode == "CSS1Compat") ? document.documentElement.clientWidth : // Standards
document.body.clientWidth; // Quirks
}
return width;
}
Ext.ns('KZ.util');
/* This cannot be used to test an uninitialised variable, only fields of an object */
KZ.util.isSet = function(variable) {
return (typeof (variable)) != 'undefined';
}
KZ.util.hideVBoxLayoutItem = function(item) {
item.hide();
//item.flex = 0;
//item.setHeight(0);
}
KZ.util.showVBoxLayoutItem = function(item) {
item.show();
//item.flex = 1;
//item.setHeight(200);
}
KZ.util.isHidden = function(vBoxLayoutItem) {
return vBoxLayoutItem.getHeight() == 0
}
KZ.util.isDigit = function(num) {
if (num == undefined || num.length>1){return false;}
if (num == ""){return false;}
var string="1234567890";
if (string.indexOf(num)!=-1){return true;}
return false;
}
KZ.util.charCmp = function(a, b) {
return (b < a) - (a < b);
}
KZ.preloadedImagesArray = new Array();
KZ.util.preloadImages = function(imageSrcArray) {
for (var i = 0; i < imageSrcArray.length; i++){
KZ.preloadedImagesArray[i]= new Image();
KZ.preloadedImagesArray[i].src = imageSrcArray[i];
}
}
KZ.util.isEmptyObject = function (obj) {
for(var prop in obj) {
if(obj.hasOwnProperty(prop))
return false;
}
return true;
}
Ext.ns('KZ');
KZ.log = function(message) {
_gaq.push(['_trackEvent', 'Error Log', message]);
if (typeof console == 'object' &&
console.log) {
console.log('KZDEBUG:' + message);
} else if(KZ.Config.isProduction == false) {
alert(message);
}
}
Ext.ns('KZ');
// This is a lazily initialised singleton, not to be instantiated
KZ.InitialSearchPanel = new function() {
this.extComponent = undefined;
var searchField;
this.isInitialised = function() {
return this.extComponent != undefined;
}
this.isVisible = function() {
return this.isInitialised() && this.extComponent.isVisible();
}
this.performSearchAndHideInitialSearchPanel = function(searchTerm) {
KZ.searchBoxPanel.performSearch(searchTerm);
this.hideInitialSearchPanelAndShowMap();
}
this.hideInitialSearchPanelAndShowMap = function() {
KZ.viewport.showTravelMap();
this.extComponent.hide();
searchField.blur();// IE bug: Prevent blinking cursor in field even though it's hidden
KZ.mapPanel.onZoomEnd.defer(2000);
}
this.showInitialSearchPanelAndHideMap = function() {
KZ.viewport.hideTravelMap();
this.extComponent.show();
// KZ.mapPanel.onZoomEnd.defer(2000);
}
this.processUserStopClick = function(event, element, store) {
var selectedStopIndex = element.getAttribute('id').split('-storeIndex-')[1];
var busStopRecord = store.getAt(selectedStopIndex);
if(busStopRecord != undefined) {
this.extComponent.fireEvent("stopSelectedEvent", busStopRecord);
this.hideInitialSearchPanelAndShowMap();
}
}
this.show = function() {
if(this.extComponent == undefined) {
searchField = new Ext.form.TextField({
id: 'initialSearchField',
allowBlank: true,
flex: 0,
autoCreate: {tag: "input", type: "text", size: "35", autocomplete: "off", spellcheck: "false"},
listeners: {
scope: this,
valid: function(textField) {
//this.hideSearchError();
},
invalid: function(textField) {
//this.setSearchError("Please enter a search term");
},
specialkey: function(field, e){
if (e.getKey() == e.RETURN || e.getKey() == e.ENTER) {
KZ.InitialSearchPanel.performSearchAndHideInitialSearchPanel(field.getValue());
}
}
},
margins: '0'
});
var searchFieldAndSearchButton = new Ext.Panel({
id: 'initialSearchFieldAndSearchButtonHolder',
layout:'hbox',
plain: true,
border:false,
bodyBorder:false,
items: [searchField,
{xtype: 'button',
id: 'initialSearchButton',
text: 'Search',
handler: function(evt, el, o) {
this.performSearchAndHideInitialSearchPanel(searchField.getValue());
_gaq.push(['_trackEvent', 'InitialSearchMethod', 'SearchUsingSearchTerm']);
},
scope:this
}
]
});
var searchUsingMapTextAndButton = new Ext.Panel({
id:'searchUsingMapTextAndButton',
hidden: !KZ.Config.enableSearchUsingMapButton,
layout:'hbox',
plain: true,
border:false,
bodyBorder:false,
items: [
{
id: 'orTextLabel',
html: ' Alternatively, you can ',
bodyBorder:false
},
{xtype: 'button',
id: 'initialSearchUsingMapButton',
text: 'Search using the Map',
handler: function(evt, el, o) {
this.hideInitialSearchPanelAndShowMap();
_gaq.push(['_trackEvent', 'InitialSearchMethod', 'SearchUsingMap']);
},
scope:this
}
]
});
var favouriteStops = KZ.userStopsStore.getFavouriteStops();
var recentStops = KZ.userStopsStore.getRecentStops();
var t = new Ext.Template(
'
{name}',
{
compiled:true
}
);
Ext.each(recentStops, function(recentStop, index) {
t.append('initial-recent-stop-list', {storeIndex: index, stopType:'recent-stop', name: recentStop.data.name});
});
Ext.each(favouriteStops, function(favouriteStop, index) {
t.append('initial-favourite-stop-list', {storeIndex: index, stopType:'favourite-stop', name: favouriteStop.data.name});
});
if(recentStops.length == 0) {
Ext.DomHelper.insertHtml('beforeEnd', Ext.getDom('initial-recent-stop-list'), Ext.getDom('no-recent-stops-message').innerHTML);
}
if(favouriteStops.length == 0) {
Ext.DomHelper.insertHtml('beforeEnd', Ext.getDom('initial-favourite-stop-list'), Ext.getDom('no-favourite-stops-message').innerHTML);
}
Ext.get('initial-recent-stop-list').on({
'click' : {
fn: function(event, element, o) {
this.processUserStopClick(event, element, KZ.userStopsStore.recentStopsStore);
},
scope: this,
delegate: 'span'
}
});
Ext.get('initial-favourite-stop-list').on({
'click' : {
fn: function(event, element, o) {
this.processUserStopClick(event, element, KZ.userStopsStore.favouriteStopsStore);
},
scope: this,
delegate: 'span'
}
});
var hideUserStops = false;
if(favouriteStops.length == 0 && recentStops.length == 0) {
hideUserStops = true;
}
this.extComponent = new Ext.Panel({
id: 'initialSearchPanel',
renderTo: 'initial-search-panel-holder',
layout:'auto',
plain: true,
//border:false,
bodyBorder:true,
padding:'12px',
items: [{contentEl: 'initialSearchPanelTitle', border:false},
{contentEl: 'initialSearchPanelPreInputBoxText', border:false},
searchFieldAndSearchButton,
{contentEl: 'initialSearchPanelPostInputBoxText', border:false},
searchUsingMapTextAndButton,
{id: 'tip', contentEl: 'tipbox', border:false},
{id:'initial-user-stop-lists-holder-panel', contentEl: 'initial-user-stop-lists-holder', border:false, hidden:hideUserStops}
]
});
}
}
}
Ext.ns('KZ');
// This is a lazily initialised singleton, not to be instatiated
KZ.HeaderMenuDialog = new function() {
var extComponent;
this.isInitialised = function() {
return extComponent != undefined;
}
this.isVisible = function() {
return this.isInitialised() && extComponent.isVisible();
}
this.getDialog = function() {
if(extComponent == undefined) {
extComponent = new Ext.Window({
title: 'Notices',
iconCls: 'notices-icon',
layout:'fit',
modal:true,
autoScroll:true,
height: 300,
width: 400,
padding:5,
draggable:false,
resizable:false,
closable:true,
closeAction:'hide',
html: ' ',
plain: true,
listeners: {
show: function() {
Ext.select('.ext-el-mask').addListener('click', function() {
extComponent.hide();
});
}
},
buttons: [{
text:'OK',
scope: this,
handler: function(){
extComponent.hide();
}
}]
});
}
return extComponent;
}
this.show = function(dialogTitle, contentUrl) {
var dialog = this.getDialog();
dialog.setTitle(dialogTitle);
dialog.show();
dialog.center();
//dialog.update("test");
dialog.load({
url: contentUrl,
nocache: false,
text: 'Loading...',
timeout: 30,
scripts: false
});
}
// Attach click handler when the dom is ready
Ext.onReady(function(){
if(Ext.get('right-menu') != undefined) {
Ext.get('right-menu').on({
'click' : {
fn: function(event, element) {
if(element.className.indexOf('dialog-popup') >= 0) {
this.show(element.innerHTML, element.getAttribute('href'));
_gaq.push(['_trackEvent', 'InformationDialogWindowPopup', element.innerHTML]);
event.stopEvent();
}
},
scope: this,
delegate: 'a'
}
});
}
}, this);
}
Ext.ns('KZ');
KZ.RouteSearchResultsContainer = function() {
this.currentRouteResults = undefined;
this.directionIndex = -1;
var selectedCallingPatternResultsStore = new Ext.data.JsonStore({
autoDestroy: false,
storeId: 'selectedCallingPatternResultsStore',
idProperty: 'id',
fields: ['id', 'smsCode', 'name', 'stopIndicator', 'towards', 'direction', 'lat', 'lng', 'routes']
});
this.displayBusRouteResults = function(busRoutesJson, directionIndex) {
this.currentRouteResults = busRoutesJson;
KZ.util.showVBoxLayoutItem(KZ.routeSearchResultsContainer.extComponent);
Ext.get('calling-pattern-results').enableDisplayMode().hide();
var staleCallingPatternResults = Ext.get('calling-pattern-results').query('.calling-pattern-item');
Ext.each(staleCallingPatternResults, function(staleResult) {
Ext.removeNode(staleResult);
});
Ext.get('calling-pattern-results').show();
var selectedDirectionIndex = 0;
if(directionIndex != undefined) {
selectedDirectionIndex = directionIndex;
}
var callingPattern = this.currentRouteResults.directions[selectedDirectionIndex];
KZ.searchResultsPanel.setResultsStatus('Bus route '
+ this.currentRouteResults.name + ' to '
+ callingPattern.name + '
'
+ 'Switch direction
');
// TODO: This should just be a custom grid render, rather than a simple template (now that we are using a store for the calling pattern).
var t = new Ext.Template([
'',
]);
t.compile();
Ext.each(callingPattern.markers, function(stop, index) {
t.append('calling-pattern-results', {stopIndex: index, stopName: stop.name});
});
KZ.searchPanel.doLayout();
Ext.get('calling-pattern-results').select('.calling-pattern-item a').on({
'mouseenter' : {
fn: function(event, htmlElement) {
var busStopIndex = htmlElement.getAttribute('rel');
var stop = this.currentRouteResults.directions[this.directionIndex].markers[busStopIndex];
var busStopRecord = Ext.StoreMgr.lookup('selectedCallingPatternResultsStore').getById(stop.id);
KZ.markerManager.setMarkerState(busStopRecord, 'hover-state');
},
scope: this
}
});
Ext.get('switch-direction').on({
'click' : {
fn: function(event, htmlElement) {
KZ.routeSearchResultsContainer.displayBusRouteResults(busRoutesJson, selectedDirectionIndex == 0 ? 1 : 0);
},
scope: this
}
});
this.extComponent.fireEvent("newSearchBoundingBoxEvent", busRoutesJson.swLatBB, busRoutesJson.swLngBB, busRoutesJson.neLatBB, busRoutesJson.neLngBB);
this.displayCallingPatternOnMap(selectedDirectionIndex);
Ext.get('route-search-container').scrollTo('top', -Ext.get('route-search-container').getHeight());
Ext.get('route-search-container').dom.scrollTop = 0;
}
this.displayCallingPatternOnMap = function(directionIndex) {
this.directionIndex = directionIndex;
selectedCallingPatternResultsStore.removeAll();
this.extComponent.fireEvent("newSearchEvent");
selectedCallingPatternResultsStore.loadData(this.currentRouteResults.directions[directionIndex].markers, false);
}
this.showStopInformation = function(event, element) {
var busStopIndex = element.getAttribute('rel');
var stop = this.currentRouteResults.directions[this.directionIndex].markers[busStopIndex];
var busStopRecord = Ext.StoreMgr.lookup('selectedCallingPatternResultsStore').getById(stop.id);
KZ.routeSearchResultsContainer.extComponent.fireEvent("stopSelectedEvent", busStopRecord);
}
Ext.get('direction-results').on({
'click' : {
fn: this.displayCallingPattern,
scope: this,
stopEvent: true,
delegate: 'a'
}
});
Ext.get('calling-pattern-results').on({
'click' : {
fn: this.showStopInformation,
scope: this,
stopEvent: true,
delegate: 'a'
}
});
this.extComponent = new Ext.Container({
id: 'route-search-container',
contentEl: 'route-search',
autoScroll: true,
hidden: true,
flex:1
});
// Ext.get('search-results-message').select('.switch-direction a').on({
// 'click' : {
// fn: function(event, htmlElement) {
// this.displayBusRouteResults(busRoutesJson, selectedDirectionIndex == 0 ? 1 : 0)
// },
// scope: this
// }
//
// });
}
Ext.ns('KZ');
KZ.RouteFilterDialog = new function() {
// private fields
var stopId, checkboxItems, allCheckbox;
this.stopInformationPanel;
this.show = function(stopInformationPanel) {
this.stopInformationPanel = stopInformationPanel;
var stopRecord = stopInformationPanel.getCurrentBusStopRecord();
stopId = stopRecord.get("id");
var allChecked = !KZ.userStopsStore.hasFilters(stopId);
allCheckbox = new Ext.form.Checkbox({
boxLabel: "Show all bus routes",
name: "allCheckbox",
checked: allChecked,
scope:this,
listeners: { check: this.allCheckboxChanged }
});
checkboxItems = [allCheckbox];
Ext.each(stopRecord.get('routes'), function(route) {
var filterOut = KZ.userStopsStore.getFilters(stopId);
var boxChecked = !filterOut || (filterOut && filterOut.indexOf(route.id) == -1);
var checkbox = new Ext.form.Checkbox({
boxLabel: route.name,
checked: boxChecked,
name: route.id,
scope:this,
listeners: { check: this.routeCheckboxChanged }
});
checkboxItems.push(checkbox);
}, this);
var checkboxGroup = new Ext.form.CheckboxGroup({
autoHeight: true,
layout: 'form',
border:false,
fieldLabel: 'Auto Layout',
columns: 1,
items: checkboxItems
});
var window = new Ext.Window({
title: 'Filter routes',
iconCls: 'filter-icon',
layout:'fit',
modal:true,
width: 200,
padding:'10 0 10 20',
draggable:false,
resizable:false,
closable:false,
closeAction:'hide',
plain: true,
items: [checkboxGroup],
listeners: {
show: function() {
Ext.select('.ext-el-mask').addListener('click', function() {
window.hide();
});
}
},
buttons: [{
text:'Ok',
scope: this,
handler: function(){
window.hide();
this.processFilter();
var filterOut = KZ.userStopsStore.getFilters(stopId);
stopInformationPanel.arrivalsGrid.applyArrivalFilter(filterOut);
stopInformationPanel.toolbar.setFilterToolBarText();
}
},{
text: 'Cancel',
handler: function(){
window.hide();
}
}]
});
window.show();
}
this.allCheckboxChanged = function(checkbox, checked) {
Ext.each(checkboxItems, function(checkbox) {
checkbox.suspendEvents(false); // to prevent recursive event triggering
checkbox.setValue(checked);
checkbox.resumeEvents();
});
}
this.routeCheckboxChanged = function(checkbox, checked) {
var allCheckboxValue = true;
Ext.each(checkboxItems, function(checkbox) {
if(checkbox.getName() != 'allCheckbox' && !checkbox.getValue()) {
allCheckboxValue = false;
return false;//break from ext loop
}
});
allCheckbox.suspendEvents(false);
allCheckbox.setValue(allCheckboxValue);
allCheckbox.resumeEvents();
}
this.processFilter = function() {
var stopRecord = KZ.userStopsStore.getStop(stopId);
var filterOutArray = [];
Ext.each(checkboxItems, function(checkbox) {
if(checkbox.getName() != 'allCheckbox' && !checkbox.getValue()) {
var routeId = checkbox.getName();
if(routeId) {
filterOutArray.push(routeId);
}
}
}, this);
KZ.userStopsStore.updateFilters(stopId, filterOutArray);
KZ.userStopsStore.persistFilterOut(stopId, filterOutArray);
}
}
Ext.ns('KZ');
KZ.UserStopsStore = function() {
var userStopsConnection;
this.recentStopsStore = new Ext.data.JsonStore({
autoDestroy: false,
storeId: 'recentStopsStore',
root: 'recentStops',
idProperty: 'id',
fields: ['id', 'smsCode', 'name', 'stopIndicator', 'towards', 'direction', 'lat', 'lng', 'routes', 'filterOut', 'staticMapUrl', 'arrivalsBoard']
});
this.favouriteStopsStore = new Ext.data.JsonStore({
autoDestroy: false,
storeId: 'favouriteStopsStore',
root: 'myStops',
idProperty: 'id',
fields: ['id', 'smsCode', 'name', 'stopIndicator', 'towards', 'direction', 'lat', 'lng', 'routes', 'filterOut', 'staticMapUrl', 'arrivalsBoard']
});
if(!KZ.util.isEmptyObject(KZ.Config.recentStopsJson)) {
this.recentStopsStore.loadData(KZ.Config.recentStopsJson, false);
}
if(!KZ.util.isEmptyObject(KZ.Config.favouriteStopsJson)) {
this.favouriteStopsStore.loadData(KZ.Config.favouriteStopsJson, false);
}
this.getRecentStops = function() {
return this.recentStopsStore.getRange();
}
this.getFavouriteStops = function() {
return this.favouriteStopsStore.getRange();
}
this.getStop = function(stopId) {
return this.recentStopsStore.getById(stopId);
}
this.addRecentStop = function(busStopRecord) {
var stopId = busStopRecord.get("id");
var existingRecord = this.recentStopsStore.getById(stopId);
var stopCount = this.recentStopsStore.getCount();
// remove the last recent stop that is not a favourite
if(stopCount + 1 > KZ.Config.maxRecentStops && existingRecord == undefined) {
this.recentStopsStore.removeAt(stopCount - 1);
}
if(existingRecord == undefined) {
var record = new this.recentStopsStore.recordType({id:stopId, name:busStopRecord.get("name"), stopIndicator:busStopRecord.get("stopIndicator"), towards:busStopRecord.get("towards"), direction:busStopRecord.get("direction"), lat:busStopRecord.get("lat"), lng:busStopRecord.get("lng"), routes: busStopRecord.get("routes"), filterOut:[] }, stopId);
record.commit();
this.recentStopsStore.insert(0, record);
}
persistRecentStop(stopId); // This will move it back to the top of the list on the server if it already exists
}
this.isFavouritesMaxLimitReached = function() {
var favouritesCount = this.favouriteStopsStore.getCount();
if(KZ.Config.maxFavouriteStops > favouritesCount) {
return false;
} else {
return true;
}
}
this.addFavouriteStop = function(busStopRecord) {
var stopId = busStopRecord.get("id");
var filterOutArray = this.recentStopsStore.getById(stopId).get("filterOut");
var record = new this.favouriteStopsStore.recordType({id:stopId, name:busStopRecord.get("name"), stopIndicator:busStopRecord.get("stopIndicator"), towards:busStopRecord.get("towards"), direction:busStopRecord.get("direction"), lat:busStopRecord.get("lat"), lng:busStopRecord.get("lng"), routes: busStopRecord.get("routes"), filterOut:filterOutArray }, stopId);
record.commit();
this.favouriteStopsStore.insert(0, record);
this.persistFavouriteStop(stopId, 0);
_gaq.push(['_trackEvent', 'My Stops', stopId, busStopRecord.data.name]);
}
var getUserStopsConnection = function() {
if(userStopsConnection == undefined) {
userStopsConnection = new Ext.data.Connection({
timeout: 8000,
autoAbort: false
});
}
return userStopsConnection;
}
var persistRecentStop = function(stopId) {
getUserStopsConnection().request({
url: '/recentStops/stopId/' + stopId + '/',
method: 'PUT',
scope: this
});
}
this.persistFavouriteStop = function(stopId, listPosition) {
getUserStopsConnection().request({
url: '/myStops/stopId/' + stopId + '/listPosition/' + listPosition + '/',
method: 'PUT',
scope: this
});
}
this.persistFilterOut = function(stopId, filterOut) {
getUserStopsConnection().request({
url: '/stopFilter/stopId/' + stopId + '/filterOut/' + filterOut +'/' ,
method: 'PUT',
scope: this
});
}
this.deleteFavouriteStop = function(stopId) {
getUserStopsConnection().request({
url: '/myStops/stopId/' + stopId + '/',
method: 'DELETE',
scope: this
});
var record = this.favouriteStopsStore.getById(stopId);
this.favouriteStopsStore.remove(record);
}
this.overrideFavouriteStop = function(prevStopId, newStopRecord) {
var newStopId = newStopRecord.get('id');
if(this.isFavourite(prevStopId) && !this.isFavourite(newStopId)) {
var prevStopRecord = this.favouriteStopsStore.getById(prevStopId);
var prevStopFavIndex = this.favouriteStopsStore.indexOf(prevStopRecord);
var filterOutArray = this.recentStopsStore.getById(newStopId).get("filterOut");
var record = new this.favouriteStopsStore.recordType({id:newStopId, name:newStopRecord.get("name"), stopIndicator:newStopRecord.get("stopIndicator"), towards:newStopRecord.get("towards"), direction:newStopRecord.get("direction"), lat:newStopRecord.get("lat"), lng:newStopRecord.get("lng"), routes: newStopRecord.get("routes"), filterOut:filterOutArray }, newStopId);
record.commit();
this.favouriteStopsStore.insert(prevStopFavIndex, record);
this.favouriteStopsStore.remove(prevStopRecord);
getUserStopsConnection().request({
url: '/myStops/prevStopId/'+prevStopId+'/newStopId/'+newStopId+'/',
method: 'PUT',
scope: this
});
_gaq.push(['_trackEvent', 'My Stops', newStopId, newStopRecord.data.name]);
return true;
} else {
return false;
}
}
this.isFavourite = function(stopId) {
var record = this.favouriteStopsStore.getById(stopId);
return record != undefined;
}
this.updateFilters = function(stopId, filterOutArray) {
var recentStopRecord = this.recentStopsStore.getById(stopId);
var favouriteStopRecord = this.favouriteStopsStore.getById(stopId);
if(recentStopRecord != undefined) {
recentStopRecord.set('filterOut', filterOutArray);
}
if(favouriteStopRecord != undefined) {
favouriteStopRecord.set('filterOut', filterOutArray);
}
}
this.getFilters = function(stopId) {
var favouriteStopsRecord = this.favouriteStopsStore.getById(stopId);
if(favouriteStopsRecord != undefined) {
return favouriteStopsRecord.get('filterOut');
}
var recentStopsRecord = this.recentStopsStore.getById(stopId);
if(recentStopsRecord != undefined) {
return recentStopsRecord.get('filterOut');
}
return [];
}
this.hasFilters = function(stopId) {
var recentStopsRecord = this.recentStopsStore.getById(stopId);
var favouriteStopsRecord = this.favouriteStopsStore.getById(stopId);
return ((recentStopsRecord != undefined && recentStopsRecord.get('filterOut').length > 0) ||
(favouriteStopsRecord != undefined && favouriteStopsRecord.get('filterOut').length > 0));
}
}
Ext.ns('KZ');
KZ.SearchBoxPanel = function() {
var performingSearchMask = undefined; // use getter method for access
var searchConnection = undefined; // use getter method for access
this.currentSearchTerm = "";
this.setSearchError = function(htmlStatus) {
Ext.get('search-error').update(htmlStatus).removeClass('x-hidden').show();
KZ.searchPanel.doLayout();
}
this.hideSearchError = function() {
Ext.get('search-error').enableDisplayMode().hide();
KZ.searchPanel.doLayout();
}
this.getSearchConnection = function() {
if(!searchConnection) {
searchConnection = new Ext.data.Connection({
timeout: 8000,
autoAbort: true
});
}
return searchConnection;
}
this.getPerformingSearchMask = function() {
if(!performingSearchMask) {
performingSearchMask = new Ext.LoadMask(KZ.searchPanel.body, {msg:"Searching..."});
}
return performingSearchMask;
}
this.extComponent = new Ext.form.TriggerField({
id: 'mapSearchField',
allowBlank: true,
flex: 0,
triggerClass: 'x-form-search-trigger',
autoCreate: {tag: "input", type: "text", autocomplete: "off", spellcheck: "false"},
listeners: {
scope: this,
valid: function(textField) {
this.hideSearchError();
},
invalid: function(textField) {
this.setSearchError("Please type something to search for");
},
specialkey: function(field, e){
if (e.getKey() == e.RETURN || e.getKey() == e.ENTER) {
this.performSearch(field.getValue());
}
}
},
margins: '5'
});
// Or you could create a delegate function
this.extComponent.onTriggerClick = function(event) {
KZ.searchBoxPanel.performSearch(this.getValue());
}
this.performSearch = function(searchTerm, newRequest) {
_gaq.push(['_trackEvent', 'SearchTerm', searchTerm]);
var fromTheMainPage = false;
if(KZ.InitialSearchPanel.isVisible() == true){
KZ.InitialSearchPanel.hideInitialSearchPanelAndShowMap();
fromTheMainPage = true;
}
this.extComponent.fireEvent("closeStopBoard");
if(searchTerm.trim() == '') {
this.extComponent.markInvalid();
return;
}
if(searchTerm != this.extComponent.getValue()) {
this.extComponent.setValue(searchTerm);
}
KZ.historyManager.addNewSearchHistory(searchTerm, true);
if((newRequest != undefined && newRequest == true) || newRequest==undefined) {
Ext.StoreMgr.lookup('stopResultsStore').removeAll();
Ext.StoreMgr.lookup('placeResultsStore').removeAll();
Ext.StoreMgr.lookup('selectedCallingPatternResultsStore').removeAll();
}
if(fromTheMainPage || newRequest== undefined || newRequest==true) { //prevent showing loading dialog no refresh is needed
this.getPerformingSearchMask().show();
KZ.searchResultsPanel.extComponent.hide();
}
this.extComponent.fireEvent("newSearchEvent");
this.currentSearchTerm = searchTerm;
var urlEncodedSearchTerm = encodeURIComponent(searchTerm);
if(newRequest != undefined && newRequest == false) {
KZ.searchResultsPanel.displayResults(KZ.searchResultsPanel.getCurrentSearchResults());
} else {
try {
this.getSearchConnection().request({
url: "/search?searchTerm=" + urlEncodedSearchTerm,
headers: {Accept: 'application/json'},
timeout: 8000,
scope: this,
success: function(resp, opt) {
this.getPerformingSearchMask().hide();
var searchResults = eval('(' + resp.responseText + ')');
KZ.searchResultsPanel.displayResults(searchResults);
},
failure: function(response, opts) {
this.getPerformingSearchMask().hide();
KZ.log('server-side failure with status code ' + response.status);
var emptyResults = {};
KZ.searchResultsPanel.displayResults(emptyResults);
}
});
} catch(e) {
KZ.log(e);
}
}
}
}
Ext.ns('KZ');
// This is a lazily initialised singleton, not to be instatiated
KZ.AutoRefreshArrivalsDialog = new function() {
var extComponent;
this.isInitialised = function() {
return extComponent != undefined;
}
this.isVisible = function() {
return this.isInitialised() && extComponent.isVisible();
}
this.show = function() {
if(extComponent == undefined) {
extComponent = new Ext.Window({
title: 'Auto-refresh',
iconCls: 'auto-refresh-icon',
layout:'fit',
modal:true,
width: 330,
padding:5,
draggable:false,
resizable:false,
closable:true,
closeAction:'hide',
plain: true,
html: 'Your arrival board will be refreshed automatically every ' + KZ.Config.autoRefreshInterval + ' seconds for ' + Math.floor(KZ.Config.autoRefreshDuration/60) + ' minutes.',
//contentEl: 'auto-refresh-message',
listeners: {
show: function() {
Ext.select('.ext-el-mask').addListener('click', function() {
extComponent.hide();
});
}
},
buttons: [{
text:'OK',
scope: this,
handler: function(){
extComponent.hide();
}
}]
});
}
extComponent.show();
extComponent.center();
}
}
Ext.ns('KZ');
KZ.OverrideFavouriteStopDialog = new function() {
var window;
var radioGroup;
var radioFieldSet;
var checkedStop;
this.prepare = function() {
var radioChecked = true;
var radioItems = [];
var favouriteStops = KZ.userStopsStore.getFavouriteStops();
Ext.each(favouriteStops, function(favouriteStop) {
var stop = favouriteStop.data;
var stopIndicator = stop.stopIndicator ? " (" + stop.stopIndicator + ") " : "";
var radioLabel = stop.name + stopIndicator;
var radio = new Ext.form.Radio({
boxLabel: radioLabel,
checked: radioChecked,
name: 'overridedFavouriteStop',
value: stop.id,
scope:this
});
radioItems.push(radio);
if(radioChecked) {
checkedStop = stop.id;
radioChecked = false;
}
}, this);
radioGroup = new Ext.form.RadioGroup({
hideLabel: true,
columns: 1,
items: radioItems
});
radioFieldSet = new Ext.form.FieldSet({
title: 'My Stops',
autoHeight: true,
border: true,
items: [radioGroup]
});
}
this.show = function(info) {
var message = new Ext.BoxComponent({
autoEl: {
html: 'The My Stops page is full. Please select a stop to replace, or press cancel.'
},
autoHeight: true,
style: {
marginBottom: '12px'
}
});
this.prepare();
window = new Ext.Window({
title: 'Confirm override',
iconCls: 'are-you-sure-icon',
layout:'fit',
modal:true,
width: 300,
padding: '5px 5px 5px 5px',
draggable:false,
resizable:false,
closable:true,
closeAction:'close',
plain: true,
items: [message, radioFieldSet],
listeners: {
show: function() {
Ext.select('.ext-el-mask').addListener('click', function() {
window.hide();
});
}
},
buttons: [{
text:'Ok',
scope: this,
handler: function(){
window.hide();
var prevStopId = radioGroup.getValue().value;
var newStopRecord = info.panel.getCurrentBusStopRecord();
KZ.userStopsStore.overrideFavouriteStop(prevStopId, newStopRecord);
info.toolbar.setFavouriteToolBarIcon();
window.close();
}
},{
text: 'Cancel',
handler: function(){
window.close();
}
}]
});
window.show();
}
}
Ext.ns('KZ');
/* Creates general notification dialog
* Object "properties" may contain properties:
* title - dialog title (optional)
* icon - the icon (optional)
* message - notification message (required)
*/
KZ.GeneralNotificationDialog = new function() {
this.show = function(properties) {
if(properties == undefined) {
return;
}
if(properties.title == undefined) {
properties.title = 'General notification';
}
if(properties.icon == undefined) {
properties.icon = 'notices-icon';
}
if(extComponent == undefined) {
var extComponent = new Ext.Window({
title: properties.title,
iconCls: properties.icon,
layout:'fit',
modal:true,
width: 330,
padding:5,
draggable:false,
resizable:false,
closable:true,
plain: true,
html: properties.message,
//contentEl: 'auto-refresh-message',
listeners: {
show: function() {
Ext.select('.ext-el-mask').addListener('click', function() {
extComponent.close();
});
}
},
buttons: [{
text:'OK',
scope: this,
handler: function(){
extComponent.close();
}
}]
});
}
extComponent.show();
}
}
Ext.ns('KZ');
// This is a lazily initialised singleton, not to be instatiated
KZ.NoticesDialog = new function() {
var extComponent;
this.isInitialised = function() {
return extComponent != undefined;
}
this.isVisible = function() {
return this.isInitialised() && extComponent.isVisible();
}
this.getDialog = function() {
if(extComponent == undefined) {
extComponent = new Ext.Window({
title: 'Notices',
iconCls: 'notices-icon',
layout:'fit',
modal:true,
autoScroll:true,
height: 300,
width: 400,
padding:5,
draggable:false,
resizable:false,
closable:true,
closeAction:'hide',
html: ' ',
plain: true,
listeners: {
show: function() {
Ext.select('.ext-el-mask').addListener('click', function() {
extComponent.hide();
});
}
},
buttons: [{
text:'OK',
scope: this,
handler: function(){
extComponent.hide();
}
}]
});
}
return extComponent;
}
this.show = function(stopInformationPanel) {
var noticesContent = this.getNoticesContent(stopInformationPanel);
var dialog = this.getDialog();
dialog.setTitle(stopInformationPanel.getCurrentBusStopRecord().get('name') + ' Notices');
dialog.show();
dialog.update(noticesContent);
}
this.getNoticesContent = function(stopInformationPanel) {
var currentBusStopServiceDisruptions = stopInformationPanel.getCurrentBusStopServiceDisruptions();
if(stopInformationPanel.arrivalsGrid.getStopBoardConnection() != undefined && stopInformationPanel.arrivalsGrid.getStopBoardConnection().isLoading()) {
return "Loading...";
}
else if(currentBusStopServiceDisruptions.infoMessages.length == 0 &&
currentBusStopServiceDisruptions.importantMessages.length == 0) {
return "There are no notices at present";
} else {
var noticesContent = '';
Ext.each(currentBusStopServiceDisruptions.importantMessages, function(msg) {
noticesContent += '- ' + msg + '
';
});
Ext.each(currentBusStopServiceDisruptions.infoMessages, function(msg) {
noticesContent += '- ' + msg + '
';
});
return noticesContent + '
';
}
}
}
Ext.ns('KZ');
KZ.ArrivalsGrid = function(stopInformationPanel, stopInformationToolbar) {
var refreshingArrivalsMask, stopBoardConnection;
var numberOfMinutesVersus24HourClockThreshold = KZ.Config.numberOfMinutesVersus24HourClockOnArrivalBoardThreshold;
this.stopInformationPanel = stopInformationPanel;
this.stopInformationToolbar = stopInformationToolbar;
this.getStopBoardConnection = function() {
return stopBoardConnection;
}
this.store = new Ext.data.JsonStore({
// store configs
autoDestroy: false,
// reader configs
root: 'arrivals',
idProperty: 'atcoCode',
fields: ['routeName', 'routeId', 'destination', 'estimatedWait', 'scheduledTime', 'isRealTime', 'displayTimeValue'],
sortData : this.sortCriteria
});
this.store.setDefaultSort('estimatedWait');
this.naturalOrderField = 'estimatedWait';
this.direction = undefined;
this.store.arrivalsGridScope = this;
this.filterRecordsOutsideArrivalsWindow = function(record, id) { //TODO: This isn't called
return record.get('estimatedWait') <= (numberOfMinutesVersus24HourClockThreshold * 60);
}
var columns = new Ext.grid.ColumnModel({
defaults: {
sortable:true
},
columns: [
{header: 'Route', width: 100, dataIndex: 'routeName'},
{header: 'To', width: 200, dataIndex: 'destination'},
{id:'scheduledTimeColumn', header: 'Time', width: 150, dataIndex: 'displayTimeValue'}
]
})
this.extComponent = new Ext.grid.GridPanel({
itemId: 'arrivals-grid',
store: this.store,
colModel: columns,
hideMode: 'offsets',
border: false,
stripeRows: true,
stateful: true,
enableColumnHide: false,
enableColumnMove: false,
enableHdMenu: true,
disableSelection: true,
viewConfig: {
emptyText: KZ.Config.noPredictionsAvailableMessage
},
bbar: {
cls: 'x-panel-footer filter-footer clickable',
html: 'Filter routes is on'
}
});
// Add footer onclick event once it has been rendered
this.extComponent.on('render', function(gridPanel) {
this.extComponent.bbar.setVisibilityMode(Ext.Element.DISPLAY);
stopInformationPanel.arrivalsGrid.extComponent.bbar.on('click', function(evt, el, cfg) {
KZ.RouteFilterDialog.show(stopInformationPanel);
}, this);
}, this);
this.extComponentHolder = new Ext.Panel({
flex:1,
layout: 'fit',
plain:true,
border: false,
items: [this.extComponent]
});
this.filterRecordsEstimatedWaitPresentation = function(record) {
var estimatedWait = record.get('estimatedWait');
var scheduledTime = record.get('scheduledTime');
var isRealTime = record.get('isRealTime');
var estimatedWaitColumnIndex = this.extComponent.getColumnModel().getIndexById('scheduledTimeColumn');
var estimatedWaitColumnHeader = this.extComponent.getColumnModel().getColumnById('scheduledTimeColumn').header;
if(!isRealTime && (estimatedWaitColumnHeader.indexOf('*') < 0)) {
this.extComponent.getColumnModel().setColumnHeader(estimatedWaitColumnIndex, 'Time (* = timetable info)');
}
if(KZ.Config.easyToReadDepartureBoardEnabled) {
if(compareArrivalTimes(estimatedWait, numberOfMinutesVersus24HourClockThreshold + ' min') <= 0){
record.data.displayTimeValue = isRealTime ? estimatedWait : estimatedWait + ' *';
} else {
record.data.displayTimeValue = isRealTime ? scheduledTime : scheduledTime + ' *';
}
} else {
record.data.displayTimeValue = isRealTime ? estimatedWait : scheduledTime + ' *';
}
record.commit();
}
this.refreshArrivals = function(keepCurrentSortCriteria, autoRefreshFlag) {
var busStopId = stopInformationPanel.getCurrentBusStopRecord().get("id");
var urlParams = autoRefreshFlag ? "?ar=1" : "";
if(!refreshingArrivalsMask) {
refreshingArrivalsMask = new Ext.LoadMask(this.extComponentHolder.body, {msg:"Loading departures..."});
}
refreshingArrivalsMask.show();
if(!stopBoardConnection) {
stopBoardConnection = new Ext.data.Connection({
timeout: 6000,
autoAbort: true
});
}
stopBoardConnection.request({
url: "/stopBoard/" + busStopId + "/"+ urlParams,
timeout: 20000,
headers: {Accept: 'application/json'},
scope: this,
success: function(resp, opt) {
var stopBoardResults = eval('(' + resp.responseText + ')');
// For testing
//stopBoardResults.serviceDisruptions.criticalMessages = ['A fire at Liverpool Street is causing major delays to all bus routes from this stop.', 'Passengers for Chancery Lane should take the 8 from Blackfriars Bridge.', 'Here is an extra long message to check that wrapping is working properly. Well is it working properly or not, hello hello mdfg msd gnfdg nafdgnfjdgn fjdgnafdjg nfdg.'];
//stopBoardResults.serviceDisruptions.importantMessages = ['This is a very important message.'];
//stopBoardResults.serviceDisruptions.infoMessages = ['There was a problem cos of weather.', 'The 88 will be running a reduced service over Easter weekend.', 'TemporaryLonger message is adLonger message is a dklfmmsdk gfmdgmfdgmsdgmg dgm gm mfd gmfd gkmsfd gmsfd gmsfd gmsfd gkmsfdgk msfd g gkmgkmgmfdgmsfd d bus shelter on some street.', 'Longer message is a longer message hello there why and then there was a cat and that. Good bye.'];
var filterOutArray = stopBoardResults.filterOut;
if(filterOutArray != undefined) {
KZ.userStopsStore.updateFilters(busStopId, filterOutArray);
}
this.presentStopBoardResults(keepCurrentSortCriteria, stopBoardResults);
},
failure: function(response, opts) {
var failureResp = {};
failureResp.stopBoardMessage = 'noPredictionsDueToSystemError';
failureResp.autoRefreshResponse = autoRefreshFlag ? autoRefreshFlag : false;
this.presentStopBoardResults(keepCurrentSortCriteria, failureResp);
KZ.log('server-side failure with status code ' + response.status);
}
});
}
this.presentStopBoardResults = function (keepCurrentSortCriteria, stopBoardResults) {
this.extComponent.getView().emptyText = '';
if(stopBoardResults.stopBoardMessage != undefined) {
this.store.removeAll();
if((stopBoardResults.stopBoardMessage == 'isSuspended') || (stopBoardResults.stopBoardMessage == 'isClosedPermanently')
|| (stopBoardResults.stopBoardMessage == 'isClosedTemporarily')){
this.extComponent.getView().emptyText = KZ.Config.suspendedStopMessage;
} else if (stopBoardResults.stopBoardMessage == 'noPredictionsDueToSystemError') {
this.extComponent.getView().emptyText = KZ.Config.noPredictionsDueToSystemError;
}
this.extComponent.getView().el.select('.x-grid3-header').setStyle('display', 'none');
// failure resistance
if(stopBoardResults.autoRefreshResponse) {
this.stopInformationPanel.cancelAutoRefreshTask(true);
}
} else {
if(keepCurrentSortCriteria) {
var sortState = this.store.getSortState();
this.store.setDefaultSort(sortState.field, sortState.direction);
} else {
this.store.setDefaultSort('estimatedWait',"ASC");
}
if(stopBoardResults.serviceDisruptions.criticalMessages.length >= 1) {
this.store.removeAll();
var criticalMessagesHtml = '';
Ext.each(stopBoardResults.serviceDisruptions.criticalMessages, function(msg) {
criticalMessagesHtml += '
' + msg + '
';
});
this.extComponent.getView().emptyText = criticalMessagesHtml + '
';
this.extComponent.getView().el.select('.x-grid3-header').setStyle('display', 'none');
} else {
if(!stopBoardResults.arrivals || stopBoardResults.arrivals.length == 0) {
this.extComponent.getView().emptyText = KZ.Config.noPredictionsAvailableMessage;
this.extComponent.getView().el.select('.x-grid3-header').setStyle('display', 'none');
this.store.removeAll();
} else {
this.extComponent.getView().el.select('.x-grid3-header').setStyle('display', 'block');
this.store.loadData(stopBoardResults);
}
var stopId = this.stopInformationPanel.getCurrentBusStopRecord().get('id');
var filters = KZ.userStopsStore.getFilters(stopId);
this.applyArrivalFilter(filters);
}
this.stopInformationPanel.setCurrentBusStopServiceDisruptions(stopBoardResults.serviceDisruptions);
if(stopBoardResults.serviceDisruptions &&
((stopBoardResults.serviceDisruptions.importantMessages && stopBoardResults.serviceDisruptions.importantMessages.length > 0) ||
(stopBoardResults.serviceDisruptions.infoMessages && stopBoardResults.serviceDisruptions.infoMessages.length > 0))) {
this.stopInformationPanel.toolbar.showNoticesButton();
}
}
// this.store.filterBy(this.filterRecordsOutsideArrivalsWindow, this);
this.stopInformationToolbar.setFilterToolBarText();
this.stopInformationPanel.setTitle(stopBoardResults.lastUpdated);
this.extComponent.show();
this.extComponent.getView().refresh();
try {
this.extComponent.doLayout();
} catch(e) {
KZ.log("Error laying out Arrivals Grid " + e);
}
if(refreshingArrivalsMask != undefined) {
refreshingArrivalsMask.hide();
}
this.store.each(this.filterRecordsEstimatedWaitPresentation, this);
this.extComponent.show();
// this.extComponent.getView().refresh();
// this.extComponent.doLayout();
}
}
KZ.ArrivalsGrid.prototype.sortCriteria = function() {
var arrivalsGrid = this.arrivalsGridScope; // switch the scope
var columnName = arrivalsGrid.extComponent.store.sortInfo.field;
var direction = arrivalsGrid.extComponent.store.sortInfo.direction;
arrivalsGrid.direction = direction || 'ASC';
switch(columnName){
case 'routeName' :
arrivalsGrid.store.data.sort(arrivalsGrid.direction, arrivalsGrid.routeSort);
break;
case 'destination' :
arrivalsGrid.naturalOrderField = 'destination';
arrivalsGrid.store.data.sort(arrivalsGrid.direction, arrivalsGrid.naturalOrderSort);
break;
default :
arrivalsGrid.naturalOrderField = 'estimatedWait';
arrivalsGrid.store.data.sort(arrivalsGrid.direction, arrivalsGrid.naturalOrderSort);
break;
}
if(arrivalsGrid.store.snapshot && arrivalsGrid.store.snapshot != arrivalsGrid.store.data){
switch(columnName){
case 'routeName' :
arrivalsGrid.store.snapshot.sort(arrivalsGrid.direction, arrivalsGrid.routeSort);
break;
case 'destination' :
arrivalsGrid.naturalOrderField = 'destination';
arrivalsGrid.store.snapshot.sort(arrivalsGrid.direction, arrivalsGrid.naturalOrderSort);
break;
default :
arrivalsGrid.naturalOrderField = 'estimatedWait';
arrivalsGrid.store.snapshot.sort(arrivalsGrid.direction, arrivalsGrid.naturalOrderSort);
break;
}
}
}
KZ.ArrivalsGrid.prototype.routeSort = function(r1, r2){
r1 = r1.get('routeName');
r2 = r2.get('routeName');
var result = 0;
if (!r1 && !r2) { //if both are undefined
return 0;
} else if (!r2) {
return 1;
} else if (!r1) {
return -1;
}
var totalLength = Math.max(r1.length, r2.length) + 1;
var length = 0;
for (var i = 0; result == 0 && i < totalLength; i++) {
if (KZ.util.isDigit(r1.charAt(i)) && KZ.util.isDigit(r2.charAt(i))) {
length++;
}
else if (KZ.util.isDigit(r1.charAt(i))) {
result = (length == 0 ? -1 : 1);
}
else if (KZ.util.isDigit(r2.charAt(i))) {
result = (length == 0 ? 1 : -1);
}
else {
for (var j = i - length; result == 0 && j <= i; j++) {
result = KZ.util.charCmp((r1.charAt(j)).toLowerCase(), (r2.charAt(j)).toLowerCase());
}
length = 0;
}
}
return result;
};
// static function
KZ.ArrivalsGrid.arrivalTimeRenderer = function(val) {
return (val =='due') ? '0 min' : val;
}
KZ.ArrivalsGrid.prototype.naturalOrderSort = function(r1, r2){
var arrivalsGrid = r1.store.arrivalsGridScope; // switch the scope
r1OrderingField = r1.get(arrivalsGrid.naturalOrderField);
r2OrderingField = r2.get(arrivalsGrid.naturalOrderField);
var result = undefined;
if(arrivalsGrid.naturalOrderField == 'estimatedWait'){
r1OrderingField = KZ.ArrivalsGrid.arrivalTimeRenderer(r1OrderingField);
r2OrderingField = KZ.ArrivalsGrid.arrivalTimeRenderer(r2OrderingField);
result = compareArrivalTimes(r1OrderingField, r2OrderingField);
} else {
result = (r1OrderingField > r2OrderingField) ? 1 : ((r1OrderingField < r2OrderingField) ? -1 : 0);
}
if(result == 0){ //In case of equals ordering by a second field
if(arrivalsGrid.naturalOrderField == 'estimatedWait'){
result = arrivalsGrid.routeSort(r1,r2);
} else if(arrivalsGrid.naturalOrderField == 'destination'){
arrivalsGrid.naturalOrderField = 'estimatedWait';
result = arrivalsGrid.naturalOrderSort(r1,r2);
arrivalsGrid.naturalOrderField = 'destination';
}
if(arrivalsGrid.direction == 'DESC'){ //Changing the direction of the second criteria too.
result = -result;
}
}
return result;
};
KZ.ArrivalsGrid.prototype.applyArrivalFilter = function(filterOutArray) {
var counter = filterOutArray.length - 1;
this.store.filterBy(function(record, id) {
if (!KZ.util.isSet(filterOutArray)) {
return true;
}
if(filterOutArray.indexOf(record.get('routeId')) == -1){
return true;
}else{
counter--;
return false;
}
}, this);
if(counter == 0){
this.extComponent.getView().emptyText = KZ.Config.noPredictionsAvailableMessage;
}
}
this.compareArrivalTimes = function(time1, time2) {
if(time1.length == time2.length) {
return (time1 > time2) ? 1 : ((time1 < time2) ? -1 : 0);
} else if(time1.length < time2.length) {
return -1;
} else {
return 1;
}
}
Ext.ns('KZ');
/* This adds "More" to the overflow toolbar button as opposed to the default >> icon */
Ext.override(Ext.layout.ToolbarLayout, {
initMore : function(){
if(!this.more){
this.moreMenu = new Ext.menu.Menu({
ownerCt : this.container,
listeners: {
beforeshow: this.beforeMoreShow,
scope: this
}
});
this.more = new Ext.Button({
text: 'More',
menu : this.moreMenu,
ownerCt : this.container
});
var td = this.insertCell(this.more, this.extrasTr, 100);
this.more.render(td);
}
}
});
var mobileAndSmsTitle = undefined;
var disableMobileAndSmsButton = false;
if(KZ.Config.enableMobile && KZ.Config.enableSMS){
mobileAndSmsTitle = 'Mobile &' + KZ.Config.String.SmsServiceTitle;
} else if (KZ.Config.enableMobile) {
mobileAndSmsTitle = 'Mobile';
} else if (KZ.Config.enableSMS){
mobileAndSmsTitle = KZ.Config.String.SmsServiceTitle;
} else {
disableMobileAndSmsButton = true;
}
KZ.StopInformationToolbar = function(stopInformationPanel, showMyStopsButton) {
var ignoreBoardUpdateRequest = false;
if(showMyStopsButton == undefined) {
showMyStopsButton = false;
}
this.setFavouriteToolBarIcon = function() {
var btn = this.extComponent.getComponent('add-remove-favourite-toolbar-button');
if(btn.hidden) return;
if(KZ.userStopsStore.isFavourite(stopInformationPanel.getCurrentBusStopRecord().get('id'))) { // TODO combine 4 lines into a method
btn.setIconClass('remove-favourite-icon');
btn.setText('Remove from My Stops');
} else {
btn.setIconClass('add-favourite-icon');
btn.setText('Add to My Stops');
}
this.extComponent.doLayout();
}
this.setFilterToolBarText = function() {
var busStopId = stopInformationPanel.getCurrentBusStopRecord().get("id");
var btn = this.extComponent.getComponent('filter-toolbar-button');
if(KZ.userStopsStore.hasFilters(busStopId)) {
stopInformationPanel.arrivalsGrid.extComponent.bbar.show();
btn.setText('Filter routes is on');
} else {
stopInformationPanel.arrivalsGrid.extComponent.bbar.hide();
btn.setText('Filter routes');
}
stopInformationPanel.arrivalsGrid.extComponent.syncHeight();
stopInformationPanel.arrivalsGrid.extComponent.doLayout();
}
this.hideNoticesButton = function() {
var btn = this.extComponent.getComponent('notices-button');
btn.hide();
btn = this.extComponent.getComponent('notices-button-seperator');
btn.hide();
this.extComponent.doLayout();
}
this.showNoticesButton = function() {
var btn = this.extComponent.getComponent('notices-button');
btn.show();
btn.el.fadeIn({duration:4, useDisplay:true});
btn = this.extComponent.getComponent('notices-button-seperator');
btn.show();
this.extComponent.doLayout();
}
this.cancelIgnoreBoardUpdateRequest = function() {
ignoreBoardUpdateRequest = false;
}
this.autoRefreshTask = {
autoRefreshRepeat : Math.floor(KZ.Config.autoRefreshDuration/KZ.Config.autoRefreshInterval),
running : false,
run : function(invocationCount) {
if(!this.running) {
if(invocationCount != 1) {
return;
} else {
this.running = true;
}
}
stopInformationPanel.arrivalsGrid.refreshArrivals(true, true);
stopInformationPanel.toolbar.extComponent.getComponent('refresh-button').disable().setText("Auto Refreshing...");
if(invocationCount > this.autoRefreshRepeat) {
this.running = false;
stopInformationPanel.cancelAutoRefreshTask();
}
},
interval : KZ.Config.autoRefreshInterval * 1000,
scope : this.autoRefreshTask
};
this.createRefreshMenu = function(visible) {
if(visible) {
return new Ext.menu.Menu({
autoDestroy: true,
items: [
{
text: 'Auto-refresh',
itemId: 'auto-refresh-button',
iconCls: 'auto-refresh-icon',
hidden: !KZ.Config.enableAutoRefresh,
scope:this,
handler: function() {
Ext.TaskMgr.start(this.autoRefreshTask);
KZ.AutoRefreshArrivalsDialog.show();
_gaq.push(['_trackEvent', 'Stop Information Toolbar', 'Auto-Refresh Button Pressed', stopInformationPanel.getCurrentBusStopRecord().data.id]);
}
}
]
});
} else {
return null;
}
}
this.extComponent = new Ext.Toolbar({
cls: 'stop-information-toolbar',
enableOverflow: true,
layoutConfig: {triggerWidth:60},
items: [
{
text: 'Notices',
itemId: 'notices-button',
iconCls: 'notices-icon',
scope: this,
handler: function() {
var noticesContent = KZ.NoticesDialog.show(stopInformationPanel);
_gaq.push(['_trackEvent', 'Stop Information Toolbar', 'Notices Button Pressed', stopInformationPanel.getCurrentBusStopRecord().data.id]);
}
},
{
scope:this,
itemId: 'add-remove-favourite-toolbar-button',
text: 'Add to My Stops',
iconCls: 'add-favourite-icon',
hidden: !showMyStopsButton,
handler: function() {
var stopRecord = stopInformationPanel.getCurrentBusStopRecord();
var stopId = stopRecord.get("id");
if(KZ.userStopsStore.isFavourite(stopId)) {
KZ.userStopsStore.deleteFavouriteStop(stopId);
_gaq.push(['_trackEvent', 'Stop Information Toolbar', 'Remove From My Stops Button Pressed', stopId]);
} else {
if(KZ.userStopsStore.isFavouritesMaxLimitReached()) {
var stopInformation = {
panel: stopInformationPanel,
toolbar: this
};
KZ.OverrideFavouriteStopDialog.show(stopInformation);
} else {
KZ.userStopsStore.addFavouriteStop(stopRecord);
}
_gaq.push(['_trackEvent', 'Stop Information Toolbar', 'Add To My Stops Button Pressed', stopId]);
}
this.setFavouriteToolBarIcon();
}
},
{xtype: 'tbseparator', itemId: 'notices-button-seperator'},
{
xtype: (KZ.Config.enableAutoRefresh?'splitbutton':'button'),
text: 'Refresh',
itemId: 'refresh-button',
iconCls: 'refresh-icon',
scope: this,
handler: function() {
if(ignoreBoardUpdateRequest == false) {
ignoreBoardUpdateRequest = true;
stopInformationPanel.arrivalsGrid.refreshArrivals(true);
this.cancelIgnoreBoardUpdateRequest.defer(10000);
_gaq.push(['_trackEvent', 'Stop Information Toolbar', 'Refresh Button Pressed', stopInformationPanel.getCurrentBusStopRecord().data.id]);
}
},
menu: this.createRefreshMenu(KZ.Config.enableAutoRefresh)
},
(KZ.Config.enableAutoRefresh?'-':''),
{
itemId: 'filter-toolbar-button',
text: 'Filter routes',
iconCls: 'filter-icon',
scope:this,
hidden:!KZ.Config.enableFilterRoutesButton,
handler: function() {
KZ.RouteFilterDialog.show(stopInformationPanel);
_gaq.push(['_trackEvent', 'Stop Information Toolbar', 'Filter Button Pressed', stopInformationPanel.getCurrentBusStopRecord().data.id]);
}
},
// {
//text: 'Plan Journey
',
//iconCls: 'plan-journey-icon',
//scope:this,
//handler: function() {
//var t = new Ext.Template([KZ.Config.JpUrlTemplate]);
//var url = t.apply({stopName: encodeURIComponent(stopInformationPanel.getCurrentBusStopRecord().get('name'))});
//window.open(url, '_blank');
//},
//hidden:!KZ.Config.enableJp
// },
{
text: mobileAndSmsTitle,
iconCls: 'mobile-and-sms',
scope:this,
hidden:disableMobileAndSmsButton,
handler: function() {
var mobTpl = Ext.Template.from('mobileServicesTplHolder');
var mobContent = mobTpl.apply({stopName: stopInformationPanel.getCurrentBusStopRecord().get('name'), smsCode: stopInformationPanel.getCurrentBusStopRecord().get('smsCode')});
// Tried to use the tpl configuration, but failed. Now manually load the content tpl.
var window = new Ext.Window({
title: mobileAndSmsTitle,
iconCls: 'mobile-and-sms',
layout:'fit',
modal:true,
width: 400,
padding:5,
draggable:false,
resizable:false,
closable:true,
closeAction:'hide',
plain: true,
html: mobContent,
data: {stopName: 'INITIAL', smsCode: 'INITIAL111'},
listeners: {
show: function() {
Ext.select('.ext-el-mask').addListener('click', function() {
window.hide();
});
}
},
buttons: [{
text:'OK',
scope: this,
handler: function(){
window.hide();
}
}]
});
window.show();
_gaq.push(['_trackEvent', 'Stop Information Toolbar', 'Mobile and SMS Button Pressed', stopInformationPanel.getCurrentBusStopRecord().data.id]);
}
}
]
});
}
Ext.ns('KZ');
// This panel is used on both the main map page and the My Stops page
KZ.StopInformationPanel = function(showStaticMap, showMyStopsButton, enablePartialCollapse, tools, draggable, initiallyHidden) {
var currentBusStopRecord;
var currentBusStopServiceDisruptions;
var staticMapPanel;
this.getCurrentBusStopRecord = function() {
return currentBusStopRecord;
}
this.getCurrentBusStopServiceDisruptions = function() {
return currentBusStopServiceDisruptions;
}
this.setCurrentBusStopServiceDisruptions = function(disruptions) {
currentBusStopServiceDisruptions = disruptions;
}
this.toolbar = new KZ.StopInformationToolbar(this, showMyStopsButton);
this.arrivalsGrid = new KZ.ArrivalsGrid(this, this.toolbar);
var stopInformationPanelItems = [this.arrivalsGrid.extComponentHolder];
this.cancelAutoRefreshTask = function(failure) {
Ext.TaskMgr.stopAll();
//Ext.TaskMgr.stop( this.toolbar.autoRefreshTask );
this.toolbar.extComponent.getComponent('refresh-button').enable().setText("Refresh");
if (failure) {
this.toolbar.autoRefreshTask.running = false;
KZ.GeneralNotificationDialog.show({
title: 'Auto refresh failure',
message: 'Auto refresh has encountered a problem and has been disabled.'
});
}
}
if(showStaticMap === true) {
var staticMapImgString = '';
staticMapPanel = new Ext.Panel({
cls: 'my-stops-map-panel',
width:200,
plain:true,
border: false,
html: staticMapImgString
});
stopInformationPanelItems.unshift(staticMapPanel);
}
this.extComponent = new Ext.Panel({
cls : 'x-portlet stop-information-panel',
region:'south',
iconCls:'arrival-board-icon',
frame:false,
tools: tools,
draggable: draggable,
split:true,
plain:true,
tbar: this.toolbar.extComponent,
collapsible:true,
hidden:initiallyHidden,
layout: 'hbox',
layoutConfig: {
align : 'stretch',
pack: 'start'
},
title: 'Arrivals',
listeners: {
scope:this,
'beforecollapse': function() {
this.cancelAutoRefreshTask();
document.title = document.title.split(" - ")[0];
if(enablePartialCollapse) {
return true;
} else {
this.extComponent.hide();
currentBusStopRecord = undefined;
if(this.extComponent.ownerCt != undefined) {
this.extComponent.ownerCt.doLayout();
}
this.extComponent.fireEvent("stopBoardClosed");
return false;
}
}
},
items: stopInformationPanelItems
});
this.extComponent.on({
'closeStopBoard' : {
scope: this,
fn: function() {
this.extComponent.collapse();
}
}
});
// Stop board is optional
this.show = function(busStopRecord, stopBoard) {
if(showStaticMap === true) {
staticMapPanel.update('
');
}
if(currentBusStopRecord != undefined && currentBusStopRecord.id != busStopRecord.id) {
this.cancelAutoRefreshTask();
}
this.arrivalsGrid.extComponent.hide();
this.arrivalsGrid.store.removeAll();
currentBusStopRecord = busStopRecord;
this.setTitle();
this.toolbar.setFavouriteToolBarIcon();
this.toolbar.hideNoticesButton();
this.extComponent.show();
if(this.extComponent.ownerCt != undefined) {
this.extComponent.ownerCt.doLayout();
}
var newSearchPointEventFunction = function() { this.extComponent.fireEvent("newSearchPointEvent", busStopRecord.get('lat'), busStopRecord.get('lng')); }
newSearchPointEventFunction.defer(310, this);// This should be in step with the maps check resize function, which is a deferred function call.
if(stopBoard != undefined){
this.arrivalsGrid.presentStopBoardResults(undefined, stopBoard);
} else { //requesting the server to get the stopBoard
this.arrivalsGrid.refreshArrivals();
}
}
this.setTitle = function(lastUpdated) {
var stopIndicator = currentBusStopRecord.get("stopIndicator");
var nameAndFlagLabel = '' + currentBusStopRecord.get("name") + (stopIndicator != undefined ? ' ('+ stopIndicator +')' : '') + '';
// For bookmarking purposes
if(Ext.get('myStopsPanel') == undefined) {
var htmlPageTitle = currentBusStopRecord.get("name") + (stopIndicator != undefined ? ' ('+ stopIndicator +')' : '') + (currentBusStopRecord.get("smsCode") != undefined ? ' :: ' + currentBusStopRecord.get("smsCode") : '');
document.title = document.title.split(" - ")[0] + ' - ' + htmlPageTitle;
}
var stopCodeLabel = currentBusStopRecord.get("smsCode") ? ' Stop code ' + currentBusStopRecord.get("smsCode") + '': '';
var lastUpdatedLabel = ' @ ' + lastUpdated + '';
if(lastUpdated) {
this.extComponent.setTitle(nameAndFlagLabel + lastUpdatedLabel + stopCodeLabel);
} else {
this.extComponent.setTitle(nameAndFlagLabel + stopCodeLabel);
}
}
}
Ext.ns('KZ');
KZ.SearchResultsPanel = function() {
var currentSearchResults = undefined;
var resultsStatus = new Ext.Container({
id: 'results-status-container',
contentEl: 'resultsStatus',
flex: 0,
autoHeight:true
});
var searchHelpContainer = new Ext.Container({
id: 'search-help-container',
contentEl: 'searchHelp',
autoScroll: true,
flex: 1
});
var ambiguousResultsContainer = new Ext.Container({
id: 'ambiguous-results-container',
contentEl: 'ambiguous-search-results',
autoScroll: true,
flex:1,
hidden:true
});
KZ.routeSearchResultsContainer = new KZ.RouteSearchResultsContainer();
this.getSearchHelpContainer = function () {
return searchHelpContainer;
}
this.getCurrentSearchResults = function() {
return currentSearchResults;
}
this.resetCurrentSearchResults = function() {
currentSearchResults = undefined;
}
this.resultsShowing = function() {
return searchHelpContainer.getHeight() < 1;
}
this.setResultsStatus = function(htmlStatus) {
Ext.get('search-results-message').update(htmlStatus).removeClass('x-hidden').show();;
}
this.resetSearchResultsPanel = function() {
this.setResultsStatus('');
resultsStatus.show();
KZ.util.hideVBoxLayoutItem(searchHelpContainer);
KZ.util.hideVBoxLayoutItem(resultsAccordion);
placeResultsPanel.hide();
KZ.util.hideVBoxLayoutItem(KZ.routeSearchResultsContainer.extComponent);
KZ.util.hideVBoxLayoutItem(ambiguousResultsContainer);
}
this.displayResults = function(jsonResults, showPanels) {
currentSearchResults = jsonResults;
var resultStatus = '';
this.resetSearchResultsPanel();
/* TODO: This is a hacky interim solution to remove stop searching for certain clients. This behaviour will be
* removed completely in the near future. It is not currently removed on the server side. We simply ignore any results on the client
*/
if(!KZ.Config.enableBusStopSearch && jsonResults.results && jsonResults.results.stopResults &&
!jsonResults.results.routeResults && !jsonResults.results.placeResults) {
jsonResults.results = undefined;
}
if(!KZ.Config.enableBusStopSearch && jsonResults.results && jsonResults.results.stopResults) {
jsonResults.results.stopResults = undefined;
}
if(!KZ.util.isSet(jsonResults.stopCodeResult) && !KZ.util.isSet(jsonResults.results)) {
resultStatus += "There were no matches for " + KZ.searchBoxPanel.currentSearchTerm + ".";
_gaq.push(['_trackEvent', 'Search Classification', 'No Search Matches', KZ.searchBoxPanel.currentSearchTerm]);
}
if (KZ.util.isSet(jsonResults.results) && KZ.util.isSet(jsonResults.results.suggestion)) {
resultStatus += "There were no matches for " + KZ.searchBoxPanel.currentSearchTerm + ".
\n";
resultStatus += "Did you mean .. \"" + jsonResults.results.suggestion + "\"?";
_gaq.push(['_trackEvent', 'Search Classification', 'Did You Mean', KZ.searchBoxPanel.currentSearchTerm + ' -> ' + jsonResults.results.suggestion]);
}
if(resultStatus.length > 0) {
this.setResultsStatus(resultStatus);
this.extComponent.show();
KZ.searchPanel.doLayout();
return;
}
if(jsonResults.stopCodeResult) {
var stop = jsonResults.stopCodeResult;
var recentStopRecord = new KZ.userStopsStore.recentStopsStore.recordType({id:stop.id, smsCode:stop.smsCode, name:stop.name, stopIndicator:stop.stopIndicator, towards:stop.towards, direction:stop.direction, lat:stop.lat, lng:stop.lng, routes:stop.routes}, stop.id);
KZ.searchResultsPanel.decorateRecordWithAccessorFields(recentStopRecord);
this.setResultsStatus("Found " + stop.name + " (" + KZ.searchBoxPanel.currentSearchTerm + ")");
stopResultsPanel.setTitle("Stops");
stopResultsStore.insert(0, recentStopRecord);
KZ.markerManager.createMarkersFromStore(stopResultsStore);
KZ.searchResultsPanel.extComponent.fireEvent("stopSelectedEvent", recentStopRecord);
KZ.util.showVBoxLayoutItem(resultsAccordion);
_gaq.push(['_trackEvent', 'Search Classification', 'Stop Code Match', KZ.searchBoxPanel.currentSearchTerm]);
} else if(jsonResults.results && jsonResults.results.routeResults && jsonResults.results.stopResults) {
// see if you can configure the template from the index.jsp
// see Template.from in Template class
this.setResultsStatus("Did you mean..?
");
var previousAmbiguousResults = Ext.get('ambiguous-search-results').query('.ambiguous-result');
Ext.each(previousAmbiguousResults, function(staleResult) {
Ext.removeNode(staleResult);
});
var t = new Ext.Template([
'',
]);
var temp = Ext.get('ambiguous-search-results');
t.compile();
t.append('ambiguous-search-results', {id: 'postcode', resultType: 'Postcode: ' + KZ.searchBoxPanel.currentSearchTerm});
t.append('ambiguous-search-results', {id: 'busroute', resultType: 'Bus route: ' + KZ.searchBoxPanel.currentSearchTerm});
KZ.util.showVBoxLayoutItem(ambiguousResultsContainer);
_gaq.push(['_trackEvent', 'Search Classification', 'Ambiguous Search - Postcode Or Bus Route', KZ.searchBoxPanel.currentSearchTerm]);
} else if(jsonResults.results && jsonResults.results.routeResults) {
var busRoutesJson = jsonResults.results.routeResults;
if(busRoutesJson.length > 1 ) {
this.setResultsStatus("Did you mean..?
");
var previousAmbiguousResults = Ext.get('ambiguous-search-results').query('.ambiguous-result');
Ext.each(previousAmbiguousResults, function(staleResult) {
Ext.removeNode(staleResult);
});
var t = new Ext.Template([
'',
]);
var temp = Ext.get('ambiguous-search-results');
t.compile();
var routePosition = 1;
Ext.each(busRoutesJson, function(route) {
var routeName = route.name;
if(route.networkName != undefined) {
routeName += ' (' + route.networkName + ')';
}
t.append('ambiguous-search-results', {id: routePosition, resultType: 'Bus route: ' + routeName});
routePosition++;
});
routePosition = 1; // this value is passed to the history in order to know wich route we have selected.
KZ.util.showVBoxLayoutItem(ambiguousResultsContainer);
_gaq.push(['_trackEvent', 'Search Classification', 'Multiple Bus Route Matches', KZ.searchBoxPanel.currentSearchTerm]);
} else {
KZ.routeSearchResultsContainer.displayBusRouteResults(busRoutesJson[0]);
_gaq.push(['_trackEvent', 'Search Classification', 'Bus Route Match', KZ.searchBoxPanel.currentSearchTerm]);
}
} else if(jsonResults.results && jsonResults.results.placeResults) {
this.setResultsStatus("Results for \"" + KZ.searchBoxPanel.currentSearchTerm + "\"");
this.extComponent.fireEvent("newSearchBoundingBoxEvent", jsonResults.results.swLatBB, jsonResults.results.swLngBB, jsonResults.results.neLatBB, jsonResults.results.neLngBB);
// show the accordion
if(jsonResults.results.stopResults) {
stopResultsStore.loadData(jsonResults, false);
}
if(jsonResults.results.placeResults) {
placeResultsStore.loadData(jsonResults, false);
}
this.setAccordionPanelTitles(jsonResults);
KZ.util.showVBoxLayoutItem(resultsAccordion);
resultsAccordion.show();
jsonResults.results.stopResults == undefined ? stopResultsPanel.hide() : stopResultsPanel.show();
jsonResults.results.placeResults == undefined ? placeResultsPanel.hide() : placeResultsPanel.show();
_gaq.push(['_trackEvent', 'Search Classification', 'Place Matche(s)', KZ.searchBoxPanel.currentSearchTerm]);
} else if(jsonResults.results && jsonResults.results.lat && jsonResults.results.lng) { // Postcode search
this.setResultsStatus("Found postcode \"" + KZ.searchBoxPanel.currentSearchTerm + "\"");
this.extComponent.fireEvent("newSearchPointEvent", jsonResults.results.lat, jsonResults.results.lng);
var currentSearchTerm = KZ.searchBoxPanel.currentSearchTerm;
var record = new placeResultsStore.recordType({name:currentSearchTerm, place:undefined, lat:jsonResults.results.lat, lng:jsonResults.results.lng, domRef: undefined}, currentSearchTerm);
KZ.searchResultsPanel.decorateRecordWithAccessorFields(record);
record.commit();
placeResultsStore.insert(0, record);
placeResultsStore.fireEvent("load", placeResultsStore, placeResultsStore.getRange(), placeResultsStore.lastOptions);
_gaq.push(['_trackEvent', 'Search Classification', 'Postal Code Match', KZ.searchBoxPanel.currentSearchTerm]);
} else if(jsonResults.results && jsonResults.results.stopResults &&
!KZ.util.isSet(jsonResults.results.placeResults)) { // TODO: I think this case was only required for postcode search. Now, we can simply show all three sections of accordion
this.setResultsStatus("Stops near \"" + KZ.searchBoxPanel.currentSearchTerm + "\"");
this.extComponent.fireEvent("newSearchBoundingBoxEvent", jsonResults.results.swLatBB, jsonResults.results.swLngBB, jsonResults.results.neLatBB, jsonResults.results.neLngBB);
stopResultsStore.loadData(jsonResults, false);
stopResultsPanel.show();
this.setAccordionPanelTitles(jsonResults);
KZ.util.showVBoxLayoutItem(resultsAccordion);
_gaq.push(['_trackEvent', 'Search Classification', 'Stop Matche(s) Only', KZ.searchBoxPanel.currentSearchTerm]);
} else {
stopResultsStore.loadData(jsonResults);
KZ.log("Could not process json Results");
}
if(showPanels == undefined || showPanels != false ) {
if(stopResultsStore.getCount() > 0) {
if(stopResultsPanel.collapsed) {
stopResultsPanel.expand(false);
} else {
stopResultsPanel.fireEvent('expand', stopResultsPanel);
}
} else if(placeResultsStore.getCount() > 0) {
if(placeResultsPanel.collapsed) {
placeResultsPanel.expand(false);
} else {
placeResultsPanel.fireEvent('expand', placeResultsPanel);
}
}
this.extComponent.show();
Ext.get('route-search-container').dom.scrollTop = 0;
KZ.searchPanel.doLayout();
}
try {
stopResultsPanel.getView().refresh();
placeResultsPanel.getView().refresh();
} catch(e) {
KZ.log(e);
}
}
this.processAmbiguousSearchClick = function(event, element) {
// TODO: fix history for this new menu, then delete comments from lines starting with KZ.history
var busStopOrPostCode = element.getAttribute('rel');
if(busStopOrPostCode == 'postcode') {
//KZ.historyManager.appendUserSelection('0');
currentSearchResults.results.routeResults = undefined;
} else if(busStopOrPostCode == 'busroute') {
//KZ.historyManager.appendUserSelection('1');
currentSearchResults.results.stopResults = undefined;
} else { //We only have routes and we have to decide which direction
var menuPosition = busStopOrPostCode;
//KZ.historyManager.appendUserSelection(menuPosition);
var selectedRoute = currentSearchResults.results.routeResults[menuPosition - 1];
currentSearchResults.results.routeResults = new Array();
currentSearchResults.results.routeResults[0] = selectedRoute;
}
KZ.util.hideVBoxLayoutItem(ambiguousResultsContainer);
this.displayResults(currentSearchResults);
}
Ext.get('ambiguous-search-results').on({
'click' : {
fn: this.processAmbiguousSearchClick,
scope: this,
stopEvent: true,
delegate: 'a'
}
});
this.setAccordionPanelTitles = function(jsonResults) {
stopResultsPanel.setTitle("Stops (" + stopResultsStore.getCount() + ")");
placeResultsPanel.setTitle("Places & Streets (" + placeResultsStore.getCount() + ")");
}
var stopResultsStore = new Ext.data.JsonStore({
autoDestroy: false,
storeId: 'stopResultsStore',
root: 'results.stopResults',
idProperty: 'id',
fields: ['id', 'smsCode', 'name', 'stopIndicator', 'towards', 'direction', 'lat', 'lng', 'routes']
});
var placeResultsStore = new Ext.data.JsonStore({
autoDestroy: false,
storeId: 'placeResultsStore',
root: 'results.placeResults',
fields: ['name', 'place', 'lat', 'lng']
});
var routeMarkersStore = new Ext.data.JsonStore({
autoDestroy: false,
storeId: 'routeDirectionResultsStore',
root: 'results.routeResults.directions',
fields: ['name', 'markers']
});
// Custom rendering Template for the Grid View rows
var stopResultTemplate = new Ext.XTemplate(
'',
'',
'
{name}',
'',
' ({stopIndicator})',
'',
'
',
'
',
'towards {towards}
',
'',
'
',
'',
'{name} ',
//'{[xindex === xcount ? "" : ", "]}',
'',
'
',
'
',
{
compiled: true,
isDefined: function(stringValue) {
return stringValue != undefined && stringValue != '';
}
}
);
var placeResultTemplate = new Ext.XTemplate(
'',
'',
'{name}',
'
',
{
compiled:true
}
);
function highlightMarkerFromRecord(record) {
KZ.markerManager.setMarkerState(record, "hover-state");
}
var resultsPanelListeners = {
scope: this,
cellmouseenter: function(grid, rowIndex, htmlElement, event) {
highlightMarkerFromRecord(grid.getStore().getAt(rowIndex));
},
cellmouseleave: function(grid, rowIndex, htmlElement, event) {
KZ.markerManager.restoreHoverStateMarker();
},
expand: function(grid) {
grid.getView().refresh();
}
};
var stopResultsPanel = new Ext.grid.GridPanel({
id:'stop-results-panel',
title: 'Stops',
layout: 'fit',
iconCls: 'map-marker-sprite normal-state bus-stop-marker-type-normal-state-x',
autoScroll:true,
forceLayout: true,
store: stopResultsStore,
stripeRows: true,
hideHeaders: true,
disableSelection: true,
columns: [
{ id:'name', dataIndex: 'name', menuDisabled:true, xtype:'templatecolumn', tpl: stopResultTemplate, css: "white-space: normal !important;" }
],
plugins: [Ext.ux.grid.GridMouseEvents],
viewConfig: {
forceFit: true,
scrollOffset:2, //hides scrollbars
emptyText: 'There were no bus stop matches'
},
listeners: resultsPanelListeners
});
var placeResultsPanel = new Ext.grid.GridPanel({
id:'place-results-panel',
title: 'Places',
layout: 'fit',
iconCls: 'map-marker-sprite normal-state search-result-marker-type-normal-state-x',
autoScroll:true,
forceLayout: true,
store: placeResultsStore,
stripeRows: true,
hideHeaders: true,
disableSelection: true,
columns: [
{ id:'name', dataIndex: 'name', menuDisabled:true, xtype:'templatecolumn', tpl: placeResultTemplate, css: "white-space: normal !important;" }
],
plugins: [Ext.ux.grid.GridMouseEvents],
viewConfig: {
forceFit: true,
scrollOffset:2, //hides scrollbars
emptyText: 'There were no place matches'
},
listeners: resultsPanelListeners
});
//cellclick and rowclick do not alway fire when the row is not fully visible.
// I think this is a bug.
// stopResultsPanel.getSelectionModel().on("rowselect", function(sm, rowIndex, record) {
stopResultsPanel.on("rowclick", function(grid, rowIndex, e) {
var busStopRecord = stopResultsStore.getAt(rowIndex);
KZ.searchResultsPanel.extComponent.fireEvent("stopSelectedEvent", busStopRecord);
KZ.historyManager.addDepartureBoard(busStopRecord.id, busStopRecord.domRef.src);
}, this);
placeResultsPanel.on("rowclick", function(grid, rowIndex, e) {
this.extComponent.fireEvent("closeStopBoard");
var placeRecord = placeResultsStore.getAt(rowIndex);
this.extComponent.fireEvent("newSearchPointEvent", placeRecord.get('lat'), placeRecord.get('lng'));
}, this);
this.decorateRecordArrayWithAccessorFields = function(store, records, options) {
Ext.each(records, function(record) {
this.decorateRecordWithAccessorFields(record);
}, this);
}
this.decorateRecordWithAccessorFields = function(record, domRef) {
record.lat = record.get('lat');
record.lng = record.get('lng');
record.domRef = domRef;
}
stopResultsStore.on('load', this.decorateRecordArrayWithAccessorFields, this);
placeResultsStore.on('load', this.decorateRecordArrayWithAccessorFields, this);
Ext.StoreMgr.lookup('selectedCallingPatternResultsStore').on('load', this.decorateRecordArrayWithAccessorFields, this);
var resultsAccordion = new Ext.Panel({
id:'results-accordion',
layout: 'accordion',
hidden:true,
flex:1,
border:false,
bodyBorder:false,
autoDestroy:false,
layoutConfig: {
animate: false,
hideCollapseTool: true,
titleCollapse: true
},
items: [
stopResultsPanel,
placeResultsPanel
]
});
this.extComponent = new Ext.Container({
id:'search-results-panel',
layout:'vbox',
flex: 1,
autoDestroy:false,
layoutConfig: {
align : 'stretch',
pack: 'start'
},
items: [resultsStatus, searchHelpContainer, resultsAccordion, KZ.routeSearchResultsContainer.extComponent, ambiguousResultsContainer]
});
}
Ext.ns('KZ');
KZ.Dictionary = function() {
//Properties
//~Public
this.count = 0;
//~Private
this.Obj = new Object();
//Methods
//~Public
this.exists = Dictionary_exists;
this.add = Dictionary_add;
this.remove = Dictionary_remove;
this.removeAll = Dictionary_removeAll;
this.values = Dictionary_values;
this.keys = Dictionary_keys;
this.items = Dictionary_items;
this.getVal = Dictionary_getVal;
this.setVal = Dictionary_setVal;
this.setKey = Dictionary_setKey;
}
function Dictionary_exists(sKey){
return (this.Obj[sKey])?true:false;
}
function Dictionary_add(sKey,aVal){
var K = String(sKey);
if(this.exists(K)) return false;
this.Obj[K] = aVal;
this.count++;
return true;
}
function Dictionary_remove(sKey){
var K = String(sKey);
if(!this.exists(K)) return false;
delete this.Obj[K];
this.count--;
return true;
}
function Dictionary_removeAll(){
for(var key in this.Obj) delete this.Obj[key];
this.count = 0;
}
function Dictionary_values(){
var Arr = new Array();
for(var key in this.Obj) Arr[Arr.length] = this.Obj[key];
return Arr;
}
function Dictionary_keys(){
var Arr = new Array();
for(var key in this.Obj) Arr[Arr.length] = key;
return Arr;
}
function Dictionary_items(){
var Arr = new Array();
for(var key in this.Obj){
var A = new Array(key,this.Obj[key]);
Arr[Arr.length] = A;
}
return Arr;
}
function Dictionary_getVal(sKey){
var K = String(sKey);
return this.Obj[K];
}
function Dictionary_setVal(sKey,aVal){
var K = String(sKey);
if(this.exists(K))
this.Obj[K] = aVal;
else
this.add(K,aVal);
}
function Dictionary_setKey(sKey,sNewKey){
var K = String(sKey);
var Nk = String(sNewKey);
if(this.exists(K)){
if(!this.exists(Nk)){
this.add(Nk,this.getVal(K));
this.remove(K);
}
}
else if(!this.exists(Nk)) this.add(Nk,null);
}
Ext.ns('KZ');
KZ.MarkerHashMap = function() {
var dictionary = new KZ.Dictionary();
this.add = function(marker) {
return dictionary.add(marker.id, marker);
}
this.exists = function(markerId) {
return dictionary.exists(markerId);
}
this.getValues = function() {
return dictionary.values();
}
this.getMarker = function(markerId){
return dictionary.getVal(markerId);
}
this.removeAll = function(){
dictionary.removeAll();
}
}
Ext.ns('KZ');
/*****************************************
* I define what must be implemented by a map provider and
* define common functions across all map providers.
*****************************************/
KZ.AbstractMapProvider = function() {
var MSG_MAP_UNAVAILABLE = 'Map component is unavailable at this time.';
var mapProviderImplementationErrorMessage = function() { KZ.log("The Map Provider has not implemented a required method."); }
var markerDivContainer;
var topLeftControlsContainer;
var topLeftContainer;
/*********************************
* MAP FUNCTIONS
*********************************/
this.getZoom = mapProviderImplementationErrorMessage;
this.zoomIn = mapProviderImplementationErrorMessage;
this.zoomOut = mapProviderImplementationErrorMessage;
this.panUp = mapProviderImplementationErrorMessage;
this.panRight = mapProviderImplementationErrorMessage;
this.panDown = mapProviderImplementationErrorMessage;
this.panLeft = mapProviderImplementationErrorMessage;
this.setCenter = mapProviderImplementationErrorMessage;
this.zoomToBounds = mapProviderImplementationErrorMessage;
this.getBounds = function() {
// don't modify those values, it prevents against unnecessary requests for getting markers
return {swLat:1, swLng:1, neLat:0, neLng:0};
};
this.getBoundsCenter = mapProviderImplementationErrorMessage;
this.getBoundsZoomLevel = mapProviderImplementationErrorMessage;
// returns: a point with an x and y field
this.fromLatLngToDivPixel = function() {
return {x:0, y:0};
};
this.getMarkerDivContainer = function() {
if(markerDivContainer == undefined) {
var mapContainer = this.getTopLeftControlsContainer();
markerDivContainer = Ext.DomHelper.insertHtml('afterBegin', mapContainer, '');
}
return markerDivContainer;
}
/*********************************
* MAP CONTROLS
*********************************/
this.addOverviewMapControl = mapProviderImplementationErrorMessage;
this.addScaleControl = mapProviderImplementationErrorMessage;
//this.getTopLeftControlsContainer = mapProviderImplementationErrorMessage;
this.preventClickPropagationOnControl = mapProviderImplementationErrorMessage;
/********************
* EVENTS
********************/
//the callback requires no arguments
this.addMoveEndListener = mapProviderImplementationErrorMessage;
//the callback requires no arguments
this.addMoveStartListener = mapProviderImplementationErrorMessage;
//the callback requires no arguments
this.addClickListener = mapProviderImplementationErrorMessage;
//the callback requires no arguments
this.addZoomEndListener = mapProviderImplementationErrorMessage;
//the callback requires no arguments
this.addDragEndListener = mapProviderImplementationErrorMessage;
//the callback requires no arguments
this.addMouseWheelListener = mapProviderImplementationErrorMessage;
//the callback requires no arguments
this.addDoubleClickListener = mapProviderImplementationErrorMessage;
this.checkResize = mapProviderImplementationErrorMessage;
/**********************
* ABSTRACT FUNCTIONS
**********************/
this.getTopLeftControlsContainer = function() {
if(topLeftControlsContainer == undefined) {
var mapContainer = document.getElementById(this.containerId);
topLeftControlsContainer = Ext.DomHelper.insertHtml('afterBegin', mapContainer, ''+MSG_MAP_UNAVAILABLE+'
');
}
return topLeftControlsContainer;
}
this.getTopLeftContainer = function() {
if(topLeftContainer == undefined) {
var mapControlsContainer = this.getTopLeftControlsContainer();
topLeftContainer = Ext.DomHelper.insertHtml('afterBegin', mapControlsContainer, '');
if(!this.isMapProviderAvailable()) {
topLeftContainer.className = 'x-hidden';
}
this.preventClickPropagationOnControl(topLeftContainer);
}
return topLeftContainer;
}
this.addPanAndZoomControl = function() {
var panControlButtons = [
{
xtype:'button',
id:'pan-north-button',
iconCls:'pan-north-icon',
x: 16,
y: 0,
handler: function(button, event) {
KZ.mapPanel.getMap().panUp();
_gaq.push(['_trackEvent', 'Map Pan', 'Navigation Button Pressed', 'North']);
event.stopEvent();
}
},
{
xtype:'button',
id:'pan-east-button',
iconCls:'pan-east-icon',
x: 32,
y: 16,
handler: function(button, event) {
KZ.mapPanel.getMap().panRight();
_gaq.push(['_trackEvent', 'Map Pan', 'Navigation Button Pressed', 'East']);
event.stopEvent();
}
},
{
xtype:'button',
id:'pan-west-button',
iconCls:'pan-west-icon',
x: 0,
y: 16,
handler: function(button, event) {
KZ.mapPanel.getMap().panLeft();
_gaq.push(['_trackEvent', 'Map Pan', 'Navigation Button Pressed', 'West']);
event.stopEvent();
}
},
{
xtype:'button',
id:'pan-south-button',
iconCls:'pan-south-icon',
x: 16,
y: 32,
handler: function(button, event) {
KZ.mapPanel.getMap().panDown();
_gaq.push(['_trackEvent', 'Map Pan', 'Navigation Button Pressed', 'South']);
event.stopEvent();
}
},
{
xtype:'button',
id:'zoom-out-button',
iconCls:'zoom-out-icon',
x: 0,
y: 48,
handler: function(button, event) {
KZ.mapPanel.getMap().zoomOut();
_gaq.push(['_trackEvent', 'Map Zoom', 'Navigation Button Pressed', 'Out']);
event.stopEvent();
}
},
{
xtype:'button',
id:'zoom-in-button',
iconCls:'zoom-in-icon',
x: 32,
y: 48,
handler: function(button, event) {
KZ.mapPanel.getMap().zoomIn();
_gaq.push(['_trackEvent', 'Map Zoom', 'Navigation Button Pressed', 'In']);
event.stopEvent();
}
}
];
var customMapControlsContainer = this.getTopLeftContainer();
new Ext.Panel({
layout: 'absolute',
height:66,
width:49,
baseCls: 'x-plain',
renderTo: customMapControlsContainer,
items: panControlButtons
});
}
this.isMapProviderAvailable = function() {
if(typeof this.providerAvailability == 'undefined') {
this.providerAvailability = true;
}
return this.providerAvailability;
}
}
Ext.ns('KZ');
/*****************************************
* This is a Google Maps abstraction
*****************************************/
KZ.MapProvider = function(containerId) {
/*********************************
* CONSTRUCTOR
*********************************/
this.containerId = containerId;
if (typeof GMap2 == 'undefined') {
this.providerAvailability = false;
return;
}
//lbtm.farasi.kizoom.com Google Maps Key ABQIAAAAoBYVJtlMCG18UkeA9cCNzRTzyv0w7mfIugLiRTlTfyunp-juThRyGqqkxafLJpWyAe2yW4aM5N9Xsg
var map = new GMap2(document.getElementById(containerId));
map.enableScrollWheelZoom();
/*********************************
* MAP FUNCTIONS
*********************************/
// The zoom may need some normalization
this.getZoom = function() {
return map.getZoom();
}
this.zoomIn = function() {
map.zoomIn();
}
this.zoomOut = function() {
map.zoomOut();
}
this.panUp = function() { map.panDirection(0, 1); }
this.panRight = function() { map.panDirection(-1, 0); }
this.panDown = function() { map.panDirection(0, -1); }
this.panLeft = function() { map.panDirection(1, 0); }
// zoom is an optional parameter
this.setCenter = function(latitude, longitude, zoom) {
map.setCenter(new GLatLng(latitude, longitude), zoom);
}
this.zoomToBounds = function(swLatBB, swLngBB, neLatBB, neLngBB) {
var swLatLng = new GLatLng(swLatBB, swLngBB);
var neLatLng = new GLatLng(neLatBB, neLngBB);
var bounds = new GLatLngBounds(swLatLng, neLatLng);
map.setCenter(bounds.getCenter(), map.getBoundsZoomLevel(bounds));
}
this.getBounds = function() {
var bounds = map.getBounds();
return {swLat:bounds.getSouthWest().lat(), swLng:bounds.getSouthWest().lng(), neLat:bounds.getNorthEast().lat(), neLng:bounds.getNorthEast().lng()};
}
this.getBoundsCenter = function(swLatBB, swLngBB, neLatBB, neLngBB) {
var swLatLng = new GLatLng(swLatBB, swLngBB);
var neLatLng = new GLatLng(neLatBB, neLngBB);
var bounds = new GLatLngBounds(swLatLng, neLatLng);
return {latitude: bounds.getCenter().lat(), longitude:bounds.getCenter().lng()};
}
this.getBoundsZoomLevel = function(swLatBB, swLngBB, neLatBB, neLngBB) {
var swLatLng = new GLatLng(swLatBB, swLngBB);
var neLatLng = new GLatLng(neLatBB, neLngBB);
var bounds = new GLatLngBounds(swLatLng, neLatLng);
return map.getBoundsZoomLevel(bounds);
}
/* returns: a point with an x and y field */
this.fromLatLngToDivPixel = function(latitude, longitude) {
return map.fromLatLngToDivPixel(new GLatLng(latitude, longitude));
}
this.getMarkerDivContainer = function() {
return map.getPane(G_MAP_MARKER_PANE);
}
/*********************************
* MAP CONTROLS
*********************************/
this.addOverviewMapControl = function() {
map.addControl(new GOverviewMapControl());
}
this.addScaleControl = function() {
map.addControl(new GScaleControl());
}
this.getTopLeftControlsContainer = function() {
return Ext.DomQuery.selectNode('#map-container');
}
this.preventClickPropagationOnControl = function(element) {
// This did not appear to be necessary for Google maps since controls are added to an area of the
// map DOM where clicks do not propagate to the map itself.
}
/********************
* EVENTS
********************/
//the callback requires no arguments
this.addMoveEndListener = function(callback, scope) {
GEvent.bind(map, 'moveend', scope, callback);
}
//the callback requires no arguments
this.addMoveStartListener = function(callback, scope) {
GEvent.bind(map, 'movestart', scope, callback);
}
//the callback requires no arguments
this.addClickListener = function(callback, scope) {
GEvent.bind(map, 'click', scope, callback);
}
//the callback requires no arguments
this.addZoomEndListener = function(callback, scope) {
GEvent.bind(map, 'zoomend', scope, callback);
}
this.addDragEndListener = function(callback, scope) {
GEvent.bind(map, 'dragend', scope, callback);
}
this.addMouseWheelListener = function(callback, scope) {
GEvent.addDomListener(map.getContainer(), "DOMMouseScroll", callback);
}
this.addDoubleClickListener = function(callback, scope) {
GEvent.bind(map, "dblclick", scope, callback);
}
this.checkResize = function() {
map.checkResize();
}
return this;
}
KZ.MapProvider.prototype = new KZ.AbstractMapProvider;
Ext.ns('KZ');
KZ.MapPanel = function() {
var map;
var hideZoomInHintAfterDelayTask, showZoomInHintAfterDelayTask;
var backgroundMarkersCurrentlyHidden = false;
this.getMap = function() {
return map;
}
this.extComponent = new Ext.Container({
region:'center',
layout:'fit',
split:true,
minHeight:1,
items: [{
html: '',
xtype: 'panel'
}
]
});
this.initialise = function() {
map = new KZ.MapProvider('map-container');
map.setCenter(KZ.Config.initialMapLatitude, KZ.Config.initialMapLongitude, KZ.Config.initialMapZoom);
map.addPanAndZoomControl();
map.addScaleControl();
KZ.markerManager.initialise();
map.addMoveEndListener(this.onMapMoveEnd, this);
map.addMoveStartListener(KZ.tooltipManager.hideTooltipInstantly, KZ.tooltipManager);
map.addClickListener(KZ.tooltipManager.hideTooltip, KZ.tooltipManager);
map.addZoomEndListener(this.onZoomEnd, this);
map.addDragEndListener(function() { _gaq.push(['_trackEvent', 'Map Pan', 'Map Dragged']); }, this);
map.addMouseWheelListener(function(evt) { if ((evt.detail || -evt.wheelDelta) < 0) { _gaq.push(['_trackEvent', 'Map Zoom', 'Mouse Wheel', 'In']); } else { _gaq.push(['_trackEvent', 'Map Zoom', 'Mouse Wheel', 'Out']); } }, this);
map.addDoubleClickListener(function(evt) { _gaq.push(['_trackEvent', 'Map Zoom', 'Double Click', 'In']); }, this);
this.extComponent.on('resize', function(event, element) {
var mapPanel = this;
var checkResizeFunc = function() { map.checkResize(); mapPanel.onMapMoveEnd(); }
checkResizeFunc.defer(300); // Without deferring the call, map doesn't always update properly
}, this);
hideZoomInHintAfterDelayTask = new Ext.util.DelayedTask(function() {
Ext.get('zoom-in-hint').fadeOut({duration:1, remove:false, useDisplay: true});
});
showZoomInHintAfterDelayTask = new Ext.util.DelayedTask(function() {
Ext.get('zoom-in-hint').fadeIn({duration:1, remove:false, useDisplay: true, endOpacity: 0.88});
});
this.onMapMoveEnd.defer(500, this);
}
this.onZoomEnd = function() {
if(map.getZoom() < KZ.Config.BG_MARKER_ZOOM_THRESHOLD) {
if(KZ.searchResultsPanel.resultsShowing()) {
Ext.get('zoom-in-hint').update('Zoom in closer to view other bus stops');
} else {
Ext.get('zoom-in-hint').update('Zoom in closer to view bus stops');
}
if(!Ext.get('zoom-in-hint').isDisplayed()) {
showZoomInHintAfterDelayTask.delay(0);
}
hideZoomInHintAfterDelayTask.delay(8000);
} else {
hideZoomInHintAfterDelayTask.delay(0);
}
}
this.onMapMoveEnd = function() {
var existingBackgroundMarkers = KZ.markerManager.backgroundMarkerHashMap.getValues();
if(!Ext.getCmp('stop-results-panel').collapsed) {
KZ.markerManager.updateMarkers(Ext.StoreMgr.lookup('stopResultsStore').getRange());
}
if(!Ext.getCmp('place-results-panel').collapsed) {
KZ.markerManager.updateMarkers(Ext.StoreMgr.lookup('placeResultsStore').getRange());
}
KZ.markerManager.updateMarkers(Ext.StoreMgr.lookup('selectedCallingPatternResultsStore').getRange());
if(map.getZoom() < KZ.Config.BG_MARKER_ZOOM_THRESHOLD) { // Too zoomed out for bg markers
if(existingBackgroundMarkers.length >= 1) {
if(!backgroundMarkersCurrentlyHidden) {
KZ.markerManager.hideMarkers(existingBackgroundMarkers);
backgroundMarkersCurrentlyHidden = true;
}
}
} else { // if zoomed in enough for bg markers
KZ.markerManager.updateMarkers(existingBackgroundMarkers, 'normal-state', true);
KZ.markerManager.fetchBackgroundMarkers();
backgroundMarkersCurrentlyHidden = false;
}
}
this.showStopInformationPanelFromStore = function() {
var record = Ext.StoreMgr.lookup(this.storeIdArg).getById(this.stopIdArg);
if(record){
KZ.mapPanel.extComponent.fireEvent("stopSelectedEvent", record);
}
}
this.showStopInformationPanelFromMarker = function() {
KZ.mapPanel.showStopInformationPanelFromId(this.stopIdArg);
}
// listOfStores is optional
this.getMarkerRecord = function (stopId, listOfStores) {
if(!listOfStores) {
listOfStores = new Array('stopResultsStore', 'selectedCallingPatternResultsStore', 'backgroundMarkerHashMap',
'placeResultsStore', 'recentStopsStore', 'favouriteStopsStore');
}
var busStopRecord = undefined;
for(i=0; i 15) {
var centerPoint = map.getBoundsCenter(swLatBB, swLngBB, neLatBB, neLngBB);
map.setCenter(centerPoint.latitude, centerPoint.longitude, 15);
} else {
var boundsPadding = (neLatBB - swLatBB) * 0.05;//Such that search result markers are not placed at the VERY edge of the map
map.zoomToBounds(swLatBB - boundsPadding, swLngBB - boundsPadding, neLatBB + boundsPadding, neLngBB + boundsPadding);
}
this.onZoomEnd.defer(2000);
},
scope: this
}
});
// IMPORTANT: newSearchPointEvent should be raised before you load data into the store (for efficiency)
this.extComponent.on({
'newSearchPointEvent' : {
fn: function(lat, lng) {
if(map.getZoom() < KZ.Config.BG_MARKER_ZOOM_THRESHOLD) {
map.setCenter(lat, lng, KZ.Config.BG_MARKER_ZOOM_THRESHOLD);
} else {
map.setCenter(lat, lng);
}
},
scope: this
}
});
this.extComponent.on({
'newSearchEvent' : {
fn: KZ.markerManager.clearSearchResultMarkers,
scope: KZ.markerManager
}
});
}
Ext.ns('KZ');
KZ.TooltipManager = function() {
var tooltip, tooltipContents = undefined;
var hideTooltipAfterDelayTask = undefined;
var currentTooltipTargetPos = {top:-100, left:-100};
var HIDE_TOOLTIP_DELAY = 0;
var TOOLTIP_OFFSET = 18;
var previousTooltipId = undefined;
var stopMarkerTooltipTemplate, placeMarkerTooltipTemplate;
function initialiseStopMarkerTooltipXTemplate() {
if(!stopMarkerTooltipTemplate) {
stopMarkerTooltipTemplate = new Ext.XTemplate(
'',
'',
'towards {towards}
',
'',
'',
'{[this.getDirectionString(values.direction)]}
',
'',
'',
'{name} ',
'',
{
compiled:true,
isDefined: function(stringValue) {
return stringValue != undefined && stringValue != '';
},
getDirectionString: function(direction) {
switch (direction) {
case 'n':
return 'heading north'
break;
case 'ne':
return 'heading north-east'
break;
case 'e':
return 'heading east'
break;
case 'se':
return 'heading south-east'
break;
case 's':
return 'heading south'
break;
case 'sw':
return 'heading south-west'
break;
case 'w':
return 'heading west'
break;
case 'nw':
return 'heading north-west'
break;
default:
return '' // Empty string if we do not have heading information
break;
}
}
});
stopMarkerTooltipTemplate.compile();
}
}
var placeMarkerTooltipTemplateText = [''];
function initialiseTooltip() {
tooltip = Ext.get('map-marker-tooltip-holder');
tooltipContents = Ext.get('map-marker-tooltip');
tooltip.removeClass('x-hide-display');
tooltip.enableDisplayMode();
tooltip.on('mouseover', KZ.tooltipManager.cancelHideTooltipTask);
tooltip.on('mouseout', KZ.tooltipManager.hideTooltip);
}
this.showTooltip = function(evt, targetEl, options) {
if(!tooltip) {
initialiseTooltip();
}
if(hideTooltipAfterDelayTask) {
hideTooltipAfterDelayTask.cancel();
}
targetEl = Ext.fly(targetEl);
// If user hovers back over the same tooltip
if(currentTooltipTargetPos.top == targetEl.getTop()
&& currentTooltipTargetPos.left == targetEl.getX()
&& previousTooltipId != undefined && previousTooltipId == targetEl.id) {
if(!tooltip.isVisible()) {
tooltip.show(true);
}
return;
}
switch(this.storeIdArg) {
case 'backgroundStopStore' :
var marker = KZ.markerManager.backgroundMarkerHashMap.getMarker(this.stopIdArg);
KZ.markerManager.setMarkerState(marker, 'hover-state');
initialiseStopMarkerTooltipXTemplate();
stopMarkerTooltipTemplate.overwrite(tooltipContents, {stopId: marker.id, name: marker.name, stopIndicator: marker.stopIndicator, towards: marker.towards, direction: marker.direction, routes: marker.routes});
break;
case 'stopResultsStore' :
var record = Ext.StoreMgr.lookup('stopResultsStore').getById(this.stopIdArg);
KZ.markerManager.setMarkerState(record, 'hover-state');
initialiseStopMarkerTooltipXTemplate();
stopMarkerTooltipTemplate.overwrite(tooltipContents, {stopId: record.get('id'), name: record.get('name'), stopIndicator: record.get('stopIndicator'), towards: record.get('towards'), direction: record.get('direction'), routes: record.get('routes')});
break;
case 'selectedCallingPatternResultsStore' :
var record = Ext.StoreMgr.lookup('selectedCallingPatternResultsStore').getById(this.stopIdArg);
KZ.markerManager.setMarkerState(record, 'hover-state');
initialiseStopMarkerTooltipXTemplate();
stopMarkerTooltipTemplate.overwrite(tooltipContents, {stopId: record.get('id'), name: record.get('name'), stopIndicator: record.get('stopIndicator'), towards: record.get('towards'), direction: record.get('direction'), routes: record.get('routes')});
break;
case 'placeResultsStore' :
// NB. For place and postcode
var record = Ext.StoreMgr.lookup('placeResultsStore').getById(this.stopIdArg);
KZ.markerManager.setMarkerState(record, 'hover-state');
if(!placeMarkerTooltipTemplate) {
placeMarkerTooltipTemplate = new Ext.XTemplate(placeMarkerTooltipTemplateText);
placeMarkerTooltipTemplate.compile();
}
placeMarkerTooltipTemplate.overwrite(tooltipContents, {id: record.id, name: record.get('name')});
break;
default :
KZ.log('Could not generate tooltip- type not defined');
}
previousTooltipId = targetEl.id;
currentTooltipTargetPos.top = targetEl.getTop();
currentTooltipTargetPos.left = targetEl.getX();
tooltip.setTop(currentTooltipTargetPos.top);
tooltip.setLeft(currentTooltipTargetPos.left + TOOLTIP_OFFSET);
tooltip.show(true);
}
this.hideTooltipInstantly = function() {
this.hideTooltip(true);
}
this.hideTooltip = function(instantly) {
if(tooltip) {
if(instantly === true) {
tooltip.hide();
KZ.markerManager.restoreHoverStateMarker();
return;
}
if(!hideTooltipAfterDelayTask) {
hideTooltipAfterDelayTask = new Ext.util.DelayedTask(function() {
tooltip.hide();
KZ.markerManager.restoreHoverStateMarker();
});
}
hideTooltipAfterDelayTask.delay(HIDE_TOOLTIP_DELAY);
}
}
this.cancelHideTooltipTask = function() {
if(hideTooltipAfterDelayTask) {
hideTooltipAfterDelayTask.cancel();
}
}
Ext.get('map-marker-tooltip').on({
'click' : {
fn: function(event, element) {
// check stop name vs bus route name
if(new Ext.Element(element).hasClass('stopAnchor')) {
var busStopId = element.getAttribute('rel');
KZ.mapPanel.showStopInformationPanelFromId(busStopId);
}
},
scope: this,
stopEvent: true,
delegate: 'a'
}
});
}
Ext.ns('KZ');
KZ.HistoryManager = function() {
Ext.History.init();
var mainPageToken = '/';
this.initialize = function() {
Ext.History.add(mainPageToken);
}
var tokenDelimiter = '=';
var actionDelimiter ='|';
var searchUrlWord = 'searchTerm';
var deptBoardStopIdUrlWord = 'stopCode';
var lastSearch = undefined;
var lastDeptBoard = undefined;
this.getSearchUrlWord = function () {
return searchUrlWord;
}
this.getTokenDelimiter = function () {
return tokenDelimiter;
}
this.getActionDelimiter = function () {
return actionDelimiter;
}
this.getLastSearch = function() {
return lastSearch;
}
this.getLastDeptBoard = function() {
return lastDeptBoard;
}
//This lock is used to react against back/forward clicks and URL modified by the user only
var lockChangeEvent = false;
Ext.History.on('change', function(token){
if(!lockChangeEvent){
if(token != mainPageToken){
KZ.historyManager.parseLastUrl(token);
if(lastSearch) {//if url contains searchterm
if(lastDeptBoard) { //if url contains searchterm + departure board
showDeptBoard(lastDeptBoard);
} else {
var doNewSearch = true;
var currentResults = KZ.searchResultsPanel.getCurrentSearchResults();
if( (currentResults != undefined && currentResults.stopCodeResult != undefined
&& currentResults.stopCodeResult.id == lastSearch)
|| (KZ.searchBoxPanel.currentSearchTerm != undefined
&& KZ.searchBoxPanel.currentSearchTerm == lastSearch)){
doNewSearch = false;
KZ.searchBoxPanel.performSearch(lastSearch,false);
}
if(doNewSearch == true) {
KZ.searchBoxPanel.performSearch(lastSearch);
}
}
} else if(lastDeptBoard) { //if url ONLY contains departure board
showDeptBoard(lastDeptBoard);
} else {
KZ.log('url not well formed');
}
}
else { //first page...
KZ.searchBoxPanel.currentSearchTerm = undefined;
KZ.searchResultsPanel.resetCurrentSearchResults();
KZ.searchResultsPanel.resetSearchResultsPanel();
KZ.util.showVBoxLayoutItem(KZ.searchResultsPanel.getSearchHelpContainer());
KZ.searchPanel.doLayout();
Ext.StoreMgr.lookup('stopResultsStore').removeAll();
Ext.StoreMgr.lookup('placeResultsStore').removeAll();
Ext.StoreMgr.lookup('selectedCallingPatternResultsStore').removeAll();
KZ.markerManager.hideSearchResultMarkers();
KZ.mapPanel.getMap().setCenter(KZ.Config.initialMapLatitude, KZ.Config.initialMapLongitude, KZ.Config.initialMapZoom);
KZ.InitialSearchPanel.showInitialSearchPanelAndHideMap();
}
}
lockChangeEvent = false;
});
//TODO: similar to 'stopSelectedEvent' in TravelMap, maybe reuse some code by firing event, need to be carefully with recursion. --VF
//TODO: this needs refactoring to use the stopSelectedEvent --WR
this.restoreMarker = function (busStopRecord) {
if(busStopRecord.domRef != undefined){
KZ.searchResultsPanel.decorateRecordWithAccessorFields(busStopRecord, busStopRecord.domRef);
} else {
var busStopId = busStopRecord.data.id;
busStopRecord = KZ.mapPanel.getMarkerRecord(busStopId);
if(busStopRecord != undefined){
KZ.markerManager.createBackgroundMarkers(busStopRecord.data);
busStopRecord = KZ.mapPanel.getMarkerRecord(busStopId);
}
}
KZ.userStopsStore.addRecentStop(busStopRecord);
KZ.markerManager.restoreSelectedStateMarker();
KZ.markerManager.setMarkerState(busStopRecord, 'selected-state');
KZ.markerManager.stopInformationPanelSelectedMarker = busStopRecord;
var busStopId = busStopRecord.get("id");
_gaq.push(['_trackEvent', 'Stop Selected', busStopRecord.data.id, busStopRecord.data.name]); // TODO: Once the stop selected event is introduced, this line should also be removed.
KZ.stopInformationPanel.show(busStopRecord); //TODO: This bypasses the stopSelectedEvent which should always be used as this is the common entry point for showing a departure board (used for tracking and other things). Once this method is refactored, remove the analytics tracking code above --WR
}
this.addNewSearchHistory = function(searchTerm, clearUrl) {
lockChangeEvent = true;
KZ.historyManager.parseLastUrl(Ext.History.getToken());
if(clearUrl != undefined && clearUrl == true) {
Ext.History.add(KZ.historyManager.buildUrl(searchTerm));
} else {
Ext.History.add(KZ.historyManager.buildUrl(searchTerm, lastDeptBoard));
}
}
this.addDepartureBoard = function(stopId, imgSrc){
lockChangeEvent = true;
KZ.historyManager.parseLastUrl(Ext.History.getToken());
var newUrl = KZ.historyManager.buildUrl(lastSearch, stopId)
Ext.History.add(newUrl);
}
this.restoreBookmark = function(fragmentIdentifier) {
if(fragmentIdentifier != '') {
KZ.historyManager.parseLastUrl(fragmentIdentifier);
if(lastDeptBoard) {//if url contains searchterm
showDeptBoard(lastDeptBoard);
_gaq.push(['_trackEvent', 'InitialSearchMethod', 'DeepLinkToDepartureBoard']);
}
}
}
var showDeptBoard = function (stopId, isStopCode) {
if(KZ.InitialSearchPanel.isVisible() == true){
KZ.InitialSearchPanel.hideInitialSearchPanelAndShowMap();
}
var record = KZ.mapPanel.getMarkerRecord(stopId, new Array('stopResultsStore','recentStopsStore', 'favouriteStopsStore'));
//if it's not in the store we have to do a new request
if(record == undefined) {
KZ.historyManager.showDeptBoardFromServer(stopId, isStopCode);
} else {
KZ.markerManager.createBackgroundMarkers(record.data);
record = KZ.mapPanel.getMarkerRecord(record.get("id"));
KZ.historyManager.restoreMarker(record);
}
}
this.closeStopBoardPanel = function () {
KZ.historyManager.parseLastUrl(Ext.History.getToken());
if(lastDeptBoard != undefined) {
lockChangeEvent = true;
Ext.History.add(KZ.historyManager.buildUrl(lastSearch));
}
}
this.showDeptBoardFromServer = function (stopId, isStopCode) {
if(stopId) {
var url = '/search?searchTerm=' + stopId;
if(isStopCode){
url = 'stopCode/' + stopId + '/';
}
try {
KZ.searchBoxPanel.getSearchConnection().request({
url: url,
headers: {Accept: 'application/json'},
timeout: 8000,
scope: this,
success: function(resp, opt) {
var jsonResults = eval('(' + resp.responseText + ')');
if(resp.responseText != '{}' && jsonResults.stopCodeResult != undefined) {
var stop = jsonResults.stopCodeResult;
KZ.markerManager.createBackgroundMarkers(stop);
var record = KZ.mapPanel.getMarkerRecord(stopId);
KZ.historyManager.restoreMarker(record);
} else {
KZ.log('The stop: ' + stopId + ' does not exist');
}
},
failure: function(response, opts) {
KZ.log('server-side failure with status code ' + response.status);
var emptyResults = {};
}
});
} catch(e) {
KZ.log(e);
}
}
}
this.parseLastUrl = function(url) {
lastSearch = undefined;
lastDeptBoard = undefined;
if(url){
var parts = url.split(actionDelimiter);
for(i=0; i');
searchResultsMarkerContainer = dh.insertHtml('afterBegin', mapContainer, '');
}
// These functions make use of a mercator projection to break the map into square regions. These regions are then
// used as areas of the map in which to request tiles
function longToTile(lon,zoom) { return (Math.floor((lon+180)/360*Math.pow(2,zoom))); }
function latToTile(lat,zoom) { return (Math.floor((1-Math.log(Math.tan(lat*Math.PI/180) + 1/Math.cos(lat*Math.PI/180))/Math.PI)/2 *Math.pow(2,zoom))); }
function tileToLong(x,z) { return (x/Math.pow(2,z)*360-180); }
function tileToLat(y,z) { var n=Math.PI-2*Math.PI*y/Math.pow(2,z); return (180/Math.PI*Math.atan(0.5*(Math.exp(n)-Math.exp(-n)))); }
// Fetch markers based on mercator projection tiles. Store the set of tiles you have already requested
// such that they are not re-requested.
this.fetchBackgroundMarkers = function() {
if(!markerConnection) {
markerConnection = new Ext.data.Connection({
timeout: 8000,
autoAbort: false
});
}
var bounds = KZ.mapPanel.getMap().getBounds();
var minXTileNumber = longToTile(bounds.swLng, MERCATOR_PROJECTION_ZOOM_LEVEL);
var maxXTileNumber = longToTile(bounds.neLng, MERCATOR_PROJECTION_ZOOM_LEVEL);
var minYTileNumber = latToTile(bounds.neLat, MERCATOR_PROJECTION_ZOOM_LEVEL);
var maxYTileNumber = latToTile(bounds.swLat, MERCATOR_PROJECTION_ZOOM_LEVEL);
for (var xTileNumber = minXTileNumber; xTileNumber <= maxXTileNumber; xTileNumber++) {
for (var yTileNumber = minYTileNumber; yTileNumber <= maxYTileNumber; yTileNumber++) {
try {
if(markerTilesFetchedSet.exists('tileKey-' + xTileNumber + '-' + yTileNumber)) {
continue; // This mercator tile area has already had its stops fetched
} else {
markerTilesFetchedSet.add('tileKey-' + xTileNumber + '-' + yTileNumber, 'a');
}
var tileSwLat = tileToLat(yTileNumber + 1, MERCATOR_PROJECTION_ZOOM_LEVEL);
var tileSwLng = tileToLong(xTileNumber, MERCATOR_PROJECTION_ZOOM_LEVEL);
var tileNeLat = tileToLat(yTileNumber, MERCATOR_PROJECTION_ZOOM_LEVEL);
var tileNeLng = tileToLong(xTileNumber + 1, MERCATOR_PROJECTION_ZOOM_LEVEL);
markerConnection.request({
url: "/markers/swLat/" + tileSwLat + "/swLng/" + tileSwLng + "/neLat/" + tileNeLat + "/neLng/" + tileNeLng + "/",
headers: {Accept: 'application/json'},
timeout: 8000,
scope: this,
success: function(resp, opt) {
var markerResults = eval('(' + resp.responseText + ')');
if(KZ.util.isSet(markerResults.markers)) {
this.createBackgroundMarkers(markerResults.markers);
}
},
failure: function(response, opts) {
markerTilesFetchedSet.remove('tileKey-' + xTileNumber + '-' + yTileNumber, 'a');
KZ.log('Could not fetch markers. Server-side failure with status code ' + response.status);
}
});
} catch(e) {
KZ.log(e);
}
}
}
}
// State is optional
this.updateMarkers = function(markers, state, backgroundMarkerBool){
if(markers.length == 0) {
return;
}
var markerState, commonAnchorPoint, uncommenAnchorPoint;
if(state != undefined) {
commonAnchorPoint = this.getMarkerAnchorPoint(state);
markerState = markers[0].domRef.className.replace(MARKER_STATE_MATCHER, state);
}
var bounds = KZ.mapPanel.getMap().getBounds();
var swLat = bounds.swLat;
var swLng = bounds.swLng;
var neLat = bounds.neLat;
var neLng = bounds.neLng;
var point;
var stopResultsPanelShowing = !KZ.util.isHidden(Ext.getCmp('results-accordion')) && !Ext.getCmp('stop-results-panel').collapsed;
var routeSearchPanelShowing = !KZ.util.isHidden(KZ.routeSearchResultsContainer.extComponent);
var stopResultsPanelAndRouteSearchPanelHidden = !stopResultsPanelShowing && !routeSearchPanelShowing;
Ext.each(markers, function(marker) {
// Render markers that are just off screen also so they are already rendered when the user drags the map
if(marker.lat > swLat - 0.04 && marker.lng > swLng - 0.04
&& marker.lat < neLat + 0.04 && marker.lng < neLng + 0.04) {
point = KZ.mapPanel.getMap().fromLatLngToDivPixel(marker.lat, marker.lng);
if(markerState == undefined ||
(this.stopInformationPanelSelectedMarker != undefined &&
this.stopInformationPanelSelectedMarker.id == marker.id)) {
uncommenAnchorPoint = this.getMarkerAnchorPoint(marker.domRef.className.match(MARKER_STATE_MATCHER)[0]);
marker.domRef.style.top = (point.y - uncommenAnchorPoint.y) + 'px';
marker.domRef.style.left = (point.x - uncommenAnchorPoint.x) + 'px';
} else {
marker.domRef.className.replace(MARKER_STATE_MATCHER, state);
marker.domRef.style.top = (point.y - commonAnchorPoint.y) + 'px';
marker.domRef.style.left = (point.x - commonAnchorPoint.x) + 'px';
}
if(backgroundMarkerBool == undefined) {
marker.domRef.style.display = 'block';
}
// Display bg marker only when there is no equivalent search result marker
if(backgroundMarkerBool === true) {
if(stopResultsPanelAndRouteSearchPanelHidden ||
((stopResultsPanelShowing && Ext.StoreMgr.lookup('stopResultsStore').getById(marker.id) == undefined) ||
(routeSearchPanelShowing && Ext.StoreMgr.lookup('selectedCallingPatternResultsStore').getById(marker.id) == undefined))) {
marker.domRef.style.display = 'block';
} else {
marker.domRef.style.display = 'none';
}
} else {
marker.domRef.style.display = 'block';
}
} else {
// may want to destroy dom nodes and store entry here
marker.domRef.style.display = 'none';
}
}, this);
}
this.createBackgroundMarkers = function(markers) {
var markerAnchorPoint = this.getMarkerAnchorPoint('normal-state');
var dh = Ext.DomHelper;
var name, lat, lng, point;
Ext.each(markers, function(marker, index) {
if(!this.backgroundMarkerHashMap.exists(marker.id)) {
lat = marker.lat;
lng = marker.lng;
point = KZ.mapPanel.getMap().fromLatLngToDivPixel(lat, lng);
offsetTop = point.y - markerAnchorPoint.y;
offsetLeft = point.x - markerAnchorPoint.x;
if(marker.direction == undefined || marker.direction == '') {
marker.direction = 'x';
}
var markerElement = dh.insertHtml('afterBegin', backgroundMarkerContainer, '');
var scope = {stopIdArg: marker.id, storeIdArg: 'backgroundStopStore'};
Ext.EventManager.on(markerElement, 'click', KZ.mapPanel.showStopInformationPanelFromMarker, scope); // EventManager
Ext.EventManager.on(markerElement, 'mouseover', KZ.tooltipManager.showTooltip, scope);
Ext.EventManager.on(markerElement, 'mouseout', KZ.tooltipManager.hideTooltip, scope);
marker.domRef = markerElement;
this.backgroundMarkerHashMap.add(marker);
if(Ext.StoreMgr.lookup('stopResultsStore').getById(marker.id) != undefined ||
Ext.StoreMgr.lookup('selectedCallingPatternResultsStore').getById(marker.id) != undefined) {
marker.domRef.style.display = 'none';
}
}
}, this);
}
this.createMarkersFromStore = function(store) {
// Have markers been created for this store already?
if(store.getRange(0)[0]) {
if(store.getRange(0)[0].domRef != undefined) {
this.updateMarkers(store.getRange());
return;
}
}
var markerAnchorPoint = this.getMarkerAnchorPoint('normal-state');
var dh = Ext.DomHelper;
var id, point;
Ext.each(store.getRange(), function(record, index) {
if(record.domRef == undefined) {
point = KZ.mapPanel.getMap().fromLatLngToDivPixel(record.lat, record.lng);
point.y -= markerAnchorPoint.y;
point.x -= markerAnchorPoint.x;
if(record.data.direction == undefined || record.data.direction == '') {
record.data.direction = 'x';
}
var markerElement = dh.insertHtml('afterBegin', searchResultsMarkerContainer, '');
var scope = { stopIdArg: record.id, storeIdArg: store.storeId};
if(store.storeId == 'stopResultsStore' || store.storeId == 'selectedCallingPatternResultsStore') {
Ext.EventManager.on(markerElement, 'click', KZ.mapPanel.showStopInformationPanelFromStore, scope);
} else {
Ext.EventManager.on(markerElement, 'click', KZ.mapPanel.zoomInOnMarker, scope);
}
Ext.EventManager.on(markerElement, 'mouseover', KZ.tooltipManager.showTooltip, scope);
Ext.EventManager.on(markerElement, 'mouseout', KZ.tooltipManager.hideTooltip, scope);
record.domRef = markerElement;
}
record.domRef.style.display = 'block';
}, this);
}
this.getMarkerAnchorPoint = function(state) {
var point = {};
switch (state) {
case 'normal-state' :
point.y = NORMAL_STATE_Y_ANCHOR_POINT;
point.x = NORMAL_STATE_X_ANCHOR_POINT;
return point;
case 'hover-state' :
point.y = HOVER_STATE_Y_ANCHOR_POINT;
point.x = HOVER_STATE_X_ANCHOR_POINT;
return point;
case 'selected-state' :
point.y = SELECTED_STATE_Y_ANCHOR_POINT;
point.x = SELECTED_STATE_X_ANCHOR_POINT;
return point;
default :
KZ.log('Invalid state argument: ' + state);
}
}
this.restoreSelectedStateMarker = function() {
if(this.stopInformationPanelSelectedMarker != undefined) {
// TODO: May need to restore the if statement
//if(this.stopInformationPanelSelectedMarker.domRef.className.match(COLOUR_MATCHER)[0] == 'grey') {
KZ.markerManager.alterMarkerState(this.stopInformationPanelSelectedMarker, 'normal-state', 6);
this.stopInformationPanelSelectedMarker = undefined;
//}
}
}
this.restoreHoverStateMarker = function() {
if(hoverStateMarker != undefined) {
if(this.stopInformationPanelSelectedMarker == undefined ||
(this.stopInformationPanelSelectedMarker != undefined && this.stopInformationPanelSelectedMarker.id != hoverStateMarker.id)) {
this.alterMarkerState(hoverStateMarker, 'normal-state', 6);
hoverStateMarker = undefined;
}
}
}
this.setMarkerState = function(marker, state) {
// Restore previously highlighted marker
this.restoreHoverStateMarker();
// save currently highlighted marker
hoverStateMarker = marker;
// highlight the marker
if(this.stopInformationPanelSelectedMarker == undefined ||
this.stopInformationPanelSelectedMarker.id != marker.id) {
this.alterMarkerState(marker, state, 10);
}
}
this.alterMarkerState = function(marker, state, zIndex) {
var markerAnchorPoint = this.getMarkerAnchorPoint(state);
var markerPoint = KZ.mapPanel.getMap().fromLatLngToDivPixel(marker.lat, marker.lng);
marker.domRef.style.display = 'none';
marker.domRef.style.top = markerPoint.y - markerAnchorPoint.y + 'px';
marker.domRef.style.left = markerPoint.x - markerAnchorPoint.x + 'px';
marker.domRef.className = marker.domRef.className.replace(MARKER_STATE_MATCHER, state);
marker.domRef.style.zIndex = zIndex;
marker.domRef.style.display = 'block';
}
this.hideSearchResultMarkers = function() {
Ext.each(searchResultsMarkerContainer.childNodes, function(resultMarker, index) {
resultMarker.style.display = 'none';
});
}
this.hideMarkers = function(markers){
Ext.each(markers, function(marker) {
marker.domRef.style.display = 'none';
});
}
this.clearSearchResultMarkers = function() {
if (searchResultsMarkerContainer.hasChildNodes()) {
while(searchResultsMarkerContainer.childNodes.length >= 1){
searchResultsMarkerContainer.removeChild(searchResultsMarkerContainer.firstChild); // nodes should be garbage collected when the store is next cleared.
}
}
this.updateBackgroundMarkers();
}
this.updateBackgroundMarkers = function() {
if(KZ.mapPanel.getMap().getZoom() >= KZ.Config.BG_MARKER_ZOOM_THRESHOLD) {
this.updateMarkers(this.backgroundMarkerHashMap.getValues(), undefined, true);
}
}
this.onResultsPanelExpand = function(gridPanel) {
this.hideSearchResultMarkers();
this.createMarkersFromStore(gridPanel.getStore());
this.updateBackgroundMarkers();
}
Ext.getCmp('stop-results-panel').on('expand', this.onResultsPanelExpand, this);
Ext.getCmp('place-results-panel').on('expand', this.onResultsPanelExpand, this);
Ext.StoreMgr.lookup('selectedCallingPatternResultsStore').on({
'load' : {
fn: this.createMarkersFromStore,
scope: this
}
});
}
Ext.ns('KZ');
/* By including this javascript file vs ViewportFixedWidth.js, you are specifying a
* full screen layout rather than a fixed width (dynamic height) layout.
*/
KZ.Viewport = function() {
var pageHeader = new Ext.Panel({
id: 'page-header-container',
region: 'north',
contentEl: 'page-header',
border:false,
bodyBorder:false
});
var travelMap = new Ext.Panel({
id: 'travel-app-viewport',
layout: 'border',
margins: '0 0 0 0',
hideMode: 'offsets',
hidden: true,
border:false,
bodyBorder:false,
region: 'center',
items: [KZ.leftColumnContainer, KZ.contentContainer]
});
this.extComponent = new Ext.Viewport({
layout: 'border',
margins: '0 0 0 0',
items: [pageHeader, travelMap]
});
this.showTravelMap = function() {
travelMap.show();
}
this.hideTravelMap = function() {
travelMap.hide();
}
this.scrollWindowToShowEntireTravelMapApp = function() { } // do not need to shift viewport for full screen layout
}
// Path to the blank image must point to a valid location on your server
Ext.BLANK_IMAGE_URL = '../../js-lib/ext/resources/images/default/s.gif';
Ext.ns('KZ');
Ext.onReady(function(){
KZ.historyManager = new KZ.HistoryManager();
if(location.hash == '') {
KZ.historyManager.initialize();
}
KZ.userStopsStore = new KZ.UserStopsStore();
KZ.InitialSearchPanel.show();
KZ.searchBoxPanel = new KZ.SearchBoxPanel();
KZ.searchResultsPanel = new KZ.SearchResultsPanel();
KZ.searchPanel = new Ext.Panel({
id: 'search-panel',
title: 'Search',
iconCls: 'bus-stop-search-icon',
layout:'vbox',
layoutConfig: { align : 'stretch', pack: 'start' },
region: 'center',
listeners: {
'beforecollapse': function() {
KZ.leftColumnContainer.setWidth(0);
Ext.get('left-column-container-xsplit').enableDisplayMode().hide();
Ext.getCmp('expand-search-button').show();
Ext.getCmp('expand-search-button').el.slideIn('t', {easing:'easeOut', duration:1.0});
KZ.viewport.extComponent.doLayout();
return false;
}
},
items: [KZ.searchBoxPanel.extComponent, KZ.searchResultsPanel.extComponent]
});
// TODO: The user stops panel can be enabled here.
//KZ.userStopsPanel = new KZ.UserStopsPanel(KZ.userStopsStore);
// TODO: This should be using a vbox with split layout. If there is no user stops panel, then it can use an even more simple layout
// http://www.extjs.com/forum/showthread.php?t=65982
KZ.leftColumnContainer = new Ext.Panel({
id:'left-column-container',
layout:'border',
split:true, // This shouldn't be true now we no longer have the bottom left panel. May re-enable however at a later date.
region:'west',
width: 240,
minSize: 0,
maxSize:500,
margins: '4 0 4 4',
cmargins: '4 4 0 4',
border:false,
bodyBorder:false,
//items: [KZ.searchPanel, KZ.userStopsPanel.extComponent]
items: [KZ.searchPanel]
});
KZ.markerManager = new KZ.MarkerManager();
KZ.tooltipManager = new KZ.TooltipManager();
KZ.mapPanel = new KZ.MapPanel();
KZ.stopInformationPanel = new KZ.StopInformationPanel(false, true, false, undefined, false, true);
KZ.contentContainer = new Ext.Container({
id:'content-container',
layout:'border',
split:true,
region: 'center',
margins: '4 4 4 0',
items: [KZ.mapPanel.extComponent, KZ.stopInformationPanel.extComponent]
});
// Ensure the stop info panel is given the appropriate height at all times
KZ.contentContainer.on('resize', function(newViewportWidth, newViewportHeight) { this.recalculateContentContainerComponentsHeight(); }, this);
KZ.stopInformationPanel.extComponent.on('beforeshow', function(stopInformationPanel) { this.recalculateContentContainerComponentsHeight(); }, this);
// Gives the map panel and stop info panel proportional heights, unless the browser window is small, in which case, we
// only show the stop info panel, and the map is all but collapsed.
this.recalculateContentContainerComponentsHeight = function() {
var contentContainerHeight = KZ.contentContainer.getHeight();
if(contentContainerHeight < 280) {
KZ.stopInformationPanel.extComponent.setHeight(contentContainerHeight - 1 - 5); // The height of the map panel minus the height of the divider
KZ.contentContainer.doLayout();
return;
} else {
KZ.stopInformationPanel.extComponent.setHeight(contentContainerHeight * 0.54);
KZ.contentContainer.doLayout();
return;
}
}
KZ.viewport = new KZ.Viewport();
KZ.mapPanel.initialise();
// Component intercommunication
KZ.mapPanel.extComponent.relayEvents(KZ.searchBoxPanel.extComponent, ["newSearchEvent"]);
KZ.mapPanel.extComponent.relayEvents(KZ.routeSearchResultsContainer.extComponent, ["newSearchEvent"]);
KZ.mapPanel.extComponent.relayEvents(KZ.searchResultsPanel.extComponent, ["newSearchBoundingBoxEvent", "newSearchPointEvent"]);
KZ.mapPanel.extComponent.relayEvents(Ext.getCmp('route-search-container'), ["newSearchBoundingBoxEvent", "newSearchPointEvent"]);
KZ.mapPanel.extComponent.relayEvents(KZ.stopInformationPanel.extComponent, ["newSearchPointEvent"]);
KZ.stopInformationPanel.extComponent.relayEvents(KZ.searchBoxPanel.extComponent, ["closeStopBoard"]);
KZ.stopInformationPanel.extComponent.relayEvents(KZ.searchResultsPanel.extComponent, ["closeStopBoard"]);
KZ.viewport.extComponent.relayEvents(KZ.InitialSearchPanel.extComponent, ["stopSelectedEvent"]);
KZ.viewport.extComponent.relayEvents(Ext.History, ["stopSelectedEvent"]);
/*KZ.viewport.extComponent.relayEvents(KZ.userStopsPanel.extComponent, ["stopSelectedEvent"]);*/
KZ.viewport.extComponent.relayEvents(KZ.mapPanel.extComponent, ["stopSelectedEvent"]);
KZ.viewport.extComponent.relayEvents(KZ.routeSearchResultsContainer.extComponent, ["stopSelectedEvent"]);
KZ.viewport.extComponent.relayEvents(KZ.searchResultsPanel.extComponent, ["stopSelectedEvent"]);
KZ.viewport.extComponent.relayEvents(KZ.stopInformationPanel.extComponent, ["stopBoardClosed"]);
KZ.viewport.extComponent.relayEvents(KZ.searchBoxPanel.extComponent, ["newSearchEvent"]);
KZ.viewport.extComponent.on({
'newSearchEvent' : {
fn: function() {
KZ.viewport.scrollWindowToShowEntireTravelMapApp();
}
}
});
KZ.viewport.extComponent.on({
'stopBoardClosed' : {
fn: function() {
KZ.historyManager.closeStopBoardPanel();
KZ.markerManager.restoreSelectedStateMarker();
}
}
});
KZ.viewport.extComponent.on({
'stopSelectedEvent' : {
fn: function(busStopRecord, stopBoard) {
if(KZ.stopInformationPanel.getCurrentBusStopRecord() != undefined &&
KZ.stopInformationPanel.getCurrentBusStopRecord().data.id == busStopRecord.data.id) {
return; // This stop is already selected
}
if(busStopRecord.domRef != undefined){
KZ.searchResultsPanel.decorateRecordWithAccessorFields(busStopRecord, busStopRecord.domRef);
} else {
var busStopId = busStopRecord.data.id;
busStopRecord = KZ.mapPanel.getMarkerRecord(busStopId);
if(busStopRecord.domRef == undefined){
KZ.markerManager.createBackgroundMarkers(busStopRecord.data);
busStopRecord = KZ.mapPanel.getMarkerRecord(busStopId, new Array('backgroundMarkerHashMap'));
}
}
KZ.userStopsStore.addRecentStop(busStopRecord);
KZ.markerManager.restoreSelectedStateMarker();
KZ.markerManager.setMarkerState(busStopRecord, 'selected-state');
KZ.markerManager.stopInformationPanelSelectedMarker = busStopRecord;
var busStopId = busStopRecord.get("id");
KZ.historyManager.addDepartureBoard(busStopId, busStopRecord.domRef.src);
KZ.viewport.scrollWindowToShowEntireTravelMapApp();
KZ.stopInformationPanel.show(busStopRecord, stopBoard);
_gaq.push(['_trackEvent', 'Stop Selected', busStopId, busStopRecord.data.name]);
},
scope: KZ.stopInformationPanel
}
});
// TODO: might be better to use an image sprite. This doesn't appear to be making a difference
// KZ.util.preloadImages(['/img/map-marker-sprite.png']);
// Restoring previous departure board information (bookmark)
KZ.historyManager.restoreBookmark(location.hash);
}); // /onReady