Advertisement
Guest User

Untitled

a guest
Sep 19th, 2018
94
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Java 91.47 KB | None | 0 0
  1. package com.yotta.horizon.mapping.presenters;
  2.  
  3. import com.google.gwt.core.client.JavaScriptObject;
  4. import com.yotta.horizon.base.Horizon;
  5. import com.yotta.horizon.mapbuilder.shared.ItemStyleRule;
  6. import com.yotta.horizon.mapbuilder.shared.RenderSource;
  7. import com.yotta.horizon.mapping.shared.SectionItem;
  8. import com.yotta.horizon.mapping.shared.SectionItemImagesMap;
  9. import com.yotta.horizon.mapping.shared.events.SectionItemReadyEvent;
  10. import com.yotta.horizon.shared.gmaps.LatLng;
  11. import com.yotta.horizon.shared.gmaps.MVCArray;
  12. import com.yotta.horizon.shared.gmaps.Map;
  13. import com.yotta.horizon.shared.gmaps.PolyLineOptions;
  14. import com.yotta.horizon.shared.resources.HorizonResources;
  15. import com.yotta.horizon.shared.resources.config.HorizonConfigFlags;
  16. import com.yotta.horizon.shared.resources.config.HorizonConfigResource;
  17.  
  18. /**
  19.  * Represents a chainage group which boxes section items into a more friendly ui
  20.  * @author cmcnicholas
  21.  */
  22. class SectionItemPresenterChainageGroup extends JavaScriptObject {
  23.  
  24.     private static final PolyLineOptions ITEM_ACTIVE = PolyLineOptions.create(false, 2,
  25.         HorizonConfigResource.INSTANCE.chainageGroupColourActive(), 1);
  26.     private static final PolyLineOptions ELBOW_ACTIVE = PolyLineOptions.create(false, 2,
  27.         HorizonConfigResource.INSTANCE.chainageGroupColourActive(), 1);
  28.     private static final PolyLineOptions ITEM_DEFAULT = PolyLineOptions.create(false, 2,
  29.         HorizonConfigResource.INSTANCE.chainageGroupColour(), 1);
  30.     private static final PolyLineOptions ELBOW_DEFAULT = PolyLineOptions.create(false, 2,
  31.         HorizonConfigResource.INSTANCE.chainageGroupColour(), 1);
  32.     private static final JavaScriptObject CONSTRUCTOR = SectionItemPresenterChainageGroup.setup();
  33.  
  34.     protected SectionItemPresenterChainageGroup() {}
  35.  
  36.     /**
  37.      * Creates a new chainage group instance
  38.      * @param map the map to create the chainage group on
  39.      * @param minLevel the min level for the element to be active on
  40.      * @param maxLevel the max level for the element to be active on
  41.      * @param lowerChainage the lower chainage value of the group
  42.      * @param upperChainage the upper chainage value of the group
  43.      * @param chainagePath the path that the group controls the items of
  44.      * @param isLeft whether to display on the left of the road or right
  45.      * @return a chainage group instance
  46.      */
  47.     public static final native SectionItemPresenterChainageGroup create(Map map, int minLevel, int maxLevel,
  48.         int lowerChainage, int upperChainage, MVCArray<LatLng> chainagePath, boolean isLeft) /*-{
  49.         var SectionItemPresenterChainageGroup = @com.yotta.horizon.mapping.presenters.SectionItemPresenterChainageGroup::CONSTRUCTOR;
  50.         return new SectionItemPresenterChainageGroup(map, minLevel, maxLevel,
  51.                 lowerChainage, upperChainage, chainagePath, isLeft);
  52.     }-*/;
  53.  
  54.     /**
  55.      * Gets the polyline item active options
  56.      * @return a set of polyline options
  57.      */
  58.     public static final PolyLineOptions getItemActivePolyLineOptions() {
  59.         return ITEM_ACTIVE;
  60.     }
  61.  
  62.     /**
  63.      * Gets the polyline elbow active options
  64.      * @return a set of polyline options
  65.      */
  66.     public static final PolyLineOptions getElbowActivePolyLineOptions() {
  67.         return ELBOW_ACTIVE;
  68.     }
  69.  
  70.     /**
  71.      * Gets the polyline item default options
  72.      * @return a set of polyline options
  73.      */
  74.     public static final PolyLineOptions getItemDefaultPolyLineOptions() {
  75.         return ITEM_DEFAULT;
  76.     }
  77.  
  78.     /**
  79.      * Gets the polyline elbow default options
  80.      * @return a set of polyline options
  81.      */
  82.     public static final PolyLineOptions getElbowDefaultPolyLineOptions() {
  83.         return ELBOW_DEFAULT;
  84.     }
  85.  
  86.     /**
  87.      * Gets the upper chainage value this box controls
  88.      * @return a chainage value
  89.      */
  90.     public final native int getUpperChainage() /*-{
  91.         return this.getUpperChainage();
  92.     }-*/;
  93.  
  94.     /**
  95.      * Gets the lower chainage value this box controls
  96.      * @return a chainage value
  97.      */
  98.     public final native int getLowerChainage() /*-{
  99.         return this.getLowerChainage();
  100.     }-*/;
  101.  
  102.     /**
  103.      * Disposes of the chainage group and releases resources
  104.      */
  105.     public final native void dispose() /*-{
  106.         this.dispose();
  107.     }-*/;
  108.  
  109.     /**
  110.      * Method to add a section item to the control of the chainage group, the
  111.      * item will not be immediately drawn in the box
  112.      * @param item the item to add
  113.      */
  114.     public final native void addItem(SectionItem item) /*-{
  115.         this.addItem(item);
  116.     }-*/;
  117.  
  118.     /**
  119.      * Method to hover an item MANUALLY!!!
  120.      * @param item the item to hover
  121.      * @param over whether to hover over or out
  122.      */
  123.     public final native void manualHoverItem(SectionItem item, boolean over) /*-{
  124.         if (this.getBoxes() == null) {
  125.             return;
  126.         }
  127.         var key = @com.yotta.horizon.mapping.shared.SectionItemMap::getKey(Lcom/yotta/horizon/mapping/shared/SectionItem;)(item);
  128.         for (var i = 0, s = this.getBoxes().length; i < s; i++) {
  129.             var box = this.getBoxes()[i];
  130.             for (var j = 0, size = this.getBoxes()[i].getItems().length; j < size; j++) {
  131.                 var currentItem = this.getBoxes()[i].getItems()[j];
  132.                 var currentKey = @com.yotta.horizon.mapping.shared.SectionItemMap::getKey(Lcom/yotta/horizon/mapping/shared/SectionItem;)(currentItem);
  133.                 if (key == currentKey) {
  134.                     if (this.getBoxes()[i].getPopout().style.display != 'none'
  135.                             && this.getBoxes()[i].getBoxDrawer() != null
  136.                             && this.getBoxes()[i].getBoxDrawer().isVisible()) {
  137.                         $wnd.google.maps.event.trigger(this.getBoxes()[i]
  138.                                 .getBoxDrawer().getBoxes()[j].getElement(),
  139.                                 over ? 'mouseover' : 'mouseout', {
  140.                                     manual : true,
  141.                                     item : currentItem
  142.                                 });
  143.                     } else {
  144.                         // Pass edge as fix for editable polylines which cause issues if an empty object is passed
  145.                         $wnd.google.maps.event.trigger(box.getElement(),
  146.                                 over ? 'mouseover' : 'mouseout', {
  147.                                     manual : true,
  148.                                     item : currentItem
  149.                                 });
  150.                     }
  151.                 }
  152.             }
  153.         }
  154.     }-*/;
  155.  
  156.     /**
  157.      * Method to remove a section item from the chainage group, the item will
  158.      * not be immediately removed from the box
  159.      * @param item the item to remove
  160.      */
  161.     public final native void removeItem(SectionItem item) /*-{
  162.         this.removeItem(item);
  163.     }-*/;
  164.  
  165.     /**
  166.      * Method to clear all the items from the chainage group
  167.      */
  168.     public final native void clearItems() /*-{
  169.         this.clearItems();
  170.     }-*/;
  171.  
  172.     private static final String generateImage(String text, String colourHex) {
  173.         return SectionItemImagesMap.generateImage(text, colourHex);
  174.     }
  175.  
  176.     /**
  177.      * Gets the image url from the map
  178.      * @param item Section item for which to get the image
  179.      * @return url string
  180.      */
  181.     private static final String getImageUrl(SectionItem item) {
  182.         return SectionItemImagesMap.INSTANCE.get(item);
  183.     }
  184.  
  185.     /**
  186.      * Gets the cropped image url from the map (Used to display the centre of
  187.      * diagrams)
  188.      * @param item Section item for which to get the cropped image
  189.      * @return url string
  190.      */
  191.     private static final String getCropUrl(SectionItem item) {
  192.         return SectionItemImagesMap.INSTANCE.getCrop(item);
  193.     }
  194.  
  195.     /**
  196.      * Returns cropped image url if it's present, otherwise returns normal image
  197.      * url
  198.      * @param item Section item for which to get the image
  199.      * @return url string
  200.      */
  201.     private static final String getExistingUrl(SectionItem item) {
  202.         if(SectionItemImagesMap.INSTANCE.hasCropImage(item)) {
  203.             return getCropUrl(item);
  204.         }
  205.         return getImageUrl(item);
  206.     }
  207.  
  208.     /**
  209.      * Sets the image url
  210.      * @param item Section item for which to set the image url
  211.      * @param url string url of the image
  212.      */
  213.     private static final void setImageUrl(SectionItem item, String url) {
  214.         SectionItemImagesMap.INSTANCE.set(item, url);
  215.     }
  216.  
  217.     /**
  218.      * Sets the cropped image url
  219.      * @param item Section item for which to set the cropped image url
  220.      * @param url string url of the cropped image
  221.      */
  222.     private static final void setCropUrl(SectionItem item, String url) {
  223.         SectionItemImagesMap.INSTANCE.setCrop(item, url);
  224.     }
  225.  
  226.     /**
  227.      * Method to update the chainage group ui and draw active items
  228.      * @param forceUpdate method to force an update even if we think internal
  229.      *        state has not changed
  230.      */
  231.     public final native void update(boolean forceUpdate) /*-{
  232.         if (forceUpdate) {
  233.             this.setDoUpdate(true);
  234.         }
  235.         this.update();
  236.     }-*/;
  237.  
  238.     /**
  239.      * Gets the chainage group style name
  240.      * @return a style name
  241.      */
  242.     private static final String getChainageGroupStyleName() {
  243.         return HorizonResources.INSTANCE.horizonCss().chainageGroup();
  244.     }
  245.  
  246.     /**
  247.      * Gets the chainage group horizontal style name
  248.      * @return a style name
  249.      */
  250.     private static final String getChainageGroupHorizontalStyleName() {
  251.         return HorizonResources.INSTANCE.horizonCss().chainageGroupHorizontal();
  252.     }
  253.  
  254.     /**
  255.      * Gets the chainage group item style name
  256.      * @return a style name
  257.      */
  258.     private static final String getChainageGroupItemStyleName() {
  259.         return HorizonResources.INSTANCE.horizonCss().chainageGroupItem();
  260.     }
  261.  
  262.     /**
  263.      * Gets the chainage group item no diagram style name
  264.      * @return a style name
  265.      */
  266.     private static final String getChainageGroupItemNoDiagramStyleName() {
  267.         return HorizonResources.INSTANCE.horizonCss().chainageGroupItemNoDiagram();
  268.     }
  269.  
  270.     /**
  271.      * Gets the chainage group item popout style name
  272.      * @return a style name
  273.      */
  274.     private static final String getChainageGroupItemPopoutStyleName() {
  275.         return HorizonResources.INSTANCE.horizonCss().chainageGroupItemPopout();
  276.     }
  277.  
  278.     /**
  279.      * Gets the chainage group item active style name
  280.      * @return a style name
  281.      */
  282.     private static final String getChainageGroupItemActiveStyleName() {
  283.         return HorizonResources.INSTANCE.horizonCss().chainageGroupItemActive();
  284.     }
  285.  
  286.     /**
  287.      * Gets the chainage group item hovered style name
  288.      * @return a style name
  289.      */
  290.     private static final String getChainageGroupItemHoveredStyleName() {
  291.         return HorizonResources.INSTANCE.horizonCss().chainageGroupItemHovered();
  292.     }
  293.  
  294.     /**
  295.      * Gets the lollipop item counter bubble style name
  296.      * @return a style name
  297.      */
  298.     private static final String getLollipopBubbleStyleName() {
  299.         return HorizonResources.INSTANCE.horizonCss().lollipopBubble();
  300.     }
  301.  
  302.     /**
  303.      * Gets the lollipop item counter bubble style name for firefox
  304.      * @return a style name
  305.      */
  306.     private static final String getLollipopBubbleFFStyleName() {
  307.         return HorizonResources.INSTANCE.horizonCss().lollipopBubbleBottom();
  308.     }
  309.  
  310.     /**
  311.      * Gets the chainage group item small style name
  312.      * @return a style name
  313.      */
  314.     private static final String getChainageGroupItemSmallStyleName() {
  315.         return HorizonResources.INSTANCE.horizonCss().chainageGroupItemSmall();
  316.     }
  317.  
  318.     private static final void fireItemReady(int renderSourceId, int itemId, boolean raisedByDrawer) {
  319.         Horizon.getApp().getEventBus().fireEvent(new SectionItemReadyEvent(renderSourceId, itemId, raisedByDrawer));
  320.     }
  321.  
  322.     /**
  323.      * Gets the acronym of a style set name for the given section item
  324.      * @param item SectionItem for which to get the style name acronym
  325.      * @return acronym string
  326.      */
  327.     private static final String getAcronymName(SectionItem item) {
  328.         final RenderSource rg = Horizon.getApp().getUser().getRenderGroups().get(item.getRenderGroupId());
  329.         if(rg != null && rg.getHiLodItemStyleRuleSet() != null) {
  330.             for(final ItemStyleRule isr : rg.getHiLodItemStyleRuleSet().getItemStyleRules().values()) {
  331.                 if(isr.getItemStyleRuleId() == item.getGeometry().getStyleRuleId()) {
  332.                     if(isr.getShortCode() != null) {
  333.                         return isr.getShortCode();
  334.                     }
  335.                     final StringBuilder labelBuilder = new StringBuilder(5);
  336.                     for(int i = 0, j = 0; i < isr.getDescription().length() && j < 5; i++) {
  337.                         if(Character.isUpperCase(isr.getDescription().charAt(i))) {
  338.                             labelBuilder.append(isr.getDescription().charAt(i));
  339.                             j++;
  340.                         }
  341.                     }
  342.                     isr.setShortCode(labelBuilder.toString());
  343.                     return labelBuilder.toString();
  344.                 }
  345.             }
  346.         }
  347.         return item.getGeometry().getFeatureCode();
  348.     }
  349.  
  350.     /**
  351.      * Creates the constructor for a chainage group
  352.      * @return a javascript function
  353.      */
  354.     private static final native JavaScriptObject setup() /*-{
  355.         // Constructor for direction enumeration
  356.         var ChainageGroupDirection = function() {};
  357.  
  358.         ChainageGroupDirection.EAST = new ChainageGroupDirection();
  359.         ChainageGroupDirection.SOUTH = new ChainageGroupDirection();
  360.         ChainageGroupDirection.WEST = new ChainageGroupDirection();
  361.         ChainageGroupDirection.NORTH = new ChainageGroupDirection();
  362.  
  363.         // Method to calculate which direction to use given the orientation of the road and the heading
  364.         ChainageGroupDirection.calculate = function(isLeft, heading) {
  365.             if(heading < 0) {
  366.                 throw "heading cannot be less than zero!";
  367.             }
  368.             if(heading >= 360) {
  369.                 throw "heading cannot be greater than or equal to 360!";
  370.             }
  371.  
  372.             if(heading >= 315 || heading < 45) {
  373.                 return isLeft ? ChainageGroupDirection.WEST : ChainageGroupDirection.EAST;
  374.             } else if(heading >= 45 && heading < 135) {
  375.                 return isLeft ? ChainageGroupDirection.NORTH : ChainageGroupDirection.SOUTH;
  376.             } else if(heading >= 135 && heading < 225) {
  377.                 return isLeft ? ChainageGroupDirection.EAST : ChainageGroupDirection.WEST;
  378.             } else if(heading >= 225 && heading < 315) {
  379.                 return isLeft ? ChainageGroupDirection.SOUTH : ChainageGroupDirection.NORTH;
  380.             } else {
  381.                 throw "Heading value not handled";
  382.             }
  383.         };
  384.  
  385.         // Constructor for anchor enumeration
  386.         var ChainageGroupAnchor = function() {};
  387.  
  388.         ChainageGroupAnchor.TOP_LEFT = new ChainageGroupAnchor();
  389.         ChainageGroupAnchor.TOP_RIGHT = new ChainageGroupAnchor();
  390.         ChainageGroupAnchor.BOTTOM_LEFT = new ChainageGroupAnchor();
  391.         ChainageGroupAnchor.BOTTOM_RIGHT = new ChainageGroupAnchor();
  392.  
  393.         // Method to calculate which anchor to use given the orientation of the road and the heading
  394.         ChainageGroupAnchor.calculate = function(isLeft, heading) {
  395.             if(heading < 0) {
  396.                 throw "heading cannot be less than zero!";
  397.             }
  398.             if(heading >= 360) {
  399.                 throw "heading cannot be greater than or equal to 360!";
  400.             }
  401.  
  402.             if(heading >= 0 && heading < 90) {
  403.                 return isLeft ? ChainageGroupAnchor.BOTTOM_RIGHT : ChainageGroupAnchor.TOP_LEFT;
  404.             } else if(heading >= 90 && heading < 180) {
  405.                 return isLeft ? ChainageGroupAnchor.BOTTOM_LEFT : ChainageGroupAnchor.TOP_RIGHT;
  406.             } else if(heading >= 180 && heading < 270) {
  407.                 return isLeft ? ChainageGroupAnchor.TOP_LEFT : ChainageGroupAnchor.BOTTOM_RIGHT;
  408.             } else if(heading >= 270 && heading < 360) {
  409.                 return isLeft ? ChainageGroupAnchor.TOP_RIGHT : ChainageGroupAnchor.BOTTOM_LEFT;
  410.             } else {
  411.                 throw "Heading value not handled";
  412.             }
  413.         };
  414.  
  415.         // Constants
  416.         var PIXEL_OFFSET = 100;
  417.         var PIXEL_OFFSET_ELBOW = 50;
  418.         var MAX_ITEMS_PER_ROW = 8;
  419.         var POLYLINE_ELBOW_DEFAULT = @com.yotta.horizon.mapping.presenters.SectionItemPresenterChainageGroup::ELBOW_DEFAULT;
  420.         var POLYLINE_ELBOW_ACTIVE = @com.yotta.horizon.mapping.presenters.SectionItemPresenterChainageGroup::ELBOW_ACTIVE;
  421.         var POLYLINE_ITEM_DEFAULT = @com.yotta.horizon.mapping.presenters.SectionItemPresenterChainageGroup::ITEM_DEFAULT;
  422.         var POLYLINE_ITEM_ACTIVE = @com.yotta.horizon.mapping.presenters.SectionItemPresenterChainageGroup::ITEM_ACTIVE;
  423.  
  424.         // Retains a pointer to the last box selected
  425.         var lastBoxSelected;
  426.  
  427.         // Retains a pointer to the last box draw open
  428.         var lastBoxDrawerSelected;
  429.  
  430.         // Retains a pointer to the throttle of hover events
  431.         var itemHoverThrottle;
  432.  
  433.  
  434.         // CONSTRUCTOR ChainageGroupBox
  435.         // Represents the window boxes that display signs and diagrams
  436.         var ChainageGroupBox = function(map, elbowPolyline, elbowLatLng, sectionItem, key, isSmall, direction, sectionHeading) {
  437.             var me = this; // Reference to self
  438.  
  439.             // Set variables
  440.             this.color_ = null;
  441.             this.map_ = map;
  442.             this.key_ = key;
  443.             this.element_ = $doc.createElement('div');
  444.             this.popout_ = $doc.createElement('div');
  445.             this.itemCounterBubble_ = $doc.createElement('div');
  446.             this.popoutCounterBubble_ = $doc.createElement('div');
  447.             this.elbowPolyline_ = elbowPolyline;
  448.             this.selected_ = false;
  449.             this.elementMouseOverListener_ = null;
  450.             this.elementMouseOutListener_ = null;
  451.             this.popoutMouseOutListener_ = null;
  452.             this.popoutBubbleMouseOutListener_ = null;
  453.             this.popoutClickListener_ = null;
  454.             this.sectionItemMouseOutListeners_ = [];
  455.             this.sectionItemMouseOverListeners_ = [];
  456.             this.sectionItemSelectedListener_ = null;
  457.             this.direction_ = direction;
  458.             this.sectionHeading_ = sectionHeading;
  459.  
  460.             this.sectionItems_ = [sectionItem];
  461.             this.polylines_ = [];
  462.             this.boxDrawer_ = null;
  463.             this.isSmall_ = isSmall;
  464.  
  465.             // Get image urls
  466.             var hasDiagram = @com.yotta.horizon.mapping.shared.SectionItemUtils::getSectionItemDiagramId(Lcom/yotta/horizon/mapping/shared/SectionItem;)(sectionItem) != null;
  467.             var cropUrl = @com.yotta.horizon.mapping.shared.SectionItemUtils::getSectionItemImageUrlCropped(Lcom/yotta/horizon/mapping/shared/SectionItem;Z)(sectionItem, isSmall);
  468.             var imageUrl = @com.yotta.horizon.mapping.shared.SectionItemUtils::getSectionItemImageUrl(Lcom/yotta/horizon/mapping/shared/SectionItem;Z)(sectionItem, isSmall);
  469.             var color = @com.yotta.horizon.mapping.shared.SectionItemUtils::getSectionItemColor(Lcom/yotta/horizon/mapping/shared/SectionItem;)(sectionItem);
  470.             this.setColor(color);
  471.  
  472.             // Setup element
  473.             var element = this.getElement();
  474.             element.style.width = isSmall ? '16px' : '32px';
  475.             element.style.height = isSmall ? '16px' : '32px';
  476.             element.style.backgroundColor = color;
  477.             element.style.position = 'relative';
  478.             $wnd.jQuery(element).addClass(@com.yotta.horizon.mapping.presenters.SectionItemPresenterChainageGroup::getChainageGroupItemStyleName());
  479.             if(isSmall) {
  480.                 $wnd.jQuery(element).addClass(@com.yotta.horizon.mapping.presenters.SectionItemPresenterChainageGroup::getChainageGroupItemSmallStyleName());
  481.             }
  482.  
  483.             // Setup item counter bubble for the element
  484.             var itemCounterBubble = this.getBubble();
  485.             $wnd.jQuery(itemCounterBubble).addClass(@com.yotta.horizon.mapping.presenters.SectionItemPresenterChainageGroup::getLollipopBubbleStyleName());
  486.             itemCounterBubble.style.fontWeight = 'bolder';
  487.             if(isSmall) {
  488.                 itemCounterBubble.style.right = '-8px';
  489.                 itemCounterBubble.style.bottom = '-4px';
  490.  
  491.             } else {
  492.                 $wnd.jQuery(itemCounterBubble).addClass(@com.yotta.horizon.mapping.presenters.SectionItemPresenterChainageGroup::getLollipopBubbleFFStyleName());
  493.             }
  494.             element.appendChild(itemCounterBubble);
  495.  
  496.             // Setup item counter bubble for the popout
  497.             var popoutBubble = this.getPopoutBubble();
  498.             $wnd.jQuery(popoutBubble).addClass(@com.yotta.horizon.mapping.presenters.SectionItemPresenterChainageGroup::getLollipopBubbleStyleName());
  499.             $wnd.jQuery(popoutBubble).addClass(@com.yotta.horizon.mapping.presenters.SectionItemPresenterChainageGroup::getLollipopBubbleFFStyleName());
  500.             popoutBubble.style.fontWeight = 'bolder';
  501.             this.setBubbleVisible(false);
  502.  
  503.             // Setup popout
  504.             var popout = this.getPopout();
  505.             popout.style.width = isSmall ? '16px' : '32px';
  506.             popout.style.height = isSmall ? '16px' : '32px';
  507.             popout.style.backgroundColor = color;
  508.             popout.style.position = 'relative';
  509.             popout.style.borderColor = color;
  510.             $wnd.jQuery(popout).addClass(@com.yotta.horizon.mapping.presenters.SectionItemPresenterChainageGroup::getChainageGroupItemPopoutStyleName());
  511.             popout.appendChild(popoutBubble);
  512.             element.appendChild(popout);
  513.  
  514.             // Setup polyline to elbow if enabled
  515.             if(@com.yotta.horizon.mapping.presenters.SectionItemPresenterChainageGroup::isElbowEnabled()()) {
  516.                 var polyline = new $wnd.google.maps.Polyline($wnd.jQuery.extend({
  517.                 map: map,
  518.                 path: [
  519.                     this.getFirstItem().setIcon ? // Marker or polyline/polygon
  520.                         this.getFirstItem().getPosition() :
  521.                         @com.yotta.horizon.shared.gmaps.MapMath::getNearestLatLngOnPath(Lcom/yotta/horizon/shared/gmaps/MVCArray;Lcom/yotta/horizon/shared/gmaps/LatLng;)(this.getFirstItem().getPath(), elbowLatLng),
  522.                     elbowLatLng
  523.                 ]
  524.                 }, POLYLINE_ITEM_DEFAULT));
  525.                 this.polylines_.push(polyline);
  526.             }
  527.  
  528.             // Preload image if there is one
  529.             var mapImage = @com.yotta.horizon.mapping.presenters.SectionItemPresenterChainageGroup::getExistingUrl(Lcom/yotta/horizon/mapping/shared/SectionItem;)(this.getFirstItem());
  530.             if(mapImage != null) {
  531.                 element.style.background = color + ' url("'+ mapImage +'") no-repeat center center';
  532.                 element.style.backgroundSize = isSmall ? "16px" : "32px";
  533.             } else if(hasDiagram) {
  534.                 var imageElement = new $wnd.Image();
  535.                 imageElement.style.visibility = 'hidden';
  536.                 imageElement.style.width = '1px';
  537.                 imageElement.style.height = '1px';
  538.                 imageElement.onload = function() {
  539.                     element.style.background = color + ' url("' + cropUrl + '") no-repeat center center';
  540.                     element.removeChild(imageElement);
  541.                     imageElement = null;
  542.                 };
  543.                 @com.yotta.horizon.mapping.presenters.SectionItemPresenterChainageGroup::setImageUrl(Lcom/yotta/horizon/mapping/shared/SectionItem;Ljava/lang/String;)(me.getFirstItem(), imageUrl);
  544.                 @com.yotta.horizon.mapping.presenters.SectionItemPresenterChainageGroup::setCropUrl(Lcom/yotta/horizon/mapping/shared/SectionItem;Ljava/lang/String;)(me.getFirstItem(), cropUrl);
  545.                 imageElement.src = cropUrl;
  546.                 element.appendChild(imageElement);
  547.             } else {
  548.                 var dimension = isSmall ? 16 : 32;
  549.                 var imageText = @com.yotta.horizon.mapping.presenters.SectionItemPresenterChainageGroup::getAcronymName(Lcom/yotta/horizon/mapping/shared/SectionItem;)(this.getFirstItem());
  550.                 var dataUrl = @com.yotta.horizon.mapping.presenters.SectionItemPresenterChainageGroup::generateImage(Ljava/lang/String;Ljava/lang/String;)(imageText, color);
  551.                 var imageElement = new $wnd.Image();
  552.                 imageElement.style.width = dimension + "px";
  553.                 imageElement.style.height = dimension + "px";
  554.                 imageElement.onload = function() {
  555.                     element.style.background = color + ' url("'+ dataUrl +'") no-repeat center center';
  556.                     element.style.backgroundSize = isSmall ? "16px" : "32px";
  557.                     element.removeChild(imageElement);
  558.                     imageElement = null;
  559.                 };
  560.                 @com.yotta.horizon.mapping.presenters.SectionItemPresenterChainageGroup::setImageUrl(Lcom/yotta/horizon/mapping/shared/SectionItem;Ljava/lang/String;)(this.getFirstItem(), dataUrl);
  561.                 imageElement.src = dataUrl;
  562.                 element.appendChild(imageElement);
  563.             }
  564.  
  565.             // Add listener for mouse over to highlight the path to the item
  566.             this.elementMouseOverListener_ = $wnd.google.maps.event.addDomListener(element, 'mouseover', function(e) {
  567.                 if(me.getItems().length > 1 && me.getBoxDrawer().isVisible()) {
  568.                     if(me.getPopout().style.display != 'none') {
  569.                         return;
  570.                     } else if(!me.getSelected()) {
  571.                         me.getBoxDrawer().hide();
  572.                     }
  573.                 }
  574.                 // Highlight paths if enabled
  575.                 if(@com.yotta.horizon.mapping.presenters.SectionItemPresenterChainageGroup::isElbowEnabled()()) {
  576.                     if(!e.item) {
  577.                         for(var i = 0, size = me.getItems().length; i < size; i++) {
  578.                             me.getPolylines()[i].setOptions(POLYLINE_ITEM_ACTIVE);
  579.                         }
  580.                         me.getElbow().setOptions(POLYLINE_ELBOW_ACTIVE);
  581.                     } else {
  582.                         for(var i = 0, size = me.getItems().length; i < size; i++) {
  583.                             if(e.item == me.getItems()[i]) {
  584.                                 me.getPolylines()[i].setOptions(POLYLINE_ITEM_ACTIVE);
  585.                                 break;
  586.                             }
  587.                         }
  588.                         me.getElbow().setOptions(POLYLINE_ELBOW_ACTIVE);
  589.                     }
  590.                 }
  591.                 // Add hovered class
  592.                 $wnd.jQuery(me.getElement()).addClass(@com.yotta.horizon.mapping.presenters.SectionItemPresenterChainageGroup::getChainageGroupItemHoveredStyleName());
  593.  
  594.                 // Highlight the items
  595.                 if(!e.cancelItemMouseOver && !e.item) {
  596.                     // Pass edge as fix for editable polylines which cause issues if an empty object is passed
  597.                     for(var i = 0, size = me.getItems().length; i < size; i++) {
  598.                         $wnd.google.maps.event.trigger(me.getItems()[i], 'mouseover', { edge: 0, notSimpleEvent: true });
  599.                     }
  600.                 } else if(e.manual && e.item) {
  601.                     $wnd.google.maps.event.trigger(e.item, 'mouseover', {edge: 0, notSimpleEvent: true});
  602.                 } else if(e.cancelItemMouseOver && e.item && me.getItems().length > 1) {
  603.                     var selected = false;
  604.                     for(var i = 0, s = me.getBoxDrawer().getItems().length; i < s; i++) {
  605.                         if(me.getBoxDrawer().getBoxes()[i].getSelected()) {
  606.                             selected = true;
  607.                             break;
  608.                         }
  609.                     }
  610.                     if(selected) {
  611.                         $wnd.google.maps.event.trigger(me.getPopout(), 'click', { zoom: true });
  612.                     }
  613.                 }
  614.  
  615.                 // Load background image from the map
  616.                 var jqPopout = $wnd.jQuery(popout);
  617.                 var mapImage = @com.yotta.horizon.mapping.presenters.SectionItemPresenterChainageGroup::getImageUrl(Lcom/yotta/horizon/mapping/shared/SectionItem;)(me.getFirstItem());
  618.                 if(mapImage != null) {
  619.                     jqPopout.css({
  620.                         backgroundImage: 'url("'+ mapImage +'")',
  621.                         backgroundSize: me.isSmall() ? "32px" : "64px",
  622.                         backgroundRepeat: 'no-repeat'
  623.                     });
  624.                 }
  625.  
  626.                 // Show popout
  627.                 jqPopout.stop(true).show().animate({
  628.                     width: isSmall ? '32px' : '64px',
  629.                     height: isSmall ? '32px' : '64px',
  630.                     top: isSmall ? '-8px' : '-16px',
  631.                     left: isSmall ? '-8px' : '-16px',
  632.                     opacity: 1
  633.                 }, 400, 'easeOutExpo').css('overflow', 'visible');
  634.  
  635.                 // Trigger hover
  636.                 if(itemHoverThrottle) {
  637.                     clearTimeout(itemHoverThrottle);
  638.                     itemHoverThrottle = null;
  639.                 }
  640.                 if(!e.manual && me.getItems().length == 1) {
  641.                     itemHoverThrottle = setTimeout(function() {
  642.                         if(me.getFirstItem()) {
  643.                         $wnd.google.maps.event.trigger(me.getFirstItem(), 'hover', { over: true });
  644.                         }
  645.                     }, 50);
  646.                 }
  647.             });
  648.  
  649.             // Add listener for mouse out to unhighlight the path to the item
  650.             this.elementMouseOutListener_ = $wnd.google.maps.event.addDomListener(element, 'mouseout', function(e) {
  651.                 // Dont animate if selected
  652.                 if(me.getSelected()) {
  653.                     return;
  654.                 }
  655.  
  656.                 // Reset polyline if enabled
  657.                 if(@com.yotta.horizon.mapping.presenters.SectionItemPresenterChainageGroup::isElbowEnabled()()) {
  658.                     for(var i = 0, size = me.getPolylines().length; i < size; i++) {
  659.                         me.getPolylines()[i].setOptions(POLYLINE_ITEM_DEFAULT);
  660.                     }
  661.                     // Only unhighlight the elbow if it is not the same reference elbow as the current item
  662.                     if(!lastBoxSelected || lastBoxSelected.getElbow() != me.getElbow()) {
  663.                         me.getElbow().setOptions(POLYLINE_ELBOW_DEFAULT);
  664.                     }
  665.                 }
  666.  
  667.                 if(!e.cancelItemMouseOut && !e.item) {
  668.                     // Pass edge as fix for editable polylines which cause issues if an empty object is passed
  669.                     for(var i = 0, size = me.getItems().length; i < size; i++) {
  670.                         $wnd.google.maps.event.trigger(me.getItems()[i], 'mouseout', { edge: 0, notSimpleEvent: true });
  671.                     }
  672.                 } else if(e.manual && e.item) {
  673.                     $wnd.google.maps.event.trigger(e.item, 'mouseout', { edge: 0 });
  674.                 }
  675.  
  676.                 // Remove hovered class
  677.                 $wnd.jQuery(me.getElement()).removeClass(@com.yotta.horizon.mapping.presenters.SectionItemPresenterChainageGroup::getChainageGroupItemHoveredStyleName());
  678.  
  679.                 // Trigger hover
  680.                 if(itemHoverThrottle) {
  681.                     clearTimeout(itemHoverThrottle);
  682.                     itemHoverThrottle = null;
  683.                 }
  684.                 if(!e.manual) {
  685.                     itemHoverThrottle = setTimeout(function() {
  686.                         if(me.getItems()) {
  687.                         for(var i = 0, size = me.getItems().length; i < size; i++) {
  688.                             $wnd.google.maps.event.trigger(me.getItems()[i], 'hover', { over: false });
  689.                         }
  690.                         }
  691.                     }, 50);
  692.                 }
  693.             });
  694.  
  695.             // Add listener for popout mouseout to remove popout
  696.             this.popoutMouseOutListener_ = $wnd.google.maps.event.addDomListener(popout, 'mouseout', function(e) {
  697.                 // Dont animate if selected
  698.                 if(me.getSelected()) {
  699.                     return;
  700.                 }
  701.                 var jqPopout = $wnd.jQuery(popout);
  702.                 if(!e.stopAnimation) {
  703.                     jqPopout.animate({
  704.                         width: isSmall ? '16px' : '32px',
  705.                         height: isSmall ? '16px' : '32px',
  706.                         top: '0px',
  707.                         left: '0px',
  708.                         opacity: 'initial'
  709.                     }, 200, 'easeOutExpo',
  710.                         function() {
  711.                             jqPopout.css({
  712.                                 backgroundColor : color,
  713.                                 borderColor : color
  714.                             });
  715.                             jqPopout.hide();
  716.                         }
  717.                     );
  718.                 } else {
  719.                     jqPopout.stop(true).animate({
  720.                         width: isSmall ? '16px' : '32px',
  721.                         height: isSmall ? '16px' : '32px',
  722.                         top: '0px',
  723.                         left: '0px',
  724.                         opacity: 0
  725.                     }, 200, 'easeOutExpo',
  726.                         function() {
  727.                             jqPopout.css({
  728.                                 backgroundColor: color,
  729.                                 borderColor: color
  730.                             });
  731.                             jqPopout.hide();
  732.                         }
  733.                     );
  734.                 }
  735.             });
  736.  
  737.             var completeFunction = function() {
  738.                 me.setSelected(false);
  739.             };
  740.  
  741.             // Add listener for selection of the popout image
  742.             this.popoutClickListener_ = $wnd.google.maps.event.addDomListener(popout, 'click', function(e) {
  743.                 // If BoxDrawer is available
  744.                 if(me.getItems().length > 1) {
  745.                     var container = me.getBoxDrawer().getContainer();
  746.                     // If clicked to hide the box drawer
  747.                     if(me.getBoxDrawer().isVisible()) {
  748.                         var mapImage = @com.yotta.horizon.mapping.presenters.SectionItemPresenterChainageGroup::getImageUrl(Lcom/yotta/horizon/mapping/shared/SectionItem;)(me.getFirstItem());
  749.                         if(hasDiagram) {
  750.                             $wnd.jQuery(me.getPopout()).css({
  751.                                 backgroundImage : 'url("' + imageUrl + '")'
  752.                             });
  753.                         } else if(mapImage != null) {
  754.                             $wnd.jQuery(me.getPopout()).css({
  755.                                 backgroundImage : 'url("'+ mapImage +'")',
  756.                                 backgroundSize : me.isSmall() ? "32px" : "64px"
  757.                             });
  758.                         }
  759.                         if(me.getBoxDrawer().isHorizontalUp()) {
  760.                             $wnd.jQuery(container).hide("slide", { direction : "down"  }, 250, completeFunction);
  761.                         } else if(me.getBoxDrawer().isHorizontalDown()) {
  762.                             $wnd.jQuery(container).hide("slide", { direction : "up"    }, 250, completeFunction);
  763.                         } else if(me.getBoxDrawer().isVerticalLeft()) {
  764.                             $wnd.jQuery(container).hide("slide", { direction : "right" }, 250, completeFunction);
  765.                         } else if(me.getBoxDrawer().isVerticalRight()) {
  766.                             $wnd.jQuery(container).hide("slide", { direction : "left"  }, 250, completeFunction);
  767.                         }
  768.                         for(var i = 0, size = me.getBoxDrawer().getBoxes().length; i < size; i++) {
  769.                             var currentBox = me.getBoxDrawer().getBoxes()[i];
  770.                             if(currentBox.getSelected()) {
  771.                                 currentBox.setSelected(false);
  772.                                 $wnd.google.maps.event.trigger(currentBox.getItem(), 'deselect');
  773.                             }
  774.                         }
  775.                         $wnd.jQuery(me.getPopout()).animate({
  776.                             backgroundColor: color,
  777.                             borderColor: color
  778.                         });
  779.                         lastBoxDrawerSelected = null;
  780.                     // Clicked to show the box drawer
  781.                     } else {
  782.                         $wnd.google.maps.event.trigger(me.getElement(), 'mouseover', { cancelItemMouseOver: true } );
  783.                         if(me.getBoxDrawer().isHorizontalUp()) {
  784.                             $wnd.jQuery(container).css('visibility', 'visible').show().hide().show("slide", { direction : "down"  }, 250);
  785.                         } else if(me.getBoxDrawer().isHorizontalDown()) {
  786.                             $wnd.jQuery(container).css('visibility', 'visible').show().hide().show("slide", { direction : "up"    }, 250);
  787.                         } else if(me.getBoxDrawer().isVerticalLeft()) {
  788.                             $wnd.jQuery(container).css('visibility', 'visible').show().hide().show("slide", { direction : "right" }, 250);
  789.                         } else if(me.getBoxDrawer().isVerticalRight()) {
  790.                             $wnd.jQuery(container).css('visibility', 'visible').show().hide().show("slide", { direction : "left"  }, 250);
  791.                         }
  792.                         for(var i = 0, size = me.getItems().length; i < size; i++) {
  793.                             $wnd.google.maps.event.trigger(me.getItems()[i], 'mouseout', { edge: 0 });
  794.                         }
  795.                         // Close the last open box drawer if possible
  796.                         if(lastBoxDrawerSelected != null
  797.                             && lastBoxDrawerSelected != me
  798.                             && lastBoxDrawerSelected.getBoxDrawer().isVisible()) {
  799.                                 $wnd.google.maps.event.trigger(lastBoxDrawerSelected.getPopout(), 'click');
  800.                         }
  801.                         if(e && e.zoom) {
  802.                             setTimeout(function() {
  803.                                 $wnd.jQuery(me.getPopout()).css({
  804.                                     backgroundImage: 'url(images/cross.png)',
  805.                                     backgroundSize: 'auto',
  806.                                     backgroundColor: '#cf3f0f',
  807.                                     borderColor: '#cf3f0f'
  808.                                 });
  809.                             }, 50);
  810.                         } else {
  811.                             $wnd.jQuery(me.getPopout()).css({backgroundImage: 'url(images/cross.png)', backgroundSize: 'auto'});
  812.                             $wnd.jQuery(me.getPopout()).animate({
  813.                                 backgroundColor: '#cf3f0f',
  814.                                 borderColor: '#cf3f0f'
  815.                             });
  816.                         }
  817.  
  818.                         me.setSelected(true);
  819.                     }
  820.                 // Single element clicked
  821.                 } else {
  822.                     // Close the last open box drawer if possible
  823.                     if(lastBoxDrawerSelected != null
  824.                     && lastBoxDrawerSelected != me
  825.                     && lastBoxDrawerSelected.getBoxDrawer().isVisible()) {
  826.                         $wnd.google.maps.event.trigger(lastBoxDrawerSelected.getPopout(), 'click');
  827.                     }
  828.                     $wnd.google.maps.event.trigger(me.getFirstItem(), 'click', {
  829.                         edge: undefined,
  830.                         path: undefined,
  831.                         vertex: undefined
  832.                     });
  833.                     me.setSelected(true);
  834.                 }
  835.             });
  836.  
  837.             // Check if linking chainage items and section items is enabled
  838.             if(@com.yotta.horizon.mapping.presenters.SectionItemPresenterChainageGroup::isSectionItemsLinked()()) {
  839.                 // Add listener for section item mouseover events incase the user hovers over a section item
  840.                 this.createSectionItemListeners(this, this.getFirstItem());
  841.             }
  842.  
  843.             // Add listener for section item selected events incase the selected item changes
  844.             this.sectionItemSelectedListener_ = $wnd.google.maps.event.addListener(map, 'section_item_selected', function(e) {
  845.                 if(lastBoxSelected != null) {
  846.                     $wnd.google.maps.event.trigger(lastBoxSelected, 'deselect');
  847.                 }
  848.                 var match = false;
  849.                 for(var i = 0, size = me.getItems().length; i < size; i++) {
  850.                     if(me.getItems()[i] == e.item) {
  851.                         match = true;
  852.                         if(me.getItems().length > 1 && !me.getBoxDrawer().isVisible()) {
  853.                             for(var j = 0, s = me.getBoxDrawer().getBoxes().length; j < s; j++) {
  854.                                 var currentBox = me.getBoxDrawer().getBoxes()[j];
  855.                                 if(currentBox.getItem() == e.item) {
  856.                                     $wnd.google.maps.event.trigger(me.getPopout(), 'click');
  857.                                     currentBox.setSelected(true);
  858.                                     break;
  859.                                 }
  860.                             }
  861.                         }
  862.                         break;
  863.                     }
  864.                 }
  865.                 me.setSelected(match);
  866.             });
  867.  
  868.             // Check if the item is selected?
  869.             for(var i = 0, size = this.getItems().length; i < size; i++) {
  870.                 if(@com.yotta.horizon.mapping.presenters.SectionItemPresenterChainageGroup::isSectionItemSelected(Lcom/yotta/horizon/mapping/shared/SectionItem;)(this.getItems()[i])) {
  871.                     setTimeout(function() { // Needs timeout so it can be attached to the DOM
  872.                         me.setSelected(true)
  873.                     }, 0);
  874.                 }
  875.             }
  876.         };
  877.  
  878.         // Gets the dom element
  879.         ChainageGroupBox.prototype.getElement = function() {
  880.             return this.element_;
  881.         };
  882.  
  883.         // Gets the item key
  884.         ChainageGroupBox.prototype.getKey = function() {
  885.             return this.key_;
  886.         };
  887.  
  888.         // Gets the is small boolean flag
  889.         ChainageGroupBox.prototype.isSmall = function() {
  890.             return this.isSmall_;
  891.         };
  892.  
  893.         // Gets the dom popout element
  894.         ChainageGroupBox.prototype.getPopout = function() {
  895.             return this.popout_;
  896.         };
  897.  
  898.         // Gets the direction of the Chainage Group
  899.         ChainageGroupBox.prototype.getDirection = function() {
  900.             return this.direction_;
  901.         };
  902.  
  903.         // Gets the direction in which section is heading
  904.         ChainageGroupBox.prototype.getSectionHeading = function() {
  905.             return this.sectionHeading_;
  906.         };
  907.  
  908.         // Gets the first section item
  909.         ChainageGroupBox.prototype.getFirstItem = function() {
  910.             if(this.getItems() != null) {
  911.                 return this.getItems()[0];
  912.             }
  913.             return null;
  914.         };
  915.  
  916.         // Gets the section items
  917.         ChainageGroupBox.prototype.getItems = function() {
  918.             return this.sectionItems_;
  919.         };
  920.  
  921.         // Add a section item to the array
  922.         ChainageGroupBox.prototype.addItem = function(elbowLatLng, sectionItem) {
  923.             var polyline = null;
  924.             if(@com.yotta.horizon.mapping.presenters.SectionItemPresenterChainageGroup::isElbowEnabled()()) {
  925.                 polyline = new $wnd.google.maps.Polyline($wnd.jQuery.extend({
  926.                 map: this.getMap(),
  927.                 path: [
  928.                     sectionItem.setIcon ? // Marker or polyline/polygon
  929.                         sectionItem.getPosition() :
  930.                         @com.yotta.horizon.shared.gmaps.MapMath::getNearestLatLngOnPath(Lcom/yotta/horizon/shared/gmaps/MVCArray;Lcom/yotta/horizon/shared/gmaps/LatLng;)(sectionItem.getPath(), elbowLatLng),
  931.                     elbowLatLng
  932.                 ]
  933.                 }, POLYLINE_ITEM_DEFAULT));
  934.                 this.getPolylines().push(polyline);
  935.             }
  936.             this.getItems().push(sectionItem);
  937.  
  938.  
  939.             // Populate the drawer
  940.             if(this.getBoxDrawer() == null) {
  941.                 var boxDrawer = new ChainageGroupBoxDrawer(this.getMap(), this.getFirstItem(), this.getPolylines()[0],
  942.                                                         this.getElbow(), this.getColor(), this.getDirection(),
  943.                                                         this.isSmall(), this.getPopout(), this.getSectionHeading());
  944.                 this.setBoxDrawer(boxDrawer);
  945.             }
  946.             this.getBoxDrawer().addItem(sectionItem, polyline);
  947.             this.createSectionItemListeners(this, sectionItem);
  948.             this.setBubbleVisible(true);
  949.             this.setBubbleText(""+this.getItems().length);
  950.         };
  951.  
  952.         // Creates section item mouseover and mouseout listeners for the given item
  953.         ChainageGroupBox.prototype.createSectionItemListeners = function(me, item) {
  954.             var sectionItemMouseOverListener = $wnd.google.maps.event.addListener(item, 'mouseover', function(e) {
  955.                 if(!e.notSimpleEvent && (me.getItems().length == 1 || !me.getBoxDrawer().isVisible() || me.getPopout().style.display == 'none')) {
  956.                     $wnd.google.maps.event.trigger(me.getElement(), 'mouseover', {
  957.                         cancelItemMouseOver: true,
  958.                         item: item
  959.                     });
  960.                 }
  961.             });
  962.             this.sectionItemMouseOverListeners_.push(sectionItemMouseOverListener);
  963.  
  964.             var sectionItemMouseOutListener = $wnd.google.maps.event.addListener(item, 'mouseout', function(e) {
  965.                 if(me.getItems().length == 1 || !me.getBoxDrawer().isVisible() || me.getPopout().style.display == 'none') {
  966.                     $wnd.google.maps.event.trigger(me.getElement(), 'mouseout', {
  967.                         cancelItemMouseOut: true,
  968.                         item: item
  969.                     });
  970.                     $wnd.google.maps.event.trigger(me.getPopout(), 'mouseout', {
  971.                         stopAnimation: (me.getItems().length > 1 && me.getBoxDrawer().isVisible()) ? false : true
  972.                     });
  973.                 }
  974.             });
  975.             this.sectionItemMouseOutListeners_.push(sectionItemMouseOutListener);
  976.         };
  977.  
  978.         // Gets the ChainageGroupBoxDrawer
  979.         ChainageGroupBox.prototype.getBoxDrawer = function() {
  980.             return this.boxDrawer_;
  981.         };
  982.  
  983.         // Sets the ChainageGroupBoxDrawer
  984.         ChainageGroupBox.prototype.setBoxDrawer = function(boxDrawer) {
  985.             this.boxDrawer_ = boxDrawer;
  986.         };
  987.  
  988.         // Gets the elbow polyline
  989.         ChainageGroupBox.prototype.getElbow = function() {
  990.             return this.elbowPolyline_;
  991.         };
  992.  
  993.         // Gets the map
  994.         ChainageGroupBox.prototype.getMap = function() {
  995.             return this.map_;
  996.         };
  997.  
  998.         // Gets the box drawer
  999.         ChainageGroupBox.prototype.getDrawerElement = function() {
  1000.             return this.drawerElement_;
  1001.         };
  1002.  
  1003.         // Gets the items polylines
  1004.         ChainageGroupBox.prototype.getPolylines = function() {
  1005.             return this.polylines_;
  1006.         };
  1007.  
  1008.         // Gets the item counter bubble
  1009.         ChainageGroupBox.prototype.getBubble = function() {
  1010.             return this.itemCounterBubble_;
  1011.         };
  1012.  
  1013.         // Gets the popout counter bubble
  1014.         ChainageGroupBox.prototype.getPopoutBubble = function() {
  1015.             return this.popoutCounterBubble_;
  1016.         };
  1017.  
  1018.         // Sets the item&popout counter bubble text
  1019.         ChainageGroupBox.prototype.setBubbleText = function(text) {
  1020.             this.getBubble().innerHTML = text;
  1021.             this.getPopoutBubble().innerHTML = text;
  1022.         };
  1023.  
  1024.         // Sets the background colour
  1025.         ChainageGroupBox.prototype.setColor = function(color) {
  1026.             this.color_ = color;
  1027.         };
  1028.  
  1029.         // Gest the background colour
  1030.         ChainageGroupBox.prototype.getColor = function() {
  1031.             return this.color_;
  1032.         };
  1033.  
  1034.         // Sets the item & popout counter bubble visibility
  1035.         ChainageGroupBox.prototype.setBubbleVisible = function(visible) {
  1036.             this.getBubble().style.display = visible ? '' : 'none';
  1037.             this.getPopoutBubble().style.display = visible ? '' : 'none';
  1038.             if (visible) {
  1039.                 this.getBubble().removeAttribute('aria-hidden');
  1040.                 this.getPopoutBubble().removeAttribute('aria-hidden');
  1041.             } else {
  1042.                 this.getBubble().setAttribute('aria-hidden', 'true');
  1043.                 this.getPopoutBubble().setAttribute('aria-hidden', 'true');
  1044.             }
  1045.         };
  1046.  
  1047.         // Gets whether the item is selected
  1048.         ChainageGroupBox.prototype.getSelected = function() {
  1049.             return this.selected_;
  1050.         };
  1051.  
  1052.         // Sets whether the item is selected
  1053.         ChainageGroupBox.prototype.setSelected = function(selected) {
  1054.             var performUpdate = selected != this.selected_; // Flag to determine if update is required
  1055.             this.selected_ = selected;
  1056.  
  1057.             if(performUpdate) {
  1058.                 if(selected) {
  1059.                     lastBoxSelected = this;
  1060.                     if(this.getItems() != null && this.getItems().length > 1) {
  1061.                         lastBoxDrawerSelected = this;
  1062.                     }
  1063.                     $wnd.jQuery(this.getElement()).addClass(@com.yotta.horizon.mapping.presenters.SectionItemPresenterChainageGroup::getChainageGroupItemActiveStyleName());
  1064.                     if(this.getElement()) {
  1065.                     $wnd.google.maps.event.trigger(this.getElement(), 'mouseover', {
  1066.                         cancelItemMouseOver: false
  1067.                     });
  1068.                     }
  1069.                 } else {
  1070.                     if(lastBoxSelected == this) {
  1071.                         lastBoxSelected = null;
  1072.                     }
  1073.                     if(lastBoxDrawerSelected == this) {
  1074.                         lastBoxDrawerSelected = null;
  1075.                     }
  1076.                     $wnd.jQuery(this.getElement()).removeClass(@com.yotta.horizon.mapping.presenters.SectionItemPresenterChainageGroup::getChainageGroupItemActiveStyleName());
  1077.                     if(this.getElement()) {
  1078.                     $wnd.google.maps.event.trigger(this.getElement(), 'mouseout', {
  1079.                         cancelItemMouseOut: false,
  1080.                         stopAnimation: false
  1081.                     });
  1082.                     }
  1083.                     if(this.getPopout()) {
  1084.                     $wnd.google.maps.event.trigger(this.getPopout(), 'mouseout', {
  1085.                         stopAnimation: false
  1086.                     });
  1087.                 }
  1088.             }
  1089.             }
  1090.         };
  1091.  
  1092.         // Dispose of the box and remove it
  1093.         ChainageGroupBox.prototype.dispose = function() {
  1094.             // Remove listeners
  1095.             $wnd.google.maps.event.removeListener(this.elementMouseOverListener_);
  1096.             $wnd.google.maps.event.removeListener(this.elementMouseOutListener_);
  1097.             $wnd.google.maps.event.removeListener(this.popoutMouseOutListener_);
  1098.             $wnd.google.maps.event.removeListener(this.popoutBubbleMouseOutListener_);
  1099.             $wnd.google.maps.event.removeListener(this.popoutClickListener_);
  1100.             $wnd.google.maps.event.removeListener(this.sectionItemSelectedListener_);
  1101.             // Check if linking chainage items and section items is enabled
  1102.             if(@com.yotta.horizon.mapping.presenters.SectionItemPresenterChainageGroup::isSectionItemsLinked()()) {
  1103.                 for(var i = 0, size = this.getItems().length; i < size; i++) {
  1104.                     $wnd.google.maps.event.removeListener(this.sectionItemMouseOutListeners_[i]);
  1105.                     $wnd.google.maps.event.removeListener(this.sectionItemMouseOverListeners_[i]);
  1106.                 }
  1107.             }
  1108.             // Hide the item polyline if enabled
  1109.             if(@com.yotta.horizon.mapping.presenters.SectionItemPresenterChainageGroup::isElbowEnabled()()) {
  1110.                 for(var i = 0, size = this.getPolylines().length; i < size; i++) {
  1111.                     this.getPolylines()[i].setMap(null);
  1112.                 }
  1113.             }
  1114.             if(this.getItems().length > 1) {
  1115.                 this.getBoxDrawer().dispose();
  1116.             }
  1117.             // Remove the element
  1118.             if(this.getElement().parentNode) {
  1119.                 this.getElement().parentNode.removeChild(this.getElement());
  1120.             }
  1121.             // Remove pointer to last box selected if required
  1122.             if(lastBoxSelected == this) {
  1123.                 lastBoxSelected = null;
  1124.             }
  1125.             if(lastBoxDrawerSelected == this) {
  1126.                 lastBoxDrawerSelected = null;
  1127.             }
  1128.             this.elementMouseOverListener_ = null;
  1129.             this.elementMouseOutListener_ = null;
  1130.             this.popoutMouseOutListener_ = null;
  1131.             this.popoutBubbleMouseOutListener_ = null;
  1132.             this.popoutClickListener_ = null;
  1133.             this.sectionItemChangedListener_ = null;
  1134.             this.sectionItemMouseOutListeners_ = null;
  1135.             this.sectionItemMouseOverListeners_ = null;
  1136.             this.selected_ = null;
  1137.             this.elbowPolyline_ = null;
  1138.             this.element_ = null;
  1139.             this.sectionItems_ = null;
  1140.             this.polylines_ = null;
  1141.             this.maps_ = null;
  1142.             this.itemCounterBubble_ = null;
  1143.             this.boxDrawer_ = null;
  1144.         };
  1145.  
  1146.         // Method raised when the item container is moused over
  1147.         var lastItemMousedOver = null;
  1148.         var itemContainerMouseOver = function(e) {
  1149.             if(lastItemMousedOver) {
  1150.                 lastItemMousedOver.style.zIndex = null;
  1151.             }
  1152.             this.style.zIndex = 1000;
  1153.             lastItemMousedOver = this;
  1154.  
  1155.             // Stop polylines and markers from mousing over
  1156.             e.cancelBubble = true;
  1157.             if(e.stopPropagation) {
  1158.                 e.stopPropagation();
  1159.             }
  1160.         };
  1161.         var itemContainerCancel = function(e) {
  1162.             // Stop polylines and markers from mousing over
  1163.             e.cancelBubble = true;
  1164.             if(e.stopPropagation) {
  1165.                 e.stopPropagation();
  1166.             }
  1167.         };
  1168.  
  1169.         // CONSTRUCTOR ChainageGroupBoxSimple
  1170.         // Simple Section Item box that represents an item inside the drawer
  1171.         var ChainageGroupBoxSimple = function(map, sectionItem, polyline, elbow, color, isSmall) {
  1172.             var me = this; //self reference
  1173.  
  1174.             this.map_ = map;
  1175.             this.sectionItem_ = sectionItem;
  1176.             this.element_ = $doc.createElement('div');
  1177.             this.popout_ = $doc.createElement('div');
  1178.             this.polyline_ = polyline;
  1179.             this.elbow_ = elbow;
  1180.             this.selected_ = false;
  1181.             this.isSmall_ = isSmall;
  1182.  
  1183.             this.elementMouseOverListener_ = null;
  1184.             this.elementMouseOutListener_ = null;
  1185.             this.popoutMouseOutListener_ = null;
  1186.             this.popoutClickListener_ = null;
  1187.             this.sectionItemMouseOutListener_ = null;
  1188.             this.sectionItemMouseOverListener_ = null;
  1189.             this.sectionItemSelectedListener_ = null;
  1190.  
  1191.             // Setup element
  1192.             var element = this.getElement();
  1193.             element.style.width = isSmall ? '16px' : '32px';
  1194.             element.style.height = isSmall ? '16px' : '32px';
  1195.             element.style.backgroundColor = color;
  1196.  
  1197.             var mapImage;
  1198.             var hasDiagram = @com.yotta.horizon.mapping.shared.SectionItemUtils::getSectionItemDiagramId(Lcom/yotta/horizon/mapping/shared/SectionItem;)(this.getItem()) != null;
  1199.             if(hasDiagram) {
  1200.                 mapImage = @com.yotta.horizon.mapping.presenters.SectionItemPresenterChainageGroup::getCropUrl(Lcom/yotta/horizon/mapping/shared/SectionItem;)(this.getItem());
  1201.             } else {
  1202.                 mapImage = @com.yotta.horizon.mapping.presenters.SectionItemPresenterChainageGroup::getImageUrl(Lcom/yotta/horizon/mapping/shared/SectionItem;)(this.getItem());
  1203.             }
  1204.             if(mapImage != null) {
  1205.                 element.style.background = color + " url('" + mapImage + "') no-repeat center center";
  1206.                 element.style.backgroundSize = isSmall ? "16px" : "32px";
  1207.             }
  1208.             element.style.position = 'relative';
  1209.             $wnd.jQuery(element).addClass(@com.yotta.horizon.mapping.presenters.SectionItemPresenterChainageGroup::getChainageGroupItemStyleName());
  1210.             if(isSmall) {
  1211.                 $wnd.jQuery(element).addClass(@com.yotta.horizon.mapping.presenters.SectionItemPresenterChainageGroup::getChainageGroupItemSmallStyleName());
  1212.             }
  1213.  
  1214.             // Setup popout
  1215.             var popout = this.getPopout();
  1216.             popout.style.width = isSmall ? '16px' : '32px';
  1217.             popout.style.height = isSmall ? '16px' : '32px';
  1218.             popout.style.backgroundColor = color;
  1219.             popout.style.position = 'relative';
  1220.             popout.style.borderColor = color;
  1221.             $wnd.jQuery(popout).addClass(@com.yotta.horizon.mapping.presenters.SectionItemPresenterChainageGroup::getChainageGroupItemPopoutStyleName());
  1222.  
  1223.             element.appendChild(popout);
  1224.  
  1225.             // Add listener for mouse over to highlight the path to the item
  1226.             this.elementMouseOverListener_ = $wnd.google.maps.event.addDomListener(element, 'mouseover', function(e) {
  1227.                 // Highlight path if enabled
  1228.                 if(@com.yotta.horizon.mapping.presenters.SectionItemPresenterChainageGroup::isElbowEnabled()()) {
  1229.                     me.getPolyline().setOptions(POLYLINE_ITEM_ACTIVE);
  1230.                     me.getElbow().setOptions(POLYLINE_ELBOW_ACTIVE);
  1231.                 }
  1232.                 // Add hovered class
  1233.                 $wnd.jQuery(me.getElement()).addClass(@com.yotta.horizon.mapping.presenters.SectionItemPresenterChainageGroup::getChainageGroupItemHoveredStyleName());
  1234.  
  1235.                 // Highlight the item
  1236.                 if(!e.cancelItemMouseOver) {
  1237.                     // Pass edge as fix for editable polylines which cause issues if an empty object is passed
  1238.                     $wnd.google.maps.event.trigger(me.getItem(), 'mouseover', { edge: 0 });
  1239.                 }
  1240.  
  1241.                 // If there is a diagram begin loading it
  1242.                 var jqPopout = $wnd.jQuery(popout);
  1243.                 var mapImage = @com.yotta.horizon.mapping.presenters.SectionItemPresenterChainageGroup::getImageUrl(Lcom/yotta/horizon/mapping/shared/SectionItem;)(me.getItem());
  1244.                 jqPopout.empty().css({
  1245.                         backgroundImage: "url('" + mapImage + "')",
  1246.                         backgroundSize: me.isSmall() ? "32px" : "64px"
  1247.                 });
  1248.                 // Show popout
  1249.                 jqPopout.stop(true).show().animate({
  1250.                     width: isSmall ? '32px' : '64px',
  1251.                     height: isSmall ? '32px' : '64px',
  1252.                     top: isSmall ? '-8px' : '-16px',
  1253.                     left: isSmall ? '-8px' : '-16px',
  1254.                     opacity: 1
  1255.                 }, 400, 'easeOutExpo');
  1256.  
  1257.                 // Trigger hover
  1258.                 if(itemHoverThrottle) {
  1259.                     clearTimeout(itemHoverThrottle);
  1260.                     itemHoverThrottle = null;
  1261.                 }
  1262.                 if(!e.manual) {
  1263.                     itemHoverThrottle = setTimeout(function() {
  1264.                         if(me.getItem()) {
  1265.                         $wnd.google.maps.event.trigger(me.getItem(), 'hover', { over: true });
  1266.                         }
  1267.                     }, 50);
  1268.                 }
  1269.             });
  1270.  
  1271.             // Add listener for mouse out to unhighlight the path to the item
  1272.             this.elementMouseOutListener_ = $wnd.google.maps.event.addDomListener(element, 'mouseout', function(e) {
  1273.                 // Dont animate if selected
  1274.                 if(me.getSelected()) {
  1275.                     return;
  1276.                 }
  1277.  
  1278.                 // Reset polyline if enabled
  1279.                 if(@com.yotta.horizon.mapping.presenters.SectionItemPresenterChainageGroup::isElbowEnabled()()) {
  1280.                     me.getPolyline().setOptions(POLYLINE_ITEM_DEFAULT);
  1281.  
  1282.                     // Only unhighlight the elbow if it is not the same reference elbow as the current item
  1283.                     if(!lastBoxSelected || lastBoxSelected.getElbow() != me.getElbow()) {
  1284.                         me.getElbow().setOptions(POLYLINE_ELBOW_DEFAULT);
  1285.                     }
  1286.                 }
  1287.  
  1288.                 if(!e.cancelItemMouseOut) {
  1289.                     // Pass edge as fix for editable polylines which cause issues if an empty object is passed
  1290.                     $wnd.google.maps.event.trigger(me.getItem(), 'mouseout', { edge: 0 });
  1291.                 }
  1292.  
  1293.                 // Remove hovered class
  1294.                 $wnd.jQuery(me.getElement()).removeClass(@com.yotta.horizon.mapping.presenters.SectionItemPresenterChainageGroup::getChainageGroupItemHoveredStyleName());
  1295.  
  1296.                 // Trigger hover
  1297.                 if(itemHoverThrottle) {
  1298.                     clearTimeout(itemHoverThrottle);
  1299.                     itemHoverThrottle = null;
  1300.                 }
  1301.                 if(!e.manual) {
  1302.                     itemHoverThrottle = setTimeout(function() {
  1303.                         $wnd.google.maps.event.trigger(me.getItem(), 'hover', { over: false });
  1304.                     }, 50);
  1305.                 }
  1306.             });
  1307.  
  1308.             // Add listener for popout mouseout to remove popout
  1309.             this.popoutMouseOutListener_ = $wnd.google.maps.event.addDomListener(popout, 'mouseout', function() {
  1310.                 // Dont animate if selected
  1311.                 if(me.getSelected()) {
  1312.                     return;
  1313.                 }
  1314.  
  1315.                 $wnd.jQuery(popout).stop(true).animate({
  1316.                     width: isSmall ? '16px' : '32px',
  1317.                     height: isSmall ? '16px' : '32px',
  1318.                     top: '0px',
  1319.                     left: '0px',
  1320.                     opacity: 0
  1321.                 }, 200, 'easeOutExpo', function() {
  1322.                     $wnd.jQuery(popout).hide();
  1323.                 });
  1324.             });
  1325.  
  1326.             /// Add listener for selection of the popout image
  1327.             this.popoutClickListener_ = $wnd.google.maps.event.addDomListener(popout, 'click', function() {
  1328.                 $wnd.google.maps.event.trigger(me.getItem(), 'click', {
  1329.                     edge: undefined,
  1330.                     path: undefined,
  1331.                     vertex: undefined
  1332.                 });
  1333.             });
  1334.  
  1335.             // Check if linking chainage items and section items is enabled
  1336.             if(@com.yotta.horizon.mapping.presenters.SectionItemPresenterChainageGroup::isSectionItemsLinked()()) {
  1337.                 // Add listener for section item mouseover events incase the user hovers over a section item
  1338.                 this.sectionItemMouseOverListener_ = $wnd.google.maps.event.addDomListener(me.getItem(), 'mouseover', function(e) {
  1339.                     if(!e.notSimpleEvent) {
  1340.                         $wnd.google.maps.event.trigger(me.getElement(), 'mouseover', {
  1341.                             cancelItemMouseOver: true,
  1342.                             item: me.getItem()
  1343.                         });
  1344.                     }
  1345.                 });
  1346.  
  1347.                 // Add listener for section item mouseout events incase the user hovers over a section item
  1348.                 this.sectionItemMouseOutListener_ = $wnd.google.maps.event.addDomListener(me.getItem(), 'mouseout', function(e) {
  1349.                     if(!e.notSimpleEvent) {
  1350.                         $wnd.google.maps.event.trigger(me.getElement(), 'mouseout', {
  1351.                             cancelItemMouseOut: true,
  1352.                             item: me.getItem()
  1353.                         });
  1354.                         $wnd.google.maps.event.trigger(me.getPopout(), 'mouseout');
  1355.                     }
  1356.                 });
  1357.             }
  1358.  
  1359.             // Add listener for section item selected events incase the selected item changes
  1360.             this.sectionItemSelectedListener_ = $wnd.google.maps.event.addListener(map, 'section_item_selected', function(e) {
  1361.                 if(lastBoxSelected != null) {
  1362.                     $wnd.google.maps.event.trigger(lastBoxSelected, 'deselect');
  1363.                 }
  1364.                 if(me.getItem() == e.item && lastBoxDrawerSelected != null) {
  1365.                     var found = false;
  1366.                     for(var i = 0, size = lastBoxDrawerSelected.getBoxDrawer().getBoxes().length; i < size; i++) {
  1367.                         if(me == lastBoxDrawerSelected.getBoxDrawer().getBoxes()[i]) {
  1368.                             found = true;
  1369.                             break;
  1370.                         }
  1371.                     }
  1372.                     if(!found && lastBoxDrawerSelected.getBoxDrawer().isVisible()) {
  1373.                         $wnd.google.maps.event.trigger(lastBoxDrawerSelected.getPopout(), 'click');
  1374.                     }
  1375.                 }
  1376.                 me.setSelected(me.getItem() == e.item);
  1377.             });
  1378.  
  1379.             // Check if the item is selected?
  1380.             if(@com.yotta.horizon.mapping.presenters.SectionItemPresenterChainageGroup::isSectionItemSelected(Lcom/yotta/horizon/mapping/shared/SectionItem;)(this.getItem())) {
  1381.                 setTimeout(function() { // Needs timeout so it can be attached to the DOM
  1382.                     me.setSelected(true)
  1383.                 }, 0);
  1384.             }
  1385.             @com.yotta.horizon.mapping.presenters.SectionItemPresenterChainageGroup::fireItemReady(IIZ)(this.getItem().renderGroupId, this.getItem().geometry.DSIID, true);
  1386.         }
  1387.  
  1388.         ChainageGroupBoxSimple.prototype = new $wnd.google.maps.OverlayView();
  1389.  
  1390.         // Gets the dom element
  1391.         ChainageGroupBoxSimple.prototype.getElement = function() {
  1392.             return this.element_;
  1393.         };
  1394.  
  1395.         // Gets the section item
  1396.         ChainageGroupBoxSimple.prototype.getItem = function() {
  1397.             return this.sectionItem_;
  1398.         };
  1399.  
  1400.         // Gets the polyline
  1401.         ChainageGroupBoxSimple.prototype.getPolyline = function() {
  1402.             return this.polyline_;
  1403.         };
  1404.  
  1405.         // Gets the polyline elbow
  1406.         ChainageGroupBoxSimple.prototype.getElbow = function() {
  1407.             return this.elbow_;
  1408.         };
  1409.  
  1410.         // Gets the popout dom element
  1411.         ChainageGroupBoxSimple.prototype.getPopout = function() {
  1412.             return this.popout_;
  1413.         };
  1414.  
  1415.         // Gets the 'is small' boolean flag
  1416.         ChainageGroupBoxSimple.prototype.isSmall = function() {
  1417.             return this.isSmall_;
  1418.         };
  1419.  
  1420.         // Gets the 'selected' boolean flag
  1421.         ChainageGroupBoxSimple.prototype.getSelected = function() {
  1422.             return this.selected_;
  1423.         };
  1424.  
  1425.         // Sets the 'selected' boolean flag
  1426.         ChainageGroupBoxSimple.prototype.setSelected = function(selected) {
  1427.             var performUpdate = selected != this.selected_; // Flag to determine if update is required
  1428.             this.selected_ = selected;
  1429.  
  1430.             if(performUpdate) {
  1431.                 if(selected) {
  1432.                     lastBoxSelected = this;
  1433.                     $wnd.jQuery(this.getElement()).addClass(@com.yotta.horizon.mapping.presenters.SectionItemPresenterChainageGroup::getChainageGroupItemActiveStyleName());
  1434.                     if(this.getElement() != null) {
  1435.                     $wnd.google.maps.event.trigger(this.getElement(), 'mouseover', {
  1436.                         cancelItemMouseOver: false
  1437.                     });
  1438.                     }
  1439.                 } else {
  1440.                     if(lastBoxSelected == this) {
  1441.                         lastBoxSelected = null;
  1442.                     }
  1443.                     $wnd.jQuery(this.getElement()).removeClass(@com.yotta.horizon.mapping.presenters.SectionItemPresenterChainageGroup::getChainageGroupItemActiveStyleName());
  1444.                     $wnd.google.maps.event.trigger(this.getElement(), 'mouseout', {
  1445.                         cancelItemMouseOut: false
  1446.                     });
  1447.                     $wnd.google.maps.event.trigger(this.getPopout(), 'mouseout');
  1448.                 }
  1449.             }
  1450.         };
  1451.  
  1452.         // Disposes of this object
  1453.         ChainageGroupBoxSimple.prototype.dispose = function() {
  1454.             $wnd.google.maps.event.removeListener(this.elementMouseOverListener_);
  1455.             $wnd.google.maps.event.removeListener(this.elementMouseOutListener_);
  1456.             $wnd.google.maps.event.removeListener(this.popoutMouseOutListener_);
  1457.             $wnd.google.maps.event.removeListener(this.popoutClickListener_);
  1458.             $wnd.google.maps.event.removeListener(this.sectionItemMouseOutListener_);
  1459.             $wnd.google.maps.event.removeListener(this.sectionItemMouseOverListener_);
  1460.             $wnd.google.maps.event.removeListener(this.sectionItemSelectedListener_);
  1461.             // Remove the element
  1462.             if(this.getElement().parentNode) {
  1463.                 this.getElement().parentNode.removeChild(this.getElement());
  1464.             }
  1465.  
  1466.             this.getPolyline().setMap(null);
  1467.  
  1468.             this.map_ = null;
  1469.             this.sectionItem_ = null;
  1470.             this.element_ = null;
  1471.             this.popout_ = null;
  1472.             this.polyline_ = null;
  1473.             this.elbow_ = null;
  1474.             this.selected_ = null;
  1475.             this.isSmall_ = null;
  1476.             this.elementMouseOverListener_ = null;
  1477.             this.elementMouseOutListener_ = null;
  1478.             this.popoutMouseOutListener_ = null;
  1479.             this.popoutClickListener_ = null;
  1480.             this.sectionItemMouseOutListener_ = null;
  1481.             this.sectionItemMouseOverListener_ = null;
  1482.             this.sectionItemSelectedListener_ = null;
  1483.         };
  1484.  
  1485.         ChainageGroupBoxDrawer.prototype = new $wnd.google.maps.OverlayView();
  1486.  
  1487.         // CONSTRUCTOR ChainageGroupBoxDrawer
  1488.         // Element that slides out of the parent element that contains more than 1 item from the same group
  1489.         function ChainageGroupBoxDrawer(map, item, polyline, elbow, color, direction, isSmall, parent, sectionHeading) {
  1490.             this.map_ = map;
  1491.             this.items_ = [item];
  1492.             this.polylines_ = [polyline];
  1493.             this.elbow_ = elbow;
  1494.             this.color_ = color;
  1495.             this.direction_ = direction;
  1496.             this.boxes_ = [];
  1497.             this.isSmall_ = isSmall;
  1498.             this.parent_ = parent;
  1499.             this.sectionHeading_ = sectionHeading;
  1500.  
  1501.             this.setMap(map);
  1502.         };
  1503.  
  1504.         // Checks whether the section is heading between the start and the end degrees (0 being North)
  1505.         ChainageGroupBoxDrawer.prototype.isSectionHeading = function(start, end) {
  1506.             if(this.sectionHeading_ >= start && this.sectionHeading_ <= end) {
  1507.                 return true;
  1508.             }
  1509.             return false;
  1510.         };
  1511.  
  1512.         // Checks whether the section is heading North-West or South-East(from North-West)
  1513.         ChainageGroupBoxDrawer.prototype.isSectionHeadingNW = function() {
  1514.             if(this.isSectionHeading(90, 179) || this.isSectionHeading(270, 359)) {
  1515.                 return true;
  1516.             }
  1517.             return false;
  1518.         };
  1519.  
  1520.         // Checks whether the chainage group is vertical and the drawer has to slide to the left
  1521.         ChainageGroupBoxDrawer.prototype.isVerticalLeft = function() {
  1522.             if((this.getDirection() == ChainageGroupDirection.NORTH && !this.isSectionHeadingNW())
  1523.             || (this.getDirection() == ChainageGroupDirection.SOUTH && this.isSectionHeadingNW())) {
  1524.                     return true;
  1525.             }
  1526.             return false;
  1527.         };
  1528.  
  1529.         // Checks whether the chainage group is vertical and the drawer has to slide to the right
  1530.         ChainageGroupBoxDrawer.prototype.isVerticalRight = function() {
  1531.             if((this.getDirection() == ChainageGroupDirection.SOUTH && !this.isSectionHeadingNW())
  1532.             || (this.getDirection() == ChainageGroupDirection.NORTH && this.isSectionHeadingNW())) {
  1533.                     return true;
  1534.             }
  1535.             return false;
  1536.         };
  1537.  
  1538.         // Checks whether the chainage group is horizontal and the drawer has to slide up
  1539.         ChainageGroupBoxDrawer.prototype.isHorizontalUp = function() {
  1540.             if((this.getDirection() == ChainageGroupDirection.WEST && !this.isSectionHeadingNW())
  1541.             || (this.getDirection() == ChainageGroupDirection.EAST && this.isSectionHeadingNW())) {
  1542.                 return true;
  1543.             }
  1544.             return false;
  1545.         };
  1546.  
  1547.         // Checks whether the chainage group is horizontal and the drawer has to slide dowp
  1548.         ChainageGroupBoxDrawer.prototype.isHorizontalDown = function() {
  1549.             if((this.getDirection() == ChainageGroupDirection.EAST && !this.isSectionHeadingNW())
  1550.             || (this.getDirection() == ChainageGroupDirection.WEST && this.isSectionHeadingNW())) {
  1551.                 return true;
  1552.             }
  1553.             return false;
  1554.         };
  1555.  
  1556.         // Sets the position at which to draw the drawer
  1557.         ChainageGroupBoxDrawer.prototype.draw = function() {
  1558.             var offset = (this.getItems().length > MAX_ITEMS_PER_ROW ? '-15' : '10') + '%';
  1559.             if(this.isVerticalLeft()) {
  1560.                 if(this.isSmall()) {
  1561.                     this.getContainer().style.right = '32px';
  1562.                 } else {
  1563.                     this.getContainer().style.right = '65px';
  1564.                     this.getContainer().style.top = offset;
  1565.                 }
  1566.             } else if(this.isVerticalRight()) {
  1567.                 if(this.isSmall()) {
  1568.                     this.getContainer().style.left = '34px';
  1569.                 } else {
  1570.                     this.getContainer().style.left = '67px';
  1571.                     this.getContainer().style.top = offset;
  1572.                 }
  1573.             } else if(this.isHorizontalDown()) {
  1574.                 if(this.isSmall()) {
  1575.                     this.getContainer().style.top = '34px';
  1576.                 } else {
  1577.                     this.getContainer().style.top = '67px';
  1578.                     this.getContainer().style.left = offset;
  1579.                 }
  1580.             } else if(this.isHorizontalUp()) {
  1581.                 if(this.isSmall()) {
  1582.                     this.getContainer().style.bottom = '32px';
  1583.                 } else {
  1584.                     this.getContainer().style.bottom = '65px';
  1585.                     this.getContainer().style.left = offset;
  1586.                 }
  1587.             } else {
  1588.                 throw "No direction found";
  1589.             }
  1590.  
  1591.             this.getItemContainer().style.bottom = null;
  1592.             this.getItemContainer().style.right = null;
  1593.             this.getItemContainer().style.left = '-1px';
  1594.             this.getItemContainer().style.top = '-1px';
  1595.  
  1596.             this.update();
  1597.         };
  1598.  
  1599.         // Creates the DOM elements and bubbling listener blockers for the drawer
  1600.         ChainageGroupBoxDrawer.prototype.onAdd = function() {
  1601.  
  1602.             this.container_ = $doc.createElement('div');
  1603.             this.itemContainer_ = $doc.createElement('div');
  1604.  
  1605.             // Setup container
  1606.             this.getContainer().style.visibility = 'hidden';
  1607.             this.getContainer().style.position = 'absolute';
  1608.             this.getItemContainer().style.position = 'relative';
  1609.  
  1610.             // Setup the item container
  1611.             $wnd.jQuery(this.getItemContainer()).addClass(@com.yotta.horizon.mapping.presenters.SectionItemPresenterChainageGroup::getChainageGroupStyleName());
  1612.             this.getContainer().appendChild(this.getItemContainer());
  1613.  
  1614.             // Setup listener to move panel to front
  1615.             $wnd.google.maps.event.addDomListener(this.getItemContainer(), 'mouseover', itemContainerMouseOver);
  1616.  
  1617.             // Setup listeners to stop underlying polylines from mousing over
  1618.             $wnd.google.maps.event.addDomListener(this.getItemContainer(), 'click', itemContainerCancel);
  1619.             $wnd.google.maps.event.addDomListener(this.getItemContainer(), 'dblclick', itemContainerCancel);
  1620.             $wnd.google.maps.event.addDomListener(this.getItemContainer(), 'rightclick', itemContainerCancel);
  1621.             $wnd.google.maps.event.addDomListener(this.getItemContainer(), 'mousedown', itemContainerCancel);
  1622.             $wnd.google.maps.event.addDomListener(this.getItemContainer(), 'mouseup', itemContainerCancel);
  1623.             $wnd.google.maps.event.addDomListener(this.getItemContainer(), 'mousemove', itemContainerCancel);
  1624.             $wnd.google.maps.event.addDomListener(this.getItemContainer(), 'mouseout', itemContainerCancel);
  1625.             $wnd.google.maps.event.addDomListener(this.getItemContainer(), 'mouseenter', itemContainerCancel);
  1626.             $wnd.google.maps.event.addDomListener(this.getItemContainer(), 'mouseleave', itemContainerCancel);
  1627.  
  1628.             this.parent_.appendChild(this.container_);
  1629.  
  1630.         };
  1631.  
  1632.         // Gets the background colour
  1633.         ChainageGroupBoxDrawer.prototype.getColor = function() {
  1634.             return this.color_;
  1635.         };
  1636.  
  1637.         // Creates the table to hold the items and populates it
  1638.         ChainageGroupBoxDrawer.prototype.update = function() {
  1639.             if(this._cacheItemsCount === this.getItems().length) {
  1640.                 return;
  1641.             }
  1642.             this._cacheItemsCount = this.getItems().length;
  1643.            
  1644.             // Empty the item container
  1645.             this.clearItemContainer();
  1646.             // Create the items
  1647.             for(var i = 0, size = this.getItems().length; i < size; i++) {
  1648.                 var item = this.getItems()[i];
  1649.                 var polyline = this.getPolylines()[i];
  1650.                 var box = new ChainageGroupBoxSimple(this.getMap(), item, polyline, this.getElbow(), this.getColor(), this.isSmall());
  1651.                 // Add to array
  1652.                 this.getBoxes().push(box);
  1653.             }
  1654.             if(this.getItems().length > 1) {
  1655.                 // Create the container table
  1656.                 var table = $doc.createElement('table');
  1657.                 var direction = this.getDirection();
  1658.                 if(direction == ChainageGroupDirection.NORTH || direction == ChainageGroupDirection.SOUTH) {
  1659.                     // Setup class name
  1660.                     $wnd.jQuery(this.getItemContainer()).addClass(@com.yotta.horizon.mapping.presenters.SectionItemPresenterChainageGroup::getChainageGroupHorizontalStyleName());
  1661.  
  1662.                     // Create horizontal list of items
  1663.                     var row;
  1664.                     for(var i = 0, s = this.getBoxes().length; i < s; i++) {
  1665.                         if(i % MAX_ITEMS_PER_ROW == 0) {
  1666.                             if(row != null) {
  1667.                                 table.appendChild(row);
  1668.                             }
  1669.                             row = $doc.createElement('tr');
  1670.                         }
  1671.  
  1672.                         var cell = $doc.createElement('td');
  1673.                         cell.appendChild(this.getBoxes()[i].getElement());
  1674.                         row.appendChild(cell);
  1675.                     }
  1676.                     table.appendChild(row);
  1677.                 } else if(direction == ChainageGroupDirection.EAST || direction == ChainageGroupDirection.WEST) {
  1678.                     // Remove class name
  1679.                     $wnd.jQuery(this.getItemContainer()).removeClass(@com.yotta.horizon.mapping.presenters.SectionItemPresenterChainageGroup::getChainageGroupHorizontalStyleName());
  1680.  
  1681.                     // Create vertical list of items
  1682.                     var rows = [];
  1683.                     for(var i = 0, s = $wnd.Math.min(this.getBoxes().length, MAX_ITEMS_PER_ROW); i < s; i++) {
  1684.                         var row = $doc.createElement('tr');
  1685.                         rows.push(row);
  1686.                         table.appendChild(row);
  1687.                     }
  1688.                     for(var i = 0, s = this.getBoxes().length; i < s; i++) {
  1689.                         var row = rows[i % MAX_ITEMS_PER_ROW];
  1690.                         var cell = $doc.createElement('td');
  1691.                         cell.appendChild(this.getBoxes()[i].getElement());
  1692.                         row.appendChild(cell);
  1693.                     }
  1694.                 } else {
  1695.                     throw "Direction not handled";
  1696.                 }
  1697.                 this.getItemContainer().appendChild(table);
  1698.             }
  1699.         };
  1700.  
  1701.         // Gets the 'is small' boolean flag
  1702.         ChainageGroupBoxDrawer.prototype.isSmall = function() {
  1703.             return this.isSmall_;
  1704.         };
  1705.  
  1706.         // Adds item to the drawer
  1707.         ChainageGroupBoxDrawer.prototype.addItem = function(item, polyline) {
  1708.             this.getItems().push(item);
  1709.             this.getPolylines().push(polyline);
  1710.         };
  1711.  
  1712.         // Gets the polylines array for the items
  1713.         ChainageGroupBoxDrawer.prototype.getPolylines = function() {
  1714.             return this.polylines_;
  1715.         };
  1716.  
  1717.         // Gets the polyline elbow
  1718.         ChainageGroupBoxDrawer.prototype.getElbow = function() {
  1719.             return this.elbow_;
  1720.         };
  1721.  
  1722.         // Gets the map object
  1723.         ChainageGroupBoxDrawer.prototype.getMap = function() {
  1724.             return this.map_;
  1725.         };
  1726.  
  1727.         // Gets the DOM container
  1728.         ChainageGroupBoxDrawer.prototype.getContainer = function() {
  1729.             return this.container_;
  1730.         };
  1731.  
  1732.         // Shows the Drawer container
  1733.         ChainageGroupBoxDrawer.prototype.show = function() {
  1734.             if(this.getContainer() != null) {
  1735.                 this.getContainer().style.visibility = 'visible';
  1736.             }
  1737.         };
  1738.  
  1739.         // Hides the drawer container
  1740.         ChainageGroupBoxDrawer.prototype.hide = function() {
  1741.             if(this.getContainer() != null) {
  1742.                 this.getContainer().style.visibility = 'hidden';
  1743.             }
  1744.         };
  1745.  
  1746.         // Checks whether the drawer is visible (does not check the parent)
  1747.         ChainageGroupBoxDrawer.prototype.isVisible = function() {
  1748.             if(this.getContainer() != null) {
  1749.                 return this.getContainer().style.visibility == 'visible';
  1750.             }
  1751.             return false;
  1752.         };
  1753.  
  1754.         // Gets the DOM item container
  1755.         ChainageGroupBoxDrawer.prototype.getItemContainer = function() {
  1756.             return this.itemContainer_;
  1757.         };
  1758.  
  1759.         // Clears the DOM item container
  1760.         ChainageGroupBoxDrawer.prototype.clearItemContainer = function() {
  1761.             // Remove all items from container (should only be the table)
  1762.             if(this.getItemContainer() != null) {
  1763.                 while(this.getItemContainer().hasChildNodes()) {
  1764.                     this.getItemContainer().removeChild(this.getItemContainer().firstChild);
  1765.                 }
  1766.             }
  1767.         };
  1768.  
  1769.         // Gets all section items inside the drawer
  1770.         ChainageGroupBoxDrawer.prototype.getItems = function() {
  1771.             return this.items_;
  1772.         };
  1773.  
  1774.         // Gets direction of the chainage group
  1775.         ChainageGroupBoxDrawer.prototype.getDirection = function() {
  1776.             return this.direction_;
  1777.         };
  1778.  
  1779.         // Gets boxes that represent items
  1780.         ChainageGroupBoxDrawer.prototype.getBoxes = function() {
  1781.             return this.boxes_;
  1782.         };
  1783.  
  1784.         // Disposes of the current element and all child elements
  1785.         ChainageGroupBoxDrawer.prototype.dispose = function() {
  1786.             $wnd.console.log('ChainageGroupBoxDrawer.dispose()');
  1787.             $wnd.console.trace();
  1788.            
  1789.             if(this.getItemContainer() != null) {
  1790.                 $wnd.google.maps.event.clearInstanceListeners(this.getItemContainer());
  1791.             }
  1792.             for(var i = 0, size = this.getBoxes().length; i < size; i++) {
  1793.                 this.getBoxes()[i].dispose();
  1794.             }
  1795.             if(@com.yotta.horizon.mapping.presenters.SectionItemPresenterChainageGroup::isElbowEnabled()()) {
  1796.                 for(var i = 0, size = this.getPolylines().length; i < size; i++) {
  1797.                     this.getPolylines()[i].setMap(null);
  1798.                 }
  1799.             }
  1800.  
  1801.             this.clearItemContainer();
  1802.  
  1803.             if(this.getContainer() != null && this.getContainer().parentNode) {
  1804.                 this.getContainer().parentNode.removeChild(this.getContainer());
  1805.             }
  1806.             this.map_ = null;
  1807.             this.items_ = [];
  1808.             this.polylines_ = [];
  1809.             this.elbow_ = null;
  1810.             this.color_ = null;
  1811.             this.boxes_ = [];
  1812.             this.isSmall_ = null;
  1813.             this.sectionHeading_ = null;
  1814.         };
  1815.  
  1816.         // CONSTRUCTOR SectionItemPresenterChainageGroup
  1817.         // Constructor for section item window
  1818.         var SectionItemPresenterChainageGroup = function(map, minLevel, maxLevel, lowerChainage, upperChainage, chainagePath, isLeft) {
  1819.             this.map_ = map;
  1820.             this.chainagePath_ = chainagePath;
  1821.             this.minLevel_ = minLevel;
  1822.             this.maxLevel_ = maxLevel;
  1823.             this.lowerChainage_ = lowerChainage;
  1824.             this.upperChainage_ = upperChainage;
  1825.             this.isLeft_ = isLeft;
  1826.             this.container_ = null;
  1827.             this.itemContainer_ = null;
  1828.             this.anchorLatLng_ = null;
  1829.             this.elbowLatLng_ = null;
  1830.             this.anchor_ = null;
  1831.             this.direction_ = null;
  1832.             this.items_ = {};
  1833.             this.boxes_ = [];
  1834.             this.polyline_ = new $wnd.google.maps.Polyline($wnd.jQuery.extend({ map: map }, POLYLINE_ELBOW_DEFAULT));
  1835.             this.doUpdate_ = true;
  1836.             this.sectionHeading_ = null;
  1837.  
  1838.             // Show the window
  1839.             this.setMap(map);
  1840.         };
  1841.  
  1842.         // Set to inherit from overlay view
  1843.         SectionItemPresenterChainageGroup.prototype = new $wnd.google.maps.OverlayView();
  1844.  
  1845.         // Add overlay to map
  1846.         SectionItemPresenterChainageGroup.prototype.onAdd = function() {
  1847.             this.container_ = $doc.createElement('div');
  1848.             this.itemContainer_ = $doc.createElement('div');
  1849.  
  1850.             // Setup container
  1851.             this.getContainer().style.visibility = 'hidden';
  1852.             this.getContainer().style.position = 'absolute';
  1853.  
  1854.             // Setup the item container
  1855.             $wnd.jQuery(this.getItemContainer()).addClass(@com.yotta.horizon.mapping.presenters.SectionItemPresenterChainageGroup::getChainageGroupStyleName());
  1856.             this.getContainer().appendChild(this.getItemContainer());
  1857.  
  1858.             // Setup listener to move panel to front
  1859.             $wnd.google.maps.event.addDomListener(this.getItemContainer(), 'mouseover', itemContainerMouseOver);
  1860.  
  1861.             // Setup listeners to stop underlying polylines from mousing over
  1862.             $wnd.google.maps.event.addDomListener(this.getItemContainer(), 'click', itemContainerCancel);
  1863.             $wnd.google.maps.event.addDomListener(this.getItemContainer(), 'dblclick', itemContainerCancel);
  1864.             $wnd.google.maps.event.addDomListener(this.getItemContainer(), 'rightclick', itemContainerCancel);
  1865.             $wnd.google.maps.event.addDomListener(this.getItemContainer(), 'mousedown', itemContainerCancel);
  1866.             $wnd.google.maps.event.addDomListener(this.getItemContainer(), 'mouseup', itemContainerCancel);
  1867.             $wnd.google.maps.event.addDomListener(this.getItemContainer(), 'mousemove', itemContainerCancel);
  1868.             $wnd.google.maps.event.addDomListener(this.getItemContainer(), 'mouseout', itemContainerCancel);
  1869.             $wnd.google.maps.event.addDomListener(this.getItemContainer(), 'mouseenter', itemContainerCancel);
  1870.             $wnd.google.maps.event.addDomListener(this.getItemContainer(), 'mouseleave', itemContainerCancel);
  1871.  
  1872.             // Get the available panes to add to
  1873.             this.getPanes().floatPane.appendChild(this.getContainer());
  1874.  
  1875.             // Update on next draw
  1876.             this.setDoUpdate(true);
  1877.         };
  1878.  
  1879.         // Reposition and draw the element
  1880.         SectionItemPresenterChainageGroup.prototype.draw = function() {        
  1881.             var projection = this.getProjection();
  1882.  
  1883.             // Get start and end line points
  1884.             var chainagePathLength = this.getChainagePath().getLength();
  1885.             var chainageStartLatLng = this.getChainagePath().getAt(0);
  1886.             var chainageEndLatLng = this.getChainagePath().getAt(chainagePathLength - 1);
  1887.             var chainageStartPoint = projection.fromLatLngToDivPixel(chainageStartLatLng);
  1888.             var chainageEndPoint = projection.fromLatLngToDivPixel(chainageEndLatLng);
  1889.  
  1890.             // Get mid point
  1891.             var chainageMidPoint = new $wnd.google.maps.Point(
  1892.                 (chainageStartPoint.x + chainageEndPoint.x) / 2,
  1893.                 (chainageStartPoint.y + chainageEndPoint.y) / 2);
  1894.  
  1895.             // Get heading and adjust according to which side
  1896.             var sectionHeading = $wnd.google.maps.geometry.spherical.computeHeading(chainageStartLatLng, chainageEndLatLng);
  1897.             sectionHeading = sectionHeading >= 360 ? sectionHeading - 360 : sectionHeading < 0 ? 360 + sectionHeading : sectionHeading;
  1898.             var windowHeading = sectionHeading + (this.isLeft() ? -90 : 90);
  1899.             windowHeading = windowHeading >= 360 ? windowHeading - 360 : windowHeading < 0 ? 360 + windowHeading : windowHeading;
  1900.             var windowHeadingRadians = windowHeading * ($wnd.Math.PI / 180); // Convert to radians
  1901.  
  1902.             this.sectionHeading_ = sectionHeading;
  1903.  
  1904.             // Calculate offset point
  1905.             var offsetPoint = new $wnd.google.maps.Point(
  1906.                 chainageMidPoint.x + PIXEL_OFFSET * $wnd.Math.sin(windowHeadingRadians),
  1907.                 chainageMidPoint.y + PIXEL_OFFSET * -$wnd.Math.cos(windowHeadingRadians)
  1908.             );
  1909.             var offsetLatLng = projection.fromDivPixelToLatLng(offsetPoint);
  1910.             this.setAnchorLatLng(offsetLatLng); // Store the anchor lat lng coordinate
  1911.  
  1912.             // Calculate offset elbow point if enabled
  1913.             var offsetElbowPoint, offsetElbowLatLng;
  1914.             if(@com.yotta.horizon.mapping.presenters.SectionItemPresenterChainageGroup::isElbowEnabled()()) {
  1915.                 offsetElbowPoint = new $wnd.google.maps.Point(
  1916.                     chainageMidPoint.x + PIXEL_OFFSET_ELBOW * $wnd.Math.sin(windowHeadingRadians),
  1917.                     chainageMidPoint.y + PIXEL_OFFSET_ELBOW * -$wnd.Math.cos(windowHeadingRadians)
  1918.                 );
  1919.                 offsetElbowLatLng = projection.fromDivPixelToLatLng(offsetElbowPoint);
  1920.                 this.setElbowLatLng(offsetElbowLatLng); // Store the elbow lat lng coordinate
  1921.             }
  1922.  
  1923.             // Work out the intersecting perpendicular polyline path segment
  1924.             for(var i = 1; i < chainagePathLength; i++) {
  1925.                 var startLatLng = this.getChainagePath().getAt(i - 1);
  1926.                 var endLatLng = this.getChainagePath().getAt(i);
  1927.                 var startPoint = projection.fromLatLngToDivPixel(startLatLng);
  1928.                 var endPoint = projection.fromLatLngToDivPixel(endLatLng);
  1929.  
  1930.                 var intersectionPoint = @com.yotta.horizon.shared.gmaps.MapMath::getIntersection(Lcom/yotta/horizon/shared/gmaps/Point;Lcom/yotta/horizon/shared/gmaps/Point;Lcom/yotta/horizon/shared/gmaps/Point;Lcom/yotta/horizon/shared/gmaps/Point;ZZ)(chainageMidPoint, offsetPoint, startPoint, endPoint, true, false);
  1931.                 if(intersectionPoint != null) {
  1932.                     // Recalculate the offset and update
  1933.                     offsetPoint = new $wnd.google.maps.Point(
  1934.                         offsetPoint.x + (intersectionPoint.x - chainageMidPoint.x),
  1935.                         offsetPoint.y + (intersectionPoint.y - chainageMidPoint.y)
  1936.                     );
  1937.                     offsetLatLng = projection.fromDivPixelToLatLng(offsetPoint);
  1938.                     this.setAnchorLatLng(offsetLatLng); // Store the anchor lat lng coordinate
  1939.  
  1940.                     // Recalculate offset elbow and update if enabled
  1941.                     if(@com.yotta.horizon.mapping.presenters.SectionItemPresenterChainageGroup::isElbowEnabled()()) {
  1942.                         offsetElbowPoint = new $wnd.google.maps.Point(
  1943.                             offsetElbowPoint.x + (intersectionPoint.x - chainageMidPoint.x),
  1944.                             offsetElbowPoint.y + (intersectionPoint.y - chainageMidPoint.y)
  1945.                         );
  1946.                         offsetElbowLatLng = projection.fromDivPixelToLatLng(offsetElbowPoint);
  1947.                         this.setElbowLatLng(offsetElbowLatLng); // Store the elbow lat lng coordinate
  1948.                     }
  1949.  
  1950.                     break;
  1951.                 }
  1952.             }
  1953.  
  1954.             // Position container
  1955.             this.getContainer().style.left = offsetPoint.x + 'px';
  1956.             this.getContainer().style.top = offsetPoint.y + 'px';
  1957.  
  1958.             // Get the direction and anchor of the container
  1959.             var direction = ChainageGroupDirection.calculate(this.isLeft(), sectionHeading);
  1960.             var anchor = ChainageGroupAnchor.calculate(this.isLeft(), sectionHeading);
  1961.             this.setDirection(direction); // Store direction
  1962.             this.setAnchor(anchor); // Store anchor
  1963.  
  1964.             // Change anchor point of child
  1965.             if(anchor == ChainageGroupAnchor.TOP_LEFT) {
  1966.                 this.getItemContainer().style.bottom = null;
  1967.                 this.getItemContainer().style.right = null;
  1968.                 this.getItemContainer().style.left = '-1px';
  1969.                 this.getItemContainer().style.top = '-1px';
  1970.             } else if(anchor == ChainageGroupAnchor.TOP_RIGHT) {
  1971.                 this.getItemContainer().style.bottom = null;
  1972.                 this.getItemContainer().style.left = null;
  1973.                 this.getItemContainer().style.right = '-1px';
  1974.                 this.getItemContainer().style.top = '-1px';
  1975.             } else if(anchor == ChainageGroupAnchor.BOTTOM_LEFT) {
  1976.                 this.getItemContainer().style.top = null;
  1977.                 this.getItemContainer().style.right = null;
  1978.                 this.getItemContainer().style.left = '-1px';
  1979.                 this.getItemContainer().style.bottom = '-1px';
  1980.             } else if(anchor == ChainageGroupAnchor.BOTTOM_RIGHT) {
  1981.                 this.getItemContainer().style.top = null;
  1982.                 this.getItemContainer().style.left = null;
  1983.                 this.getItemContainer().style.right = '-1px';
  1984.                 this.getItemContainer().style.bottom = '-1px';
  1985.             } else {
  1986.                 throw "Anchor position not handled";
  1987.             }
  1988.  
  1989.             // Update the container items
  1990.             this.update();
  1991.         };
  1992.  
  1993.         // Remove overlay from map
  1994.         SectionItemPresenterChainageGroup.prototype.onRemove = function() {
  1995.             // Remove the element
  1996.             if(this.getContainer() && this.getContainer().parentNode) {
  1997.                 this.getContainer().parentNode.removeChild(this.getContainer());
  1998.  
  1999.                 // Remove listener to move panel to front
  2000.                 $wnd.google.maps.event.clearListeners(this.getItemContainer(), 'mouseover');
  2001.  
  2002.                 // Remove listeners to stop event propogation to polylines
  2003.                 $wnd.google.maps.event.clearListeners(this.getItemContainer(), 'click');
  2004.                 $wnd.google.maps.event.clearListeners(this.getItemContainer(), 'dblclick');
  2005.                 $wnd.google.maps.event.clearListeners(this.getItemContainer(), 'rightclick');
  2006.                 $wnd.google.maps.event.clearListeners(this.getItemContainer(), 'mousedown');
  2007.                 $wnd.google.maps.event.clearListeners(this.getItemContainer(), 'mouseup');
  2008.                 $wnd.google.maps.event.clearListeners(this.getItemContainer(), 'mousemove');
  2009.                 $wnd.google.maps.event.clearListeners(this.getItemContainer(), 'mouseout');
  2010.                 $wnd.google.maps.event.clearListeners(this.getItemContainer(), 'mouseenter');
  2011.                 $wnd.google.maps.event.clearListeners(this.getItemContainer(), 'mouseleave');
  2012.  
  2013.                 // Remove all items
  2014.                 this.clearItemContainer();
  2015.             }
  2016.  
  2017.             // Destroy elements if enabled
  2018.             if(@com.yotta.horizon.mapping.presenters.SectionItemPresenterChainageGroup::isElbowEnabled()()) {
  2019.                 this.getPolyline().setMap(null);
  2020.             }
  2021.             this.container_ = null;
  2022.             this.itemContainer_ = null;
  2023.  
  2024.             // Destroy global pointer to last moused over
  2025.             lastItemMousedOver = null;
  2026.         };
  2027.  
  2028.         // Method to force update the view
  2029.         SectionItemPresenterChainageGroup.prototype.update = function() {
  2030.             // Check an update is required
  2031.             if(
  2032.                 //this.getDoUpdate() == false ||
  2033.                 this.getMap() == null ||
  2034.                 this.getContainer() == null || this.getItemContainer() == null) {
  2035.                 return;
  2036.             }
  2037.  
  2038.             // Check we can show at this level and we have items
  2039.             var zoomLevel = this.getMap().getZoom();
  2040.             var itemsCount = this.getItemsCount();
  2041.             if(zoomLevel < this.getMinLevel() || zoomLevel > this.getMaxLevel() || itemsCount == 0) {
  2042.                 this.getContainer().style.visibility = 'hidden';
  2043.                
  2044.                 // TODO hide the inner polylines and objects
  2045.                 for(var i = 0, s = this.getBoxes().length; i < s; i++) {
  2046.                     var box = this.getBoxes()[i];
  2047.                     box.getElbow().setMap(null);
  2048.                     for(var j = 0, k = box.getPolylines().length; j < k; j++) {
  2049.                         var polyline = box.getPolylines()[j];
  2050.                         polyline.setMap(null);
  2051.                     }
  2052.                     //for(var c = 0, d = box.getBoxes().length; c < d; c++) {
  2053.                     //  var innerBox = box.getBoxes()[c];
  2054.                     //  for(var p = 0, q = innerBox.getPolylines().length; p < q; p++) {
  2055.                     //      var polyline = innerBox.getPolylines()[p];
  2056.                     //      polyline.setMap(null);
  2057.                     //  }
  2058.                     //}
  2059.                 }
  2060.  
  2061.                 // Hide if enabled
  2062.                 if(@com.yotta.horizon.mapping.presenters.SectionItemPresenterChainageGroup::isElbowEnabled()()) {
  2063.                     this.getPolyline().setMap(null);
  2064.                 }
  2065.  
  2066.                 //this.clearItemContainer(); // Empty the item container
  2067.                 return;
  2068.             } else {
  2069.                 this.getContainer().style.visibility = 'visible';
  2070.                
  2071.                 for(var i = 0, s = this.getBoxes().length; i < s; i++) {
  2072.                     var box = this.getBoxes()[i];
  2073.                     box.getElbow().setMap(this.map_);
  2074.                     for(var j = 0, k = box.getPolylines().length; j < k; j++) {
  2075.                         var polyline = box.getPolylines()[j];
  2076.                         polyline.setMap(this.map_);
  2077.                     }
  2078.                     //for(var c = 0, d = box.getBoxes().length; c < d; c++) {
  2079.                     //  var innerBox = box.getBoxes()[c];
  2080.                     //  for(var p = 0, q = innerBox.getPolylines().length; p < q; p++) {
  2081.                     //      var polyline = innerBox.getPolylines()[p];
  2082.                     //      polyline.setMap(this.map_);
  2083.                     //  }
  2084.                     //}
  2085.                 }
  2086.                
  2087.                 if(@com.yotta.horizon.mapping.presenters.SectionItemPresenterChainageGroup::isElbowEnabled()()) {
  2088.                     this.getPolyline().setMap(this.map_);
  2089.                 }
  2090.                
  2091.                 // TODO make visible the inner polylines and objects
  2092.             }
  2093.  
  2094.             // Update polyline to point from offset mid point to box if enabled
  2095.             if(@com.yotta.horizon.mapping.presenters.SectionItemPresenterChainageGroup::isElbowEnabled()()) {
  2096.                 this.getPolyline().setPath([this.getElbowLatLng(), this.getAnchorLatLng()]);
  2097.                 this.getPolyline().setMap(this.getMap());
  2098.                 this.getPolyline().setOptions(POLYLINE_ELBOW_DEFAULT);
  2099.             }
  2100.            
  2101.             if(this._cacheItemsCount === itemsCount) {
  2102.                 return;
  2103.             }
  2104.             this._cacheItemsCount = itemsCount;
  2105.  
  2106.             // Empty the item container
  2107.             this.clearItemContainer();
  2108.  
  2109.             // Count different item types to see how many boxes need to be created
  2110.             var codeList = [];
  2111.             for(var itemKey in this.getItems()) {
  2112.                 var item = this.getItems()[itemKey];
  2113.                 var itemCode = item.renderGroupId + "," + item.geometry.ISID + "," + item.geometry.DiagId;
  2114.                 if($wnd.jQuery.inArray(itemCode, codeList)) {
  2115.                     continue;
  2116.                 }
  2117.                 codeList.push(itemCode);
  2118.             }
  2119.             // Create the items
  2120.             for(var itemKey in this.getItems()) {
  2121.                 var item = this.getItems()[itemKey];
  2122.                 var found = false;
  2123.                 for(var i = 0, size = this.getBoxes().length; i < size; i++) {
  2124.                     var currentBox = this.getBoxes()[i];
  2125.                     var currentItem = currentBox.getItems()[0];
  2126.                     if(item.renderGroupId == currentItem.renderGroupId
  2127.                     && item.geometry.ISID == currentItem.geometry.ISID
  2128.                     && item.geometry.DiagId == currentItem.geometry.DiagId) {
  2129.                         currentBox.addItem(this.getElbowLatLng(), item);
  2130.                         found = true;
  2131.                         break;
  2132.                     }
  2133.                 }
  2134.                 if(!found) {
  2135.                     var box = new ChainageGroupBox(this.getMap(), this.getPolyline(), this.getElbowLatLng(), item, itemKey, codeList.length > 6, this.getDirection(), this.getSectionHeading());
  2136.                     // Add to array
  2137.                     this.getBoxes().push(box);
  2138.                 }
  2139.             }
  2140.  
  2141.             // Create the container table
  2142.             var table = $doc.createElement('table');
  2143.             var direction = this.getDirection();
  2144.             if(direction == ChainageGroupDirection.EAST || direction == ChainageGroupDirection.WEST) {
  2145.                 // Setup class name
  2146.                 $wnd.jQuery(this.getItemContainer()).addClass(@com.yotta.horizon.mapping.presenters.SectionItemPresenterChainageGroup::getChainageGroupHorizontalStyleName());
  2147.  
  2148.                 // Create horizontal list of items
  2149.                 var row;
  2150.                 for(var i = 0, s = this.getBoxes().length; i < s; i++) {
  2151.                     if(i % MAX_ITEMS_PER_ROW == 0) {
  2152.                         if(row != null) {
  2153.                             table.appendChild(row);
  2154.                         }
  2155.                         row = $doc.createElement('tr');
  2156.                     }
  2157.  
  2158.                     var cell = $doc.createElement('td');
  2159.                     cell.appendChild(this.getBoxes()[i].getElement());
  2160.                     row.appendChild(cell);
  2161.                 }
  2162.                 table.appendChild(row);
  2163.             } else if(direction == ChainageGroupDirection.NORTH || direction == ChainageGroupDirection.SOUTH) {
  2164.                 // Setup class name
  2165.                 $wnd.jQuery(this.getItemContainer()).removeClass(@com.yotta.horizon.mapping.presenters.SectionItemPresenterChainageGroup::getChainageGroupHorizontalStyleName());
  2166.  
  2167.                 // Create vertical list of items
  2168.                 var rows = [];
  2169.                 for(var i = 0, s = $wnd.Math.min(this.getBoxes().length, MAX_ITEMS_PER_ROW); i < s; i++) {
  2170.                     var row = $doc.createElement('tr');
  2171.                     rows.push(row);
  2172.                     table.appendChild(row);
  2173.                 }
  2174.                 for(var i = 0, s = this.getBoxes().length; i < s; i++) {
  2175.                     var row = rows[i % MAX_ITEMS_PER_ROW];
  2176.                     var cell = $doc.createElement('td');
  2177.                     cell.appendChild(this.getBoxes()[i].getElement());
  2178.                     row.appendChild(cell);
  2179.                 }
  2180.             } else {
  2181.                 throw "Direction not handled";
  2182.             }
  2183.             this.getItemContainer().appendChild(table);
  2184.  
  2185.             // Show container
  2186.             this.getContainer().style.visibility = 'visible';
  2187.             for(var i = 0, size = this.getBoxes().length; i < size; i++) {
  2188.                 var currentBox = this.getBoxes()[i];
  2189.                 for(var j = 0, boxSize = currentBox.getItems().length; j < boxSize; j++) {
  2190.                     var item = currentBox.getItems()[j];
  2191.                     @com.yotta.horizon.mapping.presenters.SectionItemPresenterChainageGroup::fireItemReady(IIZ)(item.renderGroupId, item.geometry.DSIID, false);
  2192.                 }
  2193.             }
  2194.         };
  2195.  
  2196.         // Method to add an item to the window, draw must be called
  2197.         // to update the window and position the items
  2198.         SectionItemPresenterChainageGroup.prototype.addItem = function(item) {
  2199.             if(this.containsItem(item)) {
  2200.                 return; // Cannot add again
  2201.             }
  2202.  
  2203.             var key = @com.yotta.horizon.mapping.shared.SectionItemMap::getKey(Lcom/yotta/horizon/mapping/shared/SectionItem;)(item);
  2204.             this.items_[key] = item;
  2205.  
  2206.             // Update on next draw
  2207.             this.setDoUpdate(true);
  2208.         };
  2209.  
  2210.         // Method to remove an item from the window, draw must be called
  2211.         // to update the window and position the items
  2212.         SectionItemPresenterChainageGroup.prototype.removeItem = function(item) {
  2213.             if(this.containsItem(item) == false) {
  2214.                 return; // Cannot remove if not in map
  2215.             }
  2216.  
  2217.             var key = @com.yotta.horizon.mapping.shared.SectionItemMap::getKey(Lcom/yotta/horizon/mapping/shared/SectionItem;)(item);
  2218.             delete this.items_[key];
  2219.  
  2220.             // Update on next draw
  2221.             this.setDoUpdate(true);
  2222.         };
  2223.  
  2224.         // Method to remove all items from the window, draw must be called
  2225.         // to update the window and position the items
  2226.         SectionItemPresenterChainageGroup.prototype.clearItems = function() {
  2227.             this.items_ = {};
  2228.  
  2229.             // Update on next draw
  2230.             this.setDoUpdate(true);
  2231.         };
  2232.  
  2233.         // Gets the section items currently being displayed
  2234.         SectionItemPresenterChainageGroup.prototype.getItems = function() {
  2235.             return this.items_;
  2236.         };
  2237.  
  2238.         // Gets the number of items currently in the map
  2239.         SectionItemPresenterChainageGroup.prototype.getItemsCount = function() {
  2240.             var count = 0;
  2241.             for(var k in this.items_) {
  2242.                 if(this.items_.hasOwnProperty(k)) {
  2243.                     ++count;
  2244.                 }
  2245.             }
  2246.             return count;
  2247.         };
  2248.  
  2249.         // Gets whether to perform a ui update on next draw
  2250.         SectionItemPresenterChainageGroup.prototype.getDoUpdate = function() {
  2251.             return this.doUpdate_;
  2252.         };
  2253.  
  2254.         SectionItemPresenterChainageGroup.prototype.getSectionHeading = function() {
  2255.             return this.sectionHeading_;
  2256.         };
  2257.  
  2258.         // Sets whether to perform a ui update on next draw
  2259.         SectionItemPresenterChainageGroup.prototype.setDoUpdate = function(doUpdate) {
  2260.             this.doUpdate_ = doUpdate;
  2261.         };
  2262.  
  2263.         // Gets whether the window contains a section item
  2264.         SectionItemPresenterChainageGroup.prototype.containsItem = function(item) {
  2265.             var key = @com.yotta.horizon.mapping.shared.SectionItemMap::getKey(Lcom/yotta/horizon/mapping/shared/SectionItem;)(item);
  2266.             return key in this.items_;
  2267.         };
  2268.  
  2269.         // Gets the chainage path
  2270.         SectionItemPresenterChainageGroup.prototype.getChainagePath = function() {
  2271.             return this.chainagePath_;
  2272.         };
  2273.  
  2274.         // Gets whether the window sits on the left or right
  2275.         SectionItemPresenterChainageGroup.prototype.isLeft = function() {
  2276.             return this.isLeft_;
  2277.         };
  2278.  
  2279.         // Gets the container for the window
  2280.         SectionItemPresenterChainageGroup.prototype.getContainer = function() {
  2281.             return this.container_;
  2282.         };
  2283.  
  2284.         // Gets the container for section items
  2285.         SectionItemPresenterChainageGroup.prototype.getItemContainer = function() {
  2286.             return this.itemContainer_;
  2287.         };
  2288.  
  2289.         // Gets the polyline that joins the box to the origin
  2290.         SectionItemPresenterChainageGroup.prototype.getPolyline = function() {
  2291.             return this.polyline_;
  2292.         };
  2293.  
  2294.         // Gets the anchor points lat lng coordinate
  2295.         SectionItemPresenterChainageGroup.prototype.getAnchorLatLng = function() {
  2296.             return this.anchorLatLng_;
  2297.         };
  2298.  
  2299.         // Sets the anchor points lat lng coordinate
  2300.         SectionItemPresenterChainageGroup.prototype.setAnchorLatLng = function(latLng) {
  2301.             this.anchorLatLng_ = latLng;
  2302.  
  2303.             // Update on next draw
  2304.             this.setDoUpdate(true);
  2305.         };
  2306.  
  2307.         // Gets the elbow points lat lng coordinate
  2308.         SectionItemPresenterChainageGroup.prototype.getElbowLatLng = function() {
  2309.             return this.elbowLatLng_;
  2310.         };
  2311.  
  2312.         // Sets the elbow points lat lng coordinate
  2313.         SectionItemPresenterChainageGroup.prototype.setElbowLatLng = function(latLng) {
  2314.             this.elbowLatLng_ = latLng;
  2315.  
  2316.             // Update on next draw
  2317.             this.setDoUpdate(true);
  2318.         };
  2319.  
  2320.         // Gets the anchor enumeration
  2321.         SectionItemPresenterChainageGroup.prototype.getAnchor = function() {
  2322.             return this.anchor_;
  2323.         };
  2324.  
  2325.         // Sets the anchor enumeration
  2326.         SectionItemPresenterChainageGroup.prototype.setAnchor = function(anchor) {
  2327.             this.anchor_ = anchor;
  2328.  
  2329.             // Update on next draw
  2330.             this.setDoUpdate(true);
  2331.         };
  2332.  
  2333.         // Gets the direction enumeration
  2334.         SectionItemPresenterChainageGroup.prototype.getDirection = function() {
  2335.             return this.direction_;
  2336.         };
  2337.  
  2338.         // Sets the direction enumeration
  2339.         SectionItemPresenterChainageGroup.prototype.setDirection = function(direction) {
  2340.             this.direction_ = direction;
  2341.  
  2342.             // Update on next draw
  2343.             this.setDoUpdate(true);
  2344.         };
  2345.  
  2346.         // Gets the box instances
  2347.         SectionItemPresenterChainageGroup.prototype.getBoxes = function() {
  2348.             return this.boxes_;
  2349.         };
  2350.  
  2351.         // Gets the min zoom range the window displays for
  2352.         SectionItemPresenterChainageGroup.prototype.getMinLevel = function() {
  2353.             return this.minLevel_;
  2354.         };
  2355.  
  2356.         // Gets the max zoom ranget the window displays for
  2357.         SectionItemPresenterChainageGroup.prototype.getMaxLevel = function() {
  2358.             return this.maxLevel_;
  2359.         };
  2360.  
  2361.         // Gets the lower chainage value of the path
  2362.         SectionItemPresenterChainageGroup.prototype.getLowerChainage = function() {
  2363.             return this.lowerChainage_;
  2364.         };
  2365.  
  2366.         // Gets the upper chainage value of the path
  2367.         SectionItemPresenterChainageGroup.prototype.getUpperChainage = function() {
  2368.             return this.upperChainage_;
  2369.         };
  2370.  
  2371.         // Method to clear the item container of all elements
  2372.         SectionItemPresenterChainageGroup.prototype.clearItemContainer = function() {
  2373.             // Unset all items (unset variables where necessary)
  2374.             for(var i = 0, s = this.getBoxes().length; i < s; i++) {
  2375.                 this.getBoxes()[i].dispose();
  2376.             }
  2377.  
  2378.             // Reset boxes
  2379.             this.boxes_ = [];
  2380.  
  2381.             // Remove all items from container (should only be the table)
  2382.             while(this.getItemContainer().hasChildNodes()) {
  2383.                 this.getItemContainer().removeChild(this.getItemContainer().firstChild);
  2384.             }
  2385.         };
  2386.  
  2387.         // Dispose of the control
  2388.         SectionItemPresenterChainageGroup.prototype.dispose = function() {
  2389.             this.setMap(null);
  2390.  
  2391.             this.map_ = null;
  2392.             this.chainagePath_ = null;
  2393.             this.minLevel_ = null;
  2394.             this.maxLevel_ = null;
  2395.             this.lowerChainage_ = null;
  2396.             this.upperChainage_ = null;
  2397.             this.isLeft_ = null;
  2398.             this.container_ = null;
  2399.             this.itemContainer_ = null;
  2400.             this.anchorLatLng_ = null;
  2401.             this.elbowLatLng_ = null;
  2402.             this.anchor_ = null;
  2403.             this.direction_ = null;
  2404.             this.items_ = null;
  2405.             this.boxes_ = null;
  2406.             this.polyline_ = null;
  2407.         };
  2408.  
  2409.         return SectionItemPresenterChainageGroup;
  2410.     }-*/;
  2411.  
  2412.     /**
  2413.      * Checks whether section items are styled and whether they listen to
  2414.      * mouseover/out events
  2415.      * @return a flag indicating whether to style section items
  2416.      */
  2417.     private static final boolean isSectionItemsLinked() {
  2418.         return HorizonConfigFlags.INSTANCE.stylesSectionItems();
  2419.     }
  2420.  
  2421.     /**
  2422.      * Checks whether the elbow drawing routine is enabled
  2423.      * @return a flag indicating if the polylines joining items to chaninage
  2424.      *         groups are enabled
  2425.      */
  2426.     private static final boolean isElbowEnabled() {
  2427.         return HorizonConfigFlags.INSTANCE.stylesElbow();
  2428.     }
  2429.  
  2430.     /**
  2431.      * Checks whether a section item is selected
  2432.      * @param item the item to check
  2433.      * @return a flag indicating selecetion
  2434.      */
  2435.     private static final boolean isSectionItemSelected(SectionItem item) {
  2436.         return item.isSelected();
  2437.     }
  2438.  
  2439. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement