1 /*
  2  * HomePane.js
  3  *
  4  * Sweet Home 3D, Copyright (c) 2024 Space Mushrooms <[email protected]>
  5  *
  6  * This program is free software; you can redistribute it and/or modify
  7  * it under the terms of the GNU General Public License as published by
  8  * the Free Software Foundation; either version 2 of the License, or
  9  * (at your option) any later version.
 10  *
 11  * This program is distributed in the hope that it will be useful,
 12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
 13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 14  * GNU General Public License for more details.
 15  *
 16  * You should have received a copy of the GNU General Public License
 17  * along with this program; if not, write to the Free Software
 18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 19  */
 20 
 21 // Requires toolkit.js
 22 
 23 /**
 24  * Creates home view associated with its controller.
 25  * @param {Home} home
 26  * @param {UserPreferences} preferences
 27  * @param {HomeController} controller
 28  * @constructor
 29  * @author Emmanuel Puybaret
 30  * @author Renaud Pawlak
 31  * @author Louis Grignon 
 32  */
 33 function HomePane(containerId, home, preferences, controller) {
 34   if (containerId != null) {
 35     this.container = document.getElementById(containerId);
 36   }
 37   if (!this.container) {
 38     this.container = document.body;
 39   }
 40   this.home = home;
 41   this.preferences = preferences;
 42   this.controller = controller;
 43   this.clipboardEmpty = true;
 44   this.actionMap = {};
 45   this.inputMap = {};
 46   this.transferHandlerEnabled = false;
 47 
 48   this.createActions(home, preferences, controller);
 49   this.initActions(preferences);
 50   this.addHomeListener(home);
 51   this.addLevelVisibilityListener(home);
 52   this.addUserPreferencesListener(preferences);
 53   this.addPlanControllerListener(controller.getPlanController());
 54   this.addFocusListener();
 55   this.createToolBar(home, preferences, controller);
 56   this.createPopupMenus(home, preferences);
 57   this.initSplitters();
 58   this.addOrientationChangeListener();
 59   
 60   // Additional implementation for Sweet Home 3D JS
 61   
 62   // Keyboard accelerators management
 63   var homePane = this;
 64   this.keydownListener = function(ev) {
 65       if (JSDialog.getTopMostDialog() !== null) {
 66         // ignore keystrokes when dialog is displayed
 67         return;
 68       }
 69 
 70       var keyStroke = KeyStroke.getKeyStrokeForEvent(ev);
 71       if (keyStroke !== undefined) {
 72         // Search action matching shortcut and call its actionPerformed method
 73         for (var actionType in homePane.actionMap) {
 74           var action = homePane.actionMap [actionType];
 75           if (action instanceof AbstractAction
 76               && action.isEnabled()
 77               && action.getValue(AbstractAction.ACCELERATOR_KEY) == keyStroke) {
 78             action.actionPerformed();
 79             ev.stopPropagation();
 80             return;
 81           }
 82         }
 83         // Search other actions in input map
 84         var actionKey = homePane.inputMap [keyStroke];
 85         if (actionKey !== undefined) {
 86           var action = homePane.actionMap [actionKey];
 87           if (action !== undefined) {
 88             action.actionPerformed(ev);
 89           }
 90           ev.stopPropagation();
 91         }
 92       }
 93     };
 94   document.addEventListener("keydown", this.keydownListener, false);
 95 
 96   var planComponent = controller.getPlanController().getView();
 97   if (planComponent != null) {
 98     // Restore viewport position if it exists
 99     var viewportX = home.getNumericProperty(HomePane.PLAN_VIEWPORT_X_VISUAL_PROPERTY);
100     var viewportY = home.getNumericProperty(HomePane.PLAN_VIEWPORT_Y_VISUAL_PROPERTY);
101     if (viewportX != null && viewportY != null) {
102       planComponent.scrollPane.scrollLeft = viewportX | 0;
103       planComponent.scrollPane.scrollTop = viewportY | 0;
104     }
105 
106     planComponent.scrollPane.addEventListener("scroll", function(ev) {
107         controller.setHomeProperty(HomePane.PLAN_VIEWPORT_X_VISUAL_PROPERTY, planComponent.scrollPane.scrollLeft.toString());
108         controller.setHomeProperty(HomePane.PLAN_VIEWPORT_Y_VISUAL_PROPERTY, planComponent.scrollPane.scrollTop.toString());
109       });
110   } 
111 
112   // Create level selector
113   this.levelSelector = document.getElementById("level-selector");
114   var levelsChangeListener = function() {
115       if (homePane.levelSelector) {
116         homePane.levelSelector.innerHTML = "";
117         if (home.getLevels().length < 2) {
118           homePane.levelSelector.style.display = "none";
119         } else {
120           for (var i = 0; i < home.getLevels().length; i++) {
121             var option = document.createElement("option");
122             option.value = i;
123             option.innerHTML = home.getLevels()[i].getName();
124             if (home.getLevels()[i] === home.getSelectedLevel()) {
125               option.selected = "selected";
126             }
127             homePane.levelSelector.appendChild(option);
128           }
129           homePane.levelSelector.style.display = "inline";
130         }
131       }
132     };
133   levelsChangeListener();
134   if (this.levelSelector) {
135     this.levelSelectorChangeListener = function(ev) {
136         controller.getPlanController().setSelectedLevel(home.getLevels()[parseInt(ev.target.value)]);
137         levelsChangeListener();
138       };
139     this.levelSelector.addEventListener("change", this.levelSelectorChangeListener);
140   }
141   home.addPropertyChangeListener("SELECTED_LEVEL", levelsChangeListener);
142   var levelChangeListener = function(ev) {
143       if ("NAME" == ev.getPropertyName()
144           || "ELEVATION" == ev.getPropertyName()
145           || "ELEVATION_INDEX" == ev.getPropertyName()) {
146         levelsChangeListener();
147       }
148     };
149   var levels = home.getLevels();
150   for (var i = 0; i < levels.length; i++) {
151     levels[i].addPropertyChangeListener(levelChangeListener);
152   }
153   home.addLevelsListener(function(ev) {
154       if (ev.getType() === CollectionEvent.Type.ADD) {
155         ev.getItem().addPropertyChangeListener(levelChangeListener);
156       } else if (ev.getType() === CollectionEvent.Type.DELETE) {
157         ev.getItem().removePropertyChangeListener(levelChangeListener);
158       }
159       levelsChangeListener();
160     });
161     
162   setTimeout(function() {
163       // Give default focus to the plan or the 3D view
164       if (planComponent != null) {
165         planComponent.getHTMLElement().focus();
166       } else if (controller.getHomeController3D().getView() != null) {
167         controller.getHomeController3D().getView().getHTMLElement().focus();
168       }
169     });
170 }
171 HomePane["__class"] = "HomePane";
172 HomePane["__interfaces"] = ["com.eteks.sweethome3d.viewcontroller.HomeView", "com.eteks.sweethome3d.viewcontroller.View"];
173 
174 HomePane.MAIN_PANE_DIVIDER_LOCATION_VISUAL_PROPERTY = "com.eteks.sweethome3d.SweetHome3D.MainPaneDividerLocation";
175 HomePane.CATALOG_PANE_DIVIDER_LOCATION_VISUAL_PROPERTY = "com.eteks.sweethome3d.SweetHome3D.CatalogPaneDividerLocation";
176 HomePane.PLAN_PANE_DIVIDER_LOCATION_VISUAL_PROPERTY = "com.eteks.sweethome3d.SweetHome3D.PlanPaneDividerLocation";
177 HomePane.PLAN_VIEWPORT_X_VISUAL_PROPERTY = "com.eteks.sweethome3d.SweetHome3D.PlanViewportX";
178 HomePane.PLAN_VIEWPORT_Y_VISUAL_PROPERTY = "com.eteks.sweethome3d.SweetHome3D.PlanViewportY";
179 HomePane.FURNITURE_VIEWPORT_Y_VISUAL_PROPERTY = "com.eteks.sweethome3d.SweetHome3D.FurnitureViewportY";
180 HomePane.DETACHED_VIEW_VISUAL_PROPERTY = ".detachedView";
181 HomePane.DETACHED_VIEW_DIVIDER_LOCATION_VISUAL_PROPERTY = ".detachedViewDividerLocation";
182 HomePane.DETACHED_VIEW_X_VISUAL_PROPERTY = ".detachedViewX";
183 HomePane.DETACHED_VIEW_Y_VISUAL_PROPERTY = ".detachedViewY";
184 HomePane.DETACHED_VIEW_WIDTH_VISUAL_PROPERTY = ".detachedViewWidth";
185 HomePane.DETACHED_VIEW_HEIGHT_VISUAL_PROPERTY = ".detachedViewHeight";
186 
187 /**
188  * @private
189  */
190 HomePane.MenuActionType = {};
191 HomePane.MenuActionType[HomePane.MenuActionType["FILE_MENU"] = 0] = "FILE_MENU";
192 HomePane.MenuActionType[HomePane.MenuActionType["EDIT_MENU"] = 1] = "EDIT_MENU";
193 HomePane.MenuActionType[HomePane.MenuActionType["FURNITURE_MENU"] = 2] = "FURNITURE_MENU";
194 HomePane.MenuActionType[HomePane.MenuActionType["PLAN_MENU"] = 3] = "PLAN_MENU";
195 HomePane.MenuActionType[HomePane.MenuActionType["VIEW_3D_MENU"] = 4] = "VIEW_3D_MENU";
196 HomePane.MenuActionType[HomePane.MenuActionType["HELP_MENU"] = 5] = "HELP_MENU";
197 HomePane.MenuActionType[HomePane.MenuActionType["OPEN_RECENT_HOME_MENU"] = 6] = "OPEN_RECENT_HOME_MENU";
198 HomePane.MenuActionType[HomePane.MenuActionType["ALIGN_OR_DISTRIBUTE_MENU"] = 7] = "ALIGN_OR_DISTRIBUTE_MENU";
199 HomePane.MenuActionType[HomePane.MenuActionType["SORT_HOME_FURNITURE_MENU"] = 8] = "SORT_HOME_FURNITURE_MENU";
200 HomePane.MenuActionType[HomePane.MenuActionType["DISPLAY_HOME_FURNITURE_PROPERTY_MENU"] = 9] = "DISPLAY_HOME_FURNITURE_PROPERTY_MENU";
201 HomePane.MenuActionType[HomePane.MenuActionType["MODIFY_TEXT_STYLE"] = 10] = "MODIFY_TEXT_STYLE";
202 HomePane.MenuActionType[HomePane.MenuActionType["GO_TO_POINT_OF_VIEW"] = 11] = "GO_TO_POINT_OF_VIEW";
203 HomePane.MenuActionType[HomePane.MenuActionType["SELECT_OBJECT_MENU"] = 12] = "SELECT_OBJECT_MENU";
204 HomePane.MenuActionType[HomePane.MenuActionType["TOGGLE_SELECTION_MENU"] = 13] = "TOGGLE_SELECTION_MENU";
205 
206 
207 /**
208  * Returns the HTML element used to view this component at screen.
209  */
210 HomePane.prototype.getHTMLElement = function() {
211   return this.container;
212 }
213 
214 /**
215  * Returns the actions map registered with this component.
216  */
217 HomePane.prototype.getActionMap = function() {
218   return this.actionMap;
219 }
220 
221 /**
222  * Convenient shortcut method to access an action.
223  * @param {string|HomeView.ActionType} actionType
224  * @private
225  */
226 HomePane.prototype.getAction = function(actionType) {
227   if (typeof actionType === "string") {
228     return this.actionMap[actionType];
229   } else {
230     return this.actionMap[HomeView.ActionType[actionType]];
231   }
232 }
233 
234 /**
235  * Create the actions map of this component.
236  * @param {Home} home
237  * @param {UserPreferences} preferences
238  * @param {HomeController} controller
239  * @private
240  */
241 HomePane.prototype.createActions = function(home, preferences, controller) {
242   var ActionType = HomeView.ActionType;
243   this.createAction(ActionType.NEW_HOME, preferences, controller, "newHome");
244   this.createAction(ActionType.NEW_HOME_FROM_EXAMPLE, preferences, controller, "newHomeFromExample");
245   this.createAction(ActionType.OPEN, preferences, controller, "open");
246   this.createAction(ActionType.DELETE_RECENT_HOMES, preferences, controller, "deleteRecentHomes");
247   this.createAction(ActionType.CLOSE, preferences, controller, "close");
248   this.createAction(ActionType.SAVE, preferences, controller, "save");
249   this.createAction(ActionType.SAVE_AS, preferences, controller, "saveAs");
250   this.createAction(ActionType.SAVE_AND_COMPRESS, preferences, controller, "saveAndCompress");
251   this.createAction(ActionType.PAGE_SETUP, preferences, controller, "setupPage");
252   this.createAction(ActionType.PRINT_PREVIEW, preferences, controller, "previewPrint");
253   this.createAction(ActionType.PRINT, preferences, controller, "print");
254   this.createAction(ActionType.PRINT_TO_PDF, preferences, controller, "printToPDF");
255   this.createAction(ActionType.PREFERENCES, preferences, controller, "editPreferences");
256   this.createAction(ActionType.EXIT, preferences, controller, "exit");
257 
258   this.createAction(ActionType.UNDO, preferences, controller, "undo");
259   this.createAction(ActionType.REDO, preferences, controller, "redo");
260   this.createClipboardAction(ActionType.CUT, preferences, null, true);
261   this.createClipboardAction(ActionType.COPY, preferences, null, true);
262   this.createClipboardAction(ActionType.PASTE, preferences, null, false);
263   this.createAction(ActionType.PASTE_TO_GROUP, preferences, controller, "pasteToGroup");
264   this.createAction(ActionType.PASTE_STYLE, preferences, controller, "pasteStyle");
265   this.createAction(ActionType.DELETE, preferences, controller, "delete");
266   this.createAction(ActionType.SELECT_ALL, preferences, controller, "selectAll");
267 
268   this.createAction(ActionType.ADD_HOME_FURNITURE, preferences, controller, "addHomeFurniture");
269   this.createAction(ActionType.ADD_FURNITURE_TO_GROUP, preferences, controller, "addFurnitureToGroup");
270   var furnitureController = controller.getFurnitureController();
271   this.createAction(ActionType.DELETE_HOME_FURNITURE, preferences, furnitureController, "deleteSelection");
272   this.createAction(ActionType.MODIFY_FURNITURE, preferences, controller, "modifySelectedFurniture");
273   this.createAction(ActionType.GROUP_FURNITURE, preferences, furnitureController, "groupSelectedFurniture");
274   this.createAction(ActionType.UNGROUP_FURNITURE, preferences, furnitureController, "ungroupSelectedFurniture");
275   this.createAction(ActionType.ALIGN_FURNITURE_ON_TOP, preferences, furnitureController, "alignSelectedFurnitureOnTop");
276   this.createAction(ActionType.ALIGN_FURNITURE_ON_BOTTOM, preferences, furnitureController, "alignSelectedFurnitureOnBottom");
277   this.createAction(ActionType.ALIGN_FURNITURE_ON_LEFT, preferences, furnitureController, "alignSelectedFurnitureOnLeft");
278   this.createAction(ActionType.ALIGN_FURNITURE_ON_RIGHT, preferences, furnitureController, "alignSelectedFurnitureOnRight");
279   this.createAction(ActionType.ALIGN_FURNITURE_ON_FRONT_SIDE, preferences, furnitureController, "alignSelectedFurnitureOnFrontSide");
280   this.createAction(ActionType.ALIGN_FURNITURE_ON_BACK_SIDE, preferences, furnitureController, "alignSelectedFurnitureOnBackSide");
281   this.createAction(ActionType.ALIGN_FURNITURE_ON_LEFT_SIDE, preferences, furnitureController, "alignSelectedFurnitureOnLeftSide");
282   this.createAction(ActionType.ALIGN_FURNITURE_ON_RIGHT_SIDE, preferences, furnitureController, "alignSelectedFurnitureOnRightSide");
283   this.createAction(ActionType.ALIGN_FURNITURE_SIDE_BY_SIDE, preferences, furnitureController, "alignSelectedFurnitureSideBySide");
284   this.createAction(ActionType.DISTRIBUTE_FURNITURE_HORIZONTALLY, preferences, furnitureController, "distributeSelectedFurnitureHorizontally");
285   this.createAction(ActionType.DISTRIBUTE_FURNITURE_VERTICALLY, preferences, furnitureController, "distributeSelectedFurnitureVertically");
286   this.createAction(ActionType.RESET_FURNITURE_ELEVATION, preferences, furnitureController, "resetFurnitureElevation");
287   var homeController3D = controller.getHomeController3D();
288   if (homeController3D.getView() != null) {
289     this.createAction(ActionType.IMPORT_FURNITURE, preferences, controller, "importFurniture");
290   }
291   this.createAction(ActionType.IMPORT_FURNITURE_LIBRARY, preferences, controller, "importFurnitureLibrary");
292   this.createAction(ActionType.IMPORT_TEXTURE, preferences, controller, "importTexture");
293   this.createAction(ActionType.IMPORT_TEXTURES_LIBRARY, preferences, controller, "importTexturesLibrary");
294   this.createAction(ActionType.SORT_HOME_FURNITURE_BY_CATALOG_ID, preferences,
295       furnitureController, "toggleFurnitureSort", "CATALOG_ID");
296   this.createAction(ActionType.SORT_HOME_FURNITURE_BY_NAME, preferences,
297       furnitureController, "toggleFurnitureSort", "NAME");
298   this.createAction(ActionType.SORT_HOME_FURNITURE_BY_DESCRIPTION, preferences,
299       furnitureController, "toggleFurnitureSort", "DESCRIPTION");
300   this.createAction(ActionType.SORT_HOME_FURNITURE_BY_CREATOR, preferences,
301       furnitureController, "toggleFurnitureSort", "CREATOR");
302   this.createAction(ActionType.SORT_HOME_FURNITURE_BY_LICENSE, preferences,
303       furnitureController, "toggleFurnitureSort", "LICENSE");
304   this.createAction(ActionType.SORT_HOME_FURNITURE_BY_WIDTH, preferences,
305       furnitureController, "toggleFurnitureSort", "WIDTH");
306   this.createAction(ActionType.SORT_HOME_FURNITURE_BY_DEPTH, preferences,
307       furnitureController, "toggleFurnitureSort", "DEPTH");
308   this.createAction(ActionType.SORT_HOME_FURNITURE_BY_HEIGHT, preferences,
309       furnitureController, "toggleFurnitureSort", "HEIGHT");
310   this.createAction(ActionType.SORT_HOME_FURNITURE_BY_X, preferences,
311       furnitureController, "toggleFurnitureSort", "X");
312   this.createAction(ActionType.SORT_HOME_FURNITURE_BY_Y, preferences,
313       furnitureController, "toggleFurnitureSort", "Y");
314   this.createAction(ActionType.SORT_HOME_FURNITURE_BY_ELEVATION, preferences,
315       furnitureController, "toggleFurnitureSort", "ELEVATION");
316   this.createAction(ActionType.SORT_HOME_FURNITURE_BY_ANGLE, preferences,
317       furnitureController, "toggleFurnitureSort", "ANGLE");
318   this.createAction(ActionType.SORT_HOME_FURNITURE_BY_LEVEL, preferences,
319       furnitureController, "toggleFurnitureSort", "LEVEL");
320   this.createAction(ActionType.SORT_HOME_FURNITURE_BY_MODEL_SIZE, preferences,
321       furnitureController, "toggleFurnitureSort", "MODEL_SIZE");
322   this.createAction(ActionType.SORT_HOME_FURNITURE_BY_COLOR, preferences,
323       furnitureController, "toggleFurnitureSort", "COLOR");
324   this.createAction(ActionType.SORT_HOME_FURNITURE_BY_TEXTURE, preferences,
325       furnitureController, "toggleFurnitureSort", "TEXTURE");
326   this.createAction(ActionType.SORT_HOME_FURNITURE_BY_MOVABILITY, preferences,
327       furnitureController, "toggleFurnitureSort", "MOVABLE");
328   this.createAction(ActionType.SORT_HOME_FURNITURE_BY_TYPE, preferences,
329       furnitureController, "toggleFurnitureSort", "DOOR_OR_WINDOW");
330   this.createAction(ActionType.SORT_HOME_FURNITURE_BY_VISIBILITY, preferences,
331       furnitureController, "toggleFurnitureSort", "VISIBLE");
332   this.createAction(ActionType.SORT_HOME_FURNITURE_BY_PRICE, preferences,
333       furnitureController, "toggleFurnitureSort", "PRICE");
334   this.createAction(ActionType.SORT_HOME_FURNITURE_BY_VALUE_ADDED_TAX_PERCENTAGE, preferences,
335       furnitureController, "toggleFurnitureSort", "VALUE_ADDED_TAX_PERCENTAGE");
336   this.createAction(ActionType.SORT_HOME_FURNITURE_BY_VALUE_ADDED_TAX, preferences,
337       furnitureController, "toggleFurnitureSort", "VALUE_ADDED_TAX");
338   this.createAction(ActionType.SORT_HOME_FURNITURE_BY_PRICE_VALUE_ADDED_TAX_INCLUDED, preferences,
339       furnitureController, "toggleFurnitureSort", "PRICE_VALUE_ADDED_TAX_INCLUDED");
340   this.createAction(ActionType.SORT_HOME_FURNITURE_BY_DESCENDING_ORDER, preferences,
341       furnitureController, "toggleFurnitureSortOrder");
342   this.createAction(ActionType.DISPLAY_HOME_FURNITURE_CATALOG_ID, preferences,
343       furnitureController, "toggleFurnitureVisibleProperty", "CATALOG_ID");
344   this.createAction(ActionType.DISPLAY_HOME_FURNITURE_NAME, preferences,
345       furnitureController, "toggleFurnitureVisibleProperty", "NAME");
346   this.createAction(ActionType.DISPLAY_HOME_FURNITURE_DESCRIPTION, preferences,
347       furnitureController, "toggleFurnitureVisibleProperty", "DESCRIPTION");
348   this.createAction(ActionType.DISPLAY_HOME_FURNITURE_CREATOR, preferences,
349       furnitureController, "toggleFurnitureVisibleProperty", "CREATOR");
350   this.createAction(ActionType.DISPLAY_HOME_FURNITURE_LICENSE, preferences,
351       furnitureController, "toggleFurnitureVisibleProperty", "LICENSE");
352   this.createAction(ActionType.DISPLAY_HOME_FURNITURE_WIDTH, preferences,
353       furnitureController, "toggleFurnitureVisibleProperty", "WIDTH");
354   this.createAction(ActionType.DISPLAY_HOME_FURNITURE_DEPTH, preferences,
355       furnitureController, "toggleFurnitureVisibleProperty", "DEPTH");
356   this.createAction(ActionType.DISPLAY_HOME_FURNITURE_HEIGHT, preferences,
357       furnitureController, "toggleFurnitureVisibleProperty", "HEIGHT");
358   this.createAction(ActionType.DISPLAY_HOME_FURNITURE_X, preferences,
359       furnitureController, "toggleFurnitureVisibleProperty", "X");
360   this.createAction(ActionType.DISPLAY_HOME_FURNITURE_Y, preferences,
361       furnitureController, "toggleFurnitureVisibleProperty", "Y");
362   this.createAction(ActionType.DISPLAY_HOME_FURNITURE_ELEVATION, preferences,
363       furnitureController, "toggleFurnitureVisibleProperty", "ELEVATION");
364   this.createAction(ActionType.DISPLAY_HOME_FURNITURE_ANGLE, preferences,
365       furnitureController, "toggleFurnitureVisibleProperty", "ANGLE");
366   this.createAction(ActionType.DISPLAY_HOME_FURNITURE_LEVEL, preferences,
367       furnitureController, "toggleFurnitureVisibleProperty", "LEVEL");
368   this.createAction(ActionType.DISPLAY_HOME_FURNITURE_MODEL_SIZE, preferences,
369       furnitureController, "toggleFurnitureVisibleProperty", "MODEL_SIZE");
370   this.createAction(ActionType.DISPLAY_HOME_FURNITURE_COLOR, preferences,
371       furnitureController, "toggleFurnitureVisibleProperty", "COLOR");
372   this.createAction(ActionType.DISPLAY_HOME_FURNITURE_TEXTURE, preferences,
373       furnitureController, "toggleFurnitureVisibleProperty", "TEXTURE");
374   this.createAction(ActionType.DISPLAY_HOME_FURNITURE_MOVABLE, preferences,
375       furnitureController, "toggleFurnitureVisibleProperty", "MOVABLE");
376   this.createAction(ActionType.DISPLAY_HOME_FURNITURE_DOOR_OR_WINDOW, preferences,
377       furnitureController, "toggleFurnitureVisibleProperty", "DOOR_OR_WINDOW");
378   this.createAction(ActionType.DISPLAY_HOME_FURNITURE_VISIBLE, preferences,
379       furnitureController, "toggleFurnitureVisibleProperty", "VISIBLE");
380   this.createAction(ActionType.DISPLAY_HOME_FURNITURE_PRICE, preferences,
381       furnitureController, "toggleFurnitureVisibleProperty", "PRICE");
382   this.createAction(ActionType.DISPLAY_HOME_FURNITURE_VALUE_ADDED_TAX_PERCENTAGE, preferences,
383       furnitureController, "toggleFurnitureVisibleProperty", "VALUE_ADDED_TAX_PERCENTAGE");
384   this.createAction(ActionType.DISPLAY_HOME_FURNITURE_VALUE_ADDED_TAX, preferences,
385       furnitureController, "toggleFurnitureVisibleProperty", "VALUE_ADDED_TAX");
386   this.createAction(ActionType.DISPLAY_HOME_FURNITURE_PRICE_VALUE_ADDED_TAX_INCLUDED, preferences,
387       furnitureController, "toggleFurnitureVisibleProperty", "PRICE_VALUE_ADDED_TAX_INCLUDED");
388   this.createAction(ActionType.EXPORT_TO_CSV, preferences, controller, "exportToCSV");
389 
390   var planController = controller.getPlanController();
391   if (planController.getView() != null) {
392     this.createAction(ActionType.SELECT_ALL_AT_ALL_LEVELS, preferences, planController, "selectAllAtAllLevels");
393     var modeGroup = [];
394     this.createToggleAction(ActionType.SELECT, planController.getMode() == PlanController.Mode.SELECTION, modeGroup,
395         preferences, controller, "setMode", PlanController.Mode.SELECTION);
396     this.createToggleAction(ActionType.PAN, planController.getMode() == PlanController.Mode.PANNING, modeGroup,
397         preferences, controller, "setMode", PlanController.Mode.PANNING);
398     this.createToggleAction(ActionType.CREATE_WALLS, planController.getMode() == PlanController.Mode.WALL_CREATION, modeGroup,
399         preferences, controller, "setMode", PlanController.Mode.WALL_CREATION);
400     this.createToggleAction(ActionType.CREATE_ROOMS, planController.getMode() == PlanController.Mode.ROOM_CREATION, modeGroup,
401         preferences, controller, "setMode", PlanController.Mode.ROOM_CREATION);
402     this.createToggleAction(ActionType.CREATE_POLYLINES, planController.getMode() == PlanController.Mode.POLYLINE_CREATION, modeGroup,
403         preferences, controller, "setMode", PlanController.Mode.POLYLINE_CREATION);
404     this.createToggleAction(ActionType.CREATE_DIMENSION_LINES, planController.getMode() == PlanController.Mode.DIMENSION_LINE_CREATION, modeGroup,
405         preferences, controller, "setMode", PlanController.Mode.DIMENSION_LINE_CREATION);
406     this.createToggleAction(ActionType.CREATE_LABELS, planController.getMode() == PlanController.Mode.LABEL_CREATION, modeGroup,
407         preferences, controller, "setMode", PlanController.Mode.LABEL_CREATION);
408     this.createAction(ActionType.DELETE_SELECTION, preferences, planController, "deleteSelection");
409     this.createAction(ActionType.LOCK_BASE_PLAN, preferences, planController, "lockBasePlan");
410     this.createAction(ActionType.UNLOCK_BASE_PLAN, preferences, planController, "unlockBasePlan");
411     this.createAction(ActionType.ENABLE_MAGNETISM, preferences, controller, "enableMagnetism");
412     this.createAction(ActionType.DISABLE_MAGNETISM, preferences, controller, "disableMagnetism");
413     this.createAction(ActionType.FLIP_HORIZONTALLY, preferences, planController, "flipHorizontally");
414     this.createAction(ActionType.FLIP_VERTICALLY, preferences, planController, "flipVertically");
415     this.createAction(ActionType.MODIFY_COMPASS, preferences, planController, "modifyCompass");
416     this.createAction(ActionType.MODIFY_WALL, preferences, planController, "modifySelectedWalls");
417     this.createAction(ActionType.MODIFY_ROOM, preferences, planController, "modifySelectedRooms");
418     this.createAction(ActionType.JOIN_WALLS, preferences, planController, "joinSelectedWalls");
419     this.createAction(ActionType.REVERSE_WALL_DIRECTION, preferences, planController, "reverseSelectedWallsDirection");
420     this.createAction(ActionType.SPLIT_WALL, preferences, planController, "splitSelectedWall");
421     // ADD_ROOM_POINT and DELETE_ROOM_POINT actions are actually defined later in updateRoomActions
422     this.createAction(ActionType.ADD_ROOM_POINT, preferences);
423     this.createAction(ActionType.DELETE_ROOM_POINT, preferences);
424     this.createAction(ActionType.MODIFY_POLYLINE, preferences, planController, "modifySelectedPolylines");
425     this.createAction(ActionType.MODIFY_DIMENSION_LINE, preferences, planController, "modifySelectedDimensionLines");
426     this.createAction(ActionType.MODIFY_LABEL, preferences, planController, "modifySelectedLabels");
427     this.createAction(ActionType.INCREASE_TEXT_SIZE, preferences, planController, "increaseTextSize");
428     this.createAction(ActionType.DECREASE_TEXT_SIZE, preferences, planController, "decreaseTextSize");
429     // Use special toggle models for bold and italic check box menu items and tool bar buttons
430     // that are selected texts in home selected items are all bold or italic
431     var toggleBoldAction = this.createBoldStyleAction(ActionType.TOGGLE_BOLD_STYLE, home, preferences, planController, "toggleBoldStyle");
432     var toggleItalicAction = this.createItalicStyleToggleModel(ActionType.TOGGLE_ITALIC_STYLE, home, preferences, planController, "toggleItalicStyle");
433     this.createAction(ActionType.IMPORT_BACKGROUND_IMAGE, preferences, controller, "importBackgroundImage");
434     this.createAction(ActionType.MODIFY_BACKGROUND_IMAGE, preferences, controller, "modifyBackgroundImage");
435     this.createAction(ActionType.HIDE_BACKGROUND_IMAGE, preferences, controller, "hideBackgroundImage");
436     this.createAction(ActionType.SHOW_BACKGROUND_IMAGE, preferences, controller, "showBackgroundImage");
437     this.createAction(ActionType.DELETE_BACKGROUND_IMAGE, preferences, controller, "deleteBackgroundImage");
438     this.createAction(ActionType.ADD_LEVEL, preferences, planController, "addLevel");
439     this.createAction(ActionType.ADD_LEVEL_AT_SAME_ELEVATION, preferences, planController, "addLevelAtSameElevation");
440     this.createAction(ActionType.MAKE_LEVEL_VIEWABLE, preferences, planController, "toggleSelectedLevelViewability");
441     this.createAction(ActionType.MAKE_LEVEL_UNVIEWABLE, preferences, planController, "toggleSelectedLevelViewability");
442     this.createAction(ActionType.MAKE_LEVEL_ONLY_VIEWABLE_ONE, preferences, planController, "setSelectedLevelOnlyViewable");
443     this.createAction(ActionType.MAKE_ALL_LEVELS_VIEWABLE, preferences, planController, "setAllLevelsViewable");
444     this.createAction(ActionType.MODIFY_LEVEL, preferences, planController, "modifySelectedLevel");
445     this.createAction(ActionType.DELETE_LEVEL, preferences, planController, "deleteSelectedLevel");
446     this.createAction(ActionType.ZOOM_IN, preferences, controller, "zoomIn");
447     this.createAction(ActionType.ZOOM_OUT, preferences, controller, "zoomOut");
448     this.createAction(ActionType.EXPORT_TO_SVG, preferences, controller, "exportToSVG");
449   }
450 
451   if (homeController3D.getView() != null) {
452     // SELECT_OBJECT and TOGGLE_SELECTION actions are actually defined later in updatePickingActions
453     this.createAction(ActionType.SELECT_OBJECT, preferences);
454     this.createAction(ActionType.TOGGLE_SELECTION, preferences);
455     var viewGroup = [];
456     this.createToggleAction(ActionType.VIEW_FROM_TOP, home.getCamera() == home.getTopCamera(), viewGroup,
457         preferences, homeController3D, "viewFromTop");
458     this.createToggleAction(ActionType.VIEW_FROM_OBSERVER, home.getCamera() == home.getObserverCamera(), viewGroup,
459         preferences, homeController3D, "viewFromObserver");
460     this.createAction(ActionType.MODIFY_OBSERVER, preferences, planController, "modifyObserverCamera");
461     this.createAction(ActionType.STORE_POINT_OF_VIEW, preferences, controller, "storeCamera");
462     this.createAction(ActionType.DELETE_POINTS_OF_VIEW, preferences, controller, "deleteCameras");
463     this.createAction(ActionType.DETACH_3D_VIEW, preferences, controller, "detachView", controller.getHomeController3D().getView());
464     this.createAction(ActionType.ATTACH_3D_VIEW, preferences, controller, "attachView", controller.getHomeController3D().getView());
465 
466     var allLevelsVisible = home.getEnvironment().isAllLevelsVisible();
467     var displayLevelGroup = [];
468     this.createToggleAction(ActionType.DISPLAY_ALL_LEVELS, allLevelsVisible, displayLevelGroup, preferences,
469         homeController3D, "displayAllLevels");
470     this.createToggleAction(ActionType.DISPLAY_SELECTED_LEVEL, !allLevelsVisible, displayLevelGroup, preferences,
471         homeController3D, "displaySelectedLevel");
472     this.createAction(ActionType.MODIFY_3D_ATTRIBUTES, preferences, homeController3D, "modifyAttributes");
473     this.createAction(ActionType.CREATE_PHOTO, preferences, controller, "createPhoto");
474     this.createAction(ActionType.CREATE_PHOTOS_AT_POINTS_OF_VIEW, preferences, controller, "createPhotos");
475     this.createAction(ActionType.CREATE_VIDEO, preferences, controller, "createVideo");
476     this.createAction(ActionType.EXPORT_TO_OBJ, preferences, controller, "exportToOBJ");
477   }
478 
479   this.createAction(ActionType.HELP, preferences, controller, "help");
480   this.createAction(ActionType.ABOUT, preferences, controller, "about");
481 
482   // Additional action for application popup menu 
483   var showApplicationMenuAction = new ResourceAction(preferences, "HomePane", "SHOW_APPLICATION_MENU", true);
484   if (showApplicationMenuAction.getValue(AbstractAction.SMALL_ICON) == null) {
485     showApplicationMenuAction.putValue(AbstractAction.NAME, "SHOW_APPLICATION_MENU");
486     showApplicationMenuAction.putValue(AbstractAction.SMALL_ICON, "menu.png");
487   }
488   var homePane = this; 
489   showApplicationMenuAction.actionPerformed = function(ev) {
490       ev.stopPropagation();
491       ev.preventDefault();
492       var planElement = controller.getPlanController().getView().getHTMLElement();
493       var contextMenuEvent = document.createEvent("Event");
494       contextMenuEvent.initEvent("contextmenu", true, true);
495       contextMenuEvent.clientX = homePane.showApplicationMenuButton.clientX + homePane.showApplicationMenuButton.clientWidth / 2;
496       contextMenuEvent.clientY = homePane.showApplicationMenuButton.clientY + homePane.showApplicationMenuButton.clientHeight / 2;
497       homePane.showApplicationMenuButton.dispatchEvent(contextMenuEvent);
498     };
499   this.getActionMap()["SHOW_APPLICATION_MENU"] = showApplicationMenuAction;
500 }
501   
502 /**
503  * Returns a new <code>ControllerAction</code> object that calls on <code>controller</code> a given
504  * <code>method</code> with its <code>parameters</code>. This action is added to the action map of this component.
505  * @param {HomeView.ActionType} actionType
506  * @param {UserPreferences} preferences
507  * @param {Object} controller
508  * @param {string} method
509  * @param {...Object} parameters
510  * @return {Object}
511  * @private
512  */
513 HomePane.prototype.createAction = function(actionType, preferences, controller, method) {
514   var parameters = [];
515   for (var i = 4; i < arguments.length; i++) {
516     parameters[i - 4] = arguments[i];
517   }
518   try {
519     var action = new ResourceAction(preferences, HomePane, HomeView.ActionType[actionType], false, controller, method, parameters);
520     this.getActionMap()[HomeView.ActionType[actionType]] = action;
521     return action;
522   } catch (ex) {
523     console.log(ex);
524   }
525 }
526 
527 /**
528  * Returns a new <code>ControllerAction</code> object associated with other actions if the same <code>group</code>.
529  * @param {HomeView.ActionType} actionType
530  * @param {boolean} selected
531  * @param {string} group
532  * @param {UserPreferences} preferences
533  * @param {Object} controller
534  * @param {string} method
535  * @param {...Object} parameters
536  * @return {Object}
537  * @private
538  */
539 HomePane.prototype.createToggleAction = function(actionType, selected, group, preferences, controller, method) {
540   var parameters = [];
541   for (var i = 6; i < arguments.length; i++) {
542     parameters[i - 6] = arguments[i];
543   }
544   var action = this.createAction.apply(this, [actionType, preferences, controller, method].concat(parameters));
545   if (group != null) {
546     group.push(action);
547     action.putValue(ResourceAction.TOGGLE_BUTTON_GROUP, group);
548     action.putValue(AbstractAction.SELECTED_KEY, selected);
549     action.addPropertyChangeListener(function(ev) {
550         if (ev.getPropertyName() == AbstractAction.SELECTED_KEY) {
551           if (ev.getNewValue()) {
552             var group = ev.getSource().getValue(ResourceAction.TOGGLE_BUTTON_GROUP);
553             for (var i = 0; i < group.length; i++) {
554               if (action !== group [i] 
555                   && group [i].getValue(AbstractAction.SELECTED_KEY)) {
556                 group [i].putValue(AbstractAction.SELECTED_KEY, false);
557               }
558             }
559           } else {
560             ev.getSource().putValue(AbstractAction.SELECTED_KEY, false);
561           }
562         }
563       })
564   }
565   return action;
566 }
567 
568 /**
569  * Creates a <code>ReourceAction</code> object that calls
570  * <code>actionPerfomed</code> method on a given
571  * existing <code>clipboardAction</code> with a source equal to focused component.
572  * @param {HomeView.ActionType} actionType
573  * @param {UserPreferences} preferences
574  * @param {Object} clipboardAction
575  * @param {boolean} copyAction
576  * @private
577  */
578 HomePane.prototype.createClipboardAction = function(actionType, preferences, clipboardAction, copyAction) {
579   var action = this.createAction(actionType, preferences);
580   var homePane = this;
581   action.actionPerformed = function(ev) {
582     if (copyAction) {
583       homePane.clipboard = Home.duplicate(homePane.home.getSelectedItems());
584       homePane.clipboardEmpty = false;
585       homePane.controller.enablePasteAction();
586     }
587     switch (actionType) {
588       case HomeView.ActionType.CUT:
589         homePane.controller.cut(homePane.home.getSelectedItems());
590         break;
591       case HomeView.ActionType.COPY:
592         break;
593       case HomeView.ActionType.PASTE:
594         homePane.controller.paste(Home.duplicate(homePane.clipboard));
595         break;
596     }
597     return action;
598   }
599 }
600 
601 /**
602  * Creates a <code>ResourceAction</code> for the given menu action type.
603  * @private
604  */
605 HomePane.prototype.getMenuAction = function(actionType) {
606   return new ResourceAction(this.preferences, HomePane, HomePane.MenuActionType[actionType], true);
607 }
608 
609 /**
610  * Adds a property change listener to <code>home</code> to update
611  * View from top and View from observer toggle models according to used camera.
612  * @param {Home} home
613  * @private
614  */
615 HomePane.prototype.addHomeListener = function(home) {
616   var homePane = this;
617   home.addPropertyChangeListener("CAMERA", function(ev) {
618       homePane.setToggleButtonModelSelected(HomeView.ActionType.VIEW_FROM_TOP, home.getCamera() == home.getTopCamera());
619       homePane.setToggleButtonModelSelected(HomeView.ActionType.VIEW_FROM_OBSERVER, home.getCamera() == home.getObserverCamera());
620     });
621 }
622 
623 /**
624  * Changes the selection of the toggle model matching the given action.
625  * @param {HomeView.ActionType} actionType
626  * @param {boolean} selected
627  * @private
628  */
629 HomePane.prototype.setToggleButtonModelSelected = function(actionType, selected) {
630   this.getAction(actionType).putValue(AbstractAction.SELECTED_KEY, selected);
631 }
632 
633 /**
634  * Adds listener to <code>home</code> to update
635  * Display all levels and Display selected level toggle models
636  * according their visibility.
637  * @param {Home} home
638  * @private
639  */
640 HomePane.prototype.addLevelVisibilityListener = function(home) {
641   var homePane = this;
642   home.getEnvironment().addPropertyChangeListener("ALL_LEVELS_VISIBLE", function(ev) {
643       var allLevelsVisible = home.getEnvironment().isAllLevelsVisible();
644       homePane.setToggleButtonModelSelected(HomeView.ActionType.DISPLAY_ALL_LEVELS, allLevelsVisible);
645       homePane.setToggleButtonModelSelected(HomeView.ActionType.DISPLAY_SELECTED_LEVEL, !allLevelsVisible);
646     });
647 }
648 
649 /**
650  * Adds a property change listener to <code>preferences</code> to update
651  * actions when some preferences change.
652  * @param {UserPreferences} preferences
653  * @private
654  */
655 HomePane.prototype.addUserPreferencesListener = function(preferences) {
656   this.preferencesChangeListener = new HomePane.UserPreferencesChangeListener(this);
657   preferences.addPropertyChangeListener("CURRENCY", this.preferencesChangeListener);
658   preferences.addPropertyChangeListener("VALUE_ADDED_TAX_ENABLED", this.preferencesChangeListener);
659 }
660 
661 /**
662  * Preferences property listener bound to this component with a weak reference to avoid
663  * strong link between preferences and this component.
664  * @param {HomePane} homePane
665  * @constructor
666  * @ignore
667  */
668 HomePane.UserPreferencesChangeListener = function(homePane) {
669   // TODO Manage weak reference ?
670   this.homePane = homePane;
671 }
672 
673 HomePane.UserPreferencesChangeListener.prototype.propertyChange = function(ev) {
674   var ActionType = HomeView.ActionType;
675   var homePane = this.homePane;
676   var preferences = ev.getSource();
677   var property = ev.getPropertyName();
678   if (homePane == null) {
679     preferences.removePropertyChangeListener(property, this);
680   } else {
681     switch (property) {
682       case "CURRENCY":
683         homePane.getAction(ActionType.DISPLAY_HOME_FURNITURE_PRICE).putValue(ResourceAction.VISIBLE, ev.getNewValue() != null);
684         homePane.getAction(ActionType.SORT_HOME_FURNITURE_BY_PRICE).putValue(ResourceAction.VISIBLE, ev.getNewValue() != null);
685         break;
686       case "VALUE_ADDED_TAX_ENABLED":
687         homePane.getAction(ActionType.DISPLAY_HOME_FURNITURE_VALUE_ADDED_TAX_PERCENTAGE).putValue(ResourceAction.VISIBLE, ev.getNewValue() == true);
688         homePane.getAction(ActionType.DISPLAY_HOME_FURNITURE_VALUE_ADDED_TAX).putValue(ResourceAction.VISIBLE, ev.getNewValue() == true);
689         homePane.getAction(ActionType.DISPLAY_HOME_FURNITURE_PRICE_VALUE_ADDED_TAX_INCLUDED).putValue(ResourceAction.VISIBLE, ev.getNewValue() == true);
690         homePane.getAction(ActionType.SORT_HOME_FURNITURE_BY_VALUE_ADDED_TAX_PERCENTAGE).putValue(ResourceAction.VISIBLE, ev.getNewValue() == true);
691         homePane.getAction(ActionType.SORT_HOME_FURNITURE_BY_VALUE_ADDED_TAX).putValue(ResourceAction.VISIBLE, ev.getNewValue() == true);
692         homePane.getAction(ActionType.SORT_HOME_FURNITURE_BY_PRICE_VALUE_ADDED_TAX_INCLUDED).putValue(ResourceAction.VISIBLE, ev.getNewValue() == true);
693         break;
694     }
695   }
696 }
697 
698 /**
699  * Sets whether some actions should be visible or not.
700  * @param {UserPreferences} preferences
701  * @private
702  */
703 HomePane.prototype.initActions = function(preferences) {
704   this.getAction(HomeView.ActionType.DISPLAY_HOME_FURNITURE_CATALOG_ID).putValue(ResourceAction.VISIBLE, false);
705   this.getAction(HomeView.ActionType.SORT_HOME_FURNITURE_BY_CATALOG_ID).putValue(ResourceAction.VISIBLE, false);
706   this.getAction(HomeView.ActionType.DISPLAY_HOME_FURNITURE_PRICE).putValue(ResourceAction.VISIBLE, preferences.getCurrency() != null);
707   this.getAction(HomeView.ActionType.SORT_HOME_FURNITURE_BY_PRICE).putValue(ResourceAction.VISIBLE, preferences.getCurrency() != null);
708   this.getAction(HomeView.ActionType.DISPLAY_HOME_FURNITURE_VALUE_ADDED_TAX_PERCENTAGE).putValue(ResourceAction.VISIBLE, preferences.isValueAddedTaxEnabled());
709   this.getAction(HomeView.ActionType.DISPLAY_HOME_FURNITURE_VALUE_ADDED_TAX).putValue(ResourceAction.VISIBLE, preferences.isValueAddedTaxEnabled());
710   this.getAction(HomeView.ActionType.DISPLAY_HOME_FURNITURE_PRICE_VALUE_ADDED_TAX_INCLUDED).putValue(ResourceAction.VISIBLE, preferences.isValueAddedTaxEnabled());
711   this.getAction(HomeView.ActionType.SORT_HOME_FURNITURE_BY_VALUE_ADDED_TAX_PERCENTAGE).putValue(ResourceAction.VISIBLE, preferences.isValueAddedTaxEnabled());
712   this.getAction(HomeView.ActionType.SORT_HOME_FURNITURE_BY_VALUE_ADDED_TAX).putValue(ResourceAction.VISIBLE, preferences.isValueAddedTaxEnabled());
713   this.getAction(HomeView.ActionType.SORT_HOME_FURNITURE_BY_PRICE_VALUE_ADDED_TAX_INCLUDED).putValue(ResourceAction.VISIBLE, preferences.isValueAddedTaxEnabled());
714 }
715 
716 /**
717  * Adds a property change listener to <code>planController</code> to update
718  * Select and Create walls toggle models according to current mode.
719  * @param {PlanController} planController
720  * @private
721  */
722 HomePane.prototype.addPlanControllerListener = function(planController) {
723   var homePane = this;
724   planController.addPropertyChangeListener("MODE", function(ev) {
725       var mode = planController.getMode();
726       homePane.setToggleButtonModelSelected(HomeView.ActionType.SELECT, mode == PlanController.Mode.SELECTION);
727       homePane.setToggleButtonModelSelected(HomeView.ActionType.PAN, mode == PlanController.Mode.PANNING);
728       homePane.setToggleButtonModelSelected(HomeView.ActionType.CREATE_WALLS, mode == PlanController.Mode.WALL_CREATION);
729       homePane.setToggleButtonModelSelected(HomeView.ActionType.CREATE_ROOMS, mode == PlanController.Mode.ROOM_CREATION);
730       homePane.setToggleButtonModelSelected(HomeView.ActionType.CREATE_POLYLINES, mode == PlanController.Mode.POLYLINE_CREATION);
731       homePane.setToggleButtonModelSelected(HomeView.ActionType.CREATE_DIMENSION_LINES, mode == PlanController.Mode.DIMENSION_LINE_CREATION);
732       homePane.setToggleButtonModelSelected(HomeView.ActionType.CREATE_LABELS, mode == PlanController.Mode.LABEL_CREATION);
733     });
734 }
735   
736 /**
737  * Adds a focus change listener to report to controller focus changes.
738  * @private
739  */
740 HomePane.prototype.addFocusListener = function() {
741   var homePane = this; 
742   this.focusListener = function(ev) {
743       // Manage focus only for plan and component 3D to simplify actions choice proposed to the user
744       var focusableViews = [// homePane.controller.getFurnitureCatalogController().getView(),
745                             // homePane.controller.getFurnitureController().getView(),
746                             homePane.controller.getPlanController().getView(),
747                             homePane.controller.getHomeController3D().getView()];
748       for (var i = 0; i < focusableViews.length; i++) {
749         for (var element = ev.target; element !== null; element = element.parentElement) {
750           if (element === focusableViews [i].getHTMLElement()) {
751             homePane.controller.focusedViewChanged(focusableViews [i]);
752             return;
753           }
754         }
755       }
756     };
757   this.getHTMLElement().addEventListener("focusin", this.focusListener);
758 }
759 
760 /**
761  * Adds the given action to <code>menu</code>.
762  * @param {string|HomeView.ActionType} actionType
763  * @param {Object} menuBuilder
764  * @private
765  */
766 HomePane.prototype.addActionToMenu = function(actionType, menuBuilder) {
767   var action = this.getAction(actionType);
768   if (action != null && action.getValue(AbstractAction.NAME) != null) {
769     menuBuilder.addMenuItem(action);
770   }
771 }
772 
773 /**
774  * Builds align or distribute menu.
775  */
776 HomePane.prototype.createAlignOrDistributeMenu = function(builder) {
777   var ActionType = HomeView.ActionType;
778   var homePane = this;
779   builder.addSubMenu(homePane.getMenuAction(HomePane.MenuActionType.ALIGN_OR_DISTRIBUTE_MENU), 
780       function(builder) {
781         homePane.addActionToMenu(ActionType.ALIGN_FURNITURE_ON_TOP, builder);
782         homePane.addActionToMenu(ActionType.ALIGN_FURNITURE_ON_BOTTOM, builder);
783         homePane.addActionToMenu(ActionType.ALIGN_FURNITURE_ON_LEFT, builder);
784         homePane.addActionToMenu(ActionType.ALIGN_FURNITURE_ON_RIGHT, builder);
785         homePane.addActionToMenu(ActionType.ALIGN_FURNITURE_ON_FRONT_SIDE, builder);
786         homePane.addActionToMenu(ActionType.ALIGN_FURNITURE_ON_BACK_SIDE, builder);
787         homePane.addActionToMenu(ActionType.ALIGN_FURNITURE_ON_LEFT_SIDE, builder);
788         homePane.addActionToMenu(ActionType.ALIGN_FURNITURE_ON_RIGHT_SIDE, builder);
789         homePane.addActionToMenu(ActionType.ALIGN_FURNITURE_SIDE_BY_SIDE, builder);
790         homePane.addActionToMenu(ActionType.DISTRIBUTE_FURNITURE_HORIZONTALLY, builder);
791         homePane.addActionToMenu(ActionType.DISTRIBUTE_FURNITURE_VERTICALLY, builder);
792       });
793 }
794 
795 /**
796  * Builds furniture sort menu.
797  */
798 HomePane.prototype.createFurnitureSortMenu = function(home, builder) {
799   var ActionType = HomeView.ActionType;
800   var homePane = this;
801   builder.addSubMenu(homePane.getMenuAction(HomePane.MenuActionType.SORT_HOME_FURNITURE_MENU), 
802       function(builder) {
803         /**
804          * @param {HomeView.ActionType} type
805          * @param {string} sortableProperty
806          */
807         var addItem = function(type, sortableProperty) {
808             var action = homePane.getAction(type);
809             if (action && action.getValue(AbstractAction.NAME) && action.getValue(ResourceAction.VISIBLE)) {
810               builder.addRadioButtonItem(action.getValue(AbstractAction.NAME), function () {
811                   action.actionPerformed();
812                 }, sortableProperty == home.getFurnitureSortedProperty());
813             }
814           };
815   
816         addItem(ActionType.SORT_HOME_FURNITURE_BY_CATALOG_ID, "CATALOG_ID");
817         addItem(ActionType.SORT_HOME_FURNITURE_BY_NAME, "NAME");
818         addItem(ActionType.SORT_HOME_FURNITURE_BY_DESCRIPTION, "DESCRIPTION");
819         addItem(ActionType.SORT_HOME_FURNITURE_BY_CREATOR, "CREATOR");
820         addItem(ActionType.SORT_HOME_FURNITURE_BY_LICENSE, "LICENSE");
821         addItem(ActionType.SORT_HOME_FURNITURE_BY_WIDTH, "WIDTH");
822         addItem(ActionType.SORT_HOME_FURNITURE_BY_DEPTH, "DEPTH");
823         addItem(ActionType.SORT_HOME_FURNITURE_BY_HEIGHT, "HEIGHT");
824         addItem(ActionType.SORT_HOME_FURNITURE_BY_X, "X");
825         addItem(ActionType.SORT_HOME_FURNITURE_BY_Y, "Y");
826         addItem(ActionType.SORT_HOME_FURNITURE_BY_ELEVATION, "ELEVATION");
827         addItem(ActionType.SORT_HOME_FURNITURE_BY_ANGLE, "ANGLE");
828         addItem(ActionType.SORT_HOME_FURNITURE_BY_LEVEL, "LEVEL");
829         addItem(ActionType.SORT_HOME_FURNITURE_BY_MODEL_SIZE, "MODEL_SIZE");
830         addItem(ActionType.SORT_HOME_FURNITURE_BY_COLOR, "COLOR");
831         addItem(ActionType.SORT_HOME_FURNITURE_BY_TEXTURE, "TEXTURE");
832         addItem(ActionType.SORT_HOME_FURNITURE_BY_MOVABILITY, "MOVABLE");
833         addItem(ActionType.SORT_HOME_FURNITURE_BY_TYPE, "DOOR_OR_WINDOW");
834         addItem(ActionType.SORT_HOME_FURNITURE_BY_VISIBILITY, "VISIBLE");
835         addItem(ActionType.SORT_HOME_FURNITURE_BY_PRICE, "PRICE");
836         addItem(ActionType.SORT_HOME_FURNITURE_BY_VALUE_ADDED_TAX_PERCENTAGE, "VALUE_ADDED_TAX_PERCENTAGE");
837         addItem(ActionType.SORT_HOME_FURNITURE_BY_VALUE_ADDED_TAX, "VALUE_ADDED_TAX");
838         addItem(ActionType.SORT_HOME_FURNITURE_BY_PRICE_VALUE_ADDED_TAX_INCLUDED, "PRICE_VALUE_ADDED_TAX_INCLUDED");
839         builder.addSeparator();
840         var descSortAction = homePane.getAction(ActionType.SORT_HOME_FURNITURE_BY_DESCENDING_ORDER);
841         if (descSortAction && descSortAction.getValue(AbstractAction.NAME) && descSortAction.getValue(ResourceAction.VISIBLE)) {
842           builder.addCheckBoxItem(descSortAction.getValue(AbstractAction.NAME), function () {
843               descSortAction.actionPerformed();
844             }, 
845             home.isFurnitureDescendingSorted());
846         }
847       });
848 }
849 
850 /**
851  * Builds furniture display property menu.
852  */
853 HomePane.prototype.createFurnitureDisplayPropertyMenu = function(home, builder) {
854   var ActionType = HomeView.ActionType;
855   var homePane = this;
856   builder.addSubMenu(homePane.getMenuAction(HomePane.MenuActionType.DISPLAY_HOME_FURNITURE_PROPERTY_MENU), 
857       function(builder) {
858         /**
859          * @param {HomeView.ActionType} type
860          * @param {string} sortableProperty
861          */
862         var addItem = function(type, sortableProperty) {
863             var action = homePane.getAction(type);
864             if (action && action.getValue(AbstractAction.NAME) && action.getValue(ResourceAction.VISIBLE)) {
865               builder.addCheckBoxItem(action.getValue(AbstractAction.NAME), function(){
866                   action.actionPerformed();
867                 }, home.getFurnitureVisibleProperties().indexOf(sortableProperty) > -1);
868             }
869           };
870   
871         addItem(ActionType.DISPLAY_HOME_FURNITURE_CATALOG_ID, "CATALOG_ID");
872         addItem(ActionType.DISPLAY_HOME_FURNITURE_NAME, "NAME");
873         addItem(ActionType.DISPLAY_HOME_FURNITURE_DESCRIPTION, "DESCRIPTION");
874         addItem(ActionType.DISPLAY_HOME_FURNITURE_CREATOR, "CREATOR");
875         addItem(ActionType.DISPLAY_HOME_FURNITURE_LICENSE, "LICENSE");
876         addItem(ActionType.DISPLAY_HOME_FURNITURE_WIDTH, "WIDTH");
877         addItem(ActionType.DISPLAY_HOME_FURNITURE_DEPTH, "DEPTH");
878         addItem(ActionType.DISPLAY_HOME_FURNITURE_HEIGHT, "HEIGHT");
879         addItem(ActionType.DISPLAY_HOME_FURNITURE_X, "X");
880         addItem(ActionType.DISPLAY_HOME_FURNITURE_Y, "Y");
881         addItem(ActionType.DISPLAY_HOME_FURNITURE_ELEVATION, "ELEVATION");
882         addItem(ActionType.DISPLAY_HOME_FURNITURE_ANGLE, "ANGLE");
883         addItem(ActionType.DISPLAY_HOME_FURNITURE_LEVEL, "LEVEL");
884         addItem(ActionType.DISPLAY_HOME_FURNITURE_MODEL_SIZE, "MODEL_SIZE");
885         addItem(ActionType.DISPLAY_HOME_FURNITURE_COLOR, "COLOR");
886         addItem(ActionType.DISPLAY_HOME_FURNITURE_TEXTURE, "TEXTURE");
887         addItem(ActionType.DISPLAY_HOME_FURNITURE_MOVABLE, "MOVABLE");
888         addItem(ActionType.DISPLAY_HOME_FURNITURE_DOOR_OR_WINDOW, "DOOR_OR_WINDOW");
889         addItem(ActionType.DISPLAY_HOME_FURNITURE_VISIBLE, "VISIBLE");
890         addItem(ActionType.DISPLAY_HOME_FURNITURE_PRICE, "PRICE");
891         addItem(ActionType.DISPLAY_HOME_FURNITURE_VALUE_ADDED_TAX_PERCENTAGE, "VALUE_ADDED_TAX_PERCENTAGE");
892         addItem(ActionType.DISPLAY_HOME_FURNITURE_VALUE_ADDED_TAX, "VALUE_ADDED_TAX");
893         addItem(ActionType.DISPLAY_HOME_FURNITURE_PRICE_VALUE_ADDED_TAX_INCLUDED, "PRICE_VALUE_ADDED_TAX_INCLUDED");
894       });
895 }
896 
897 /**
898  * Returns Lock / Unlock base plan button.
899  * @param {Home} home
900  * @param {string} additionalClass additional CSS class
901  * @return {HTMLButton}
902  * @private
903  */
904 HomePane.prototype.createLockUnlockBasePlanButton = function(home, additionalClass) {
905   var unlockBasePlanAction = this.getAction(HomeView.ActionType.UNLOCK_BASE_PLAN);
906   var lockBasePlanAction = this.getAction(HomeView.ActionType.LOCK_BASE_PLAN);
907   if (unlockBasePlanAction != null 
908       && unlockBasePlanAction.getValue(AbstractAction.NAME) != null 
909       && lockBasePlanAction.getValue(AbstractAction.NAME) != null) {
910     var lockUnlockBasePlanButton = this.createToolBarButton(
911         home.isBasePlanLocked() ? unlockBasePlanAction : lockBasePlanAction, additionalClass);
912     home.addPropertyChangeListener("BASE_PLAN_LOCKED", function() {
913         lockUnlockBasePlanButton.setAction(home.isBasePlanLocked()
914             ? unlockBasePlanAction
915             : lockBasePlanAction);
916       });
917     return lockUnlockBasePlanButton;
918   }
919   else {
920     return null;
921   }
922 }
923 
924 /**
925  * Returns Enable / Disable magnetism button.
926  * @param {UserPreferences} preferences
927  * @param {string} additionalClass additional CSS class
928  * @return {HTMLButton}
929  * @private
930  */
931 HomePane.prototype.createEnableDisableMagnetismButton = function(preferences, additionalClass) {
932   var disableMagnetismAction = this.getAction(HomeView.ActionType.DISABLE_MAGNETISM);
933   var enableMagnetismAction = this.getAction(HomeView.ActionType.ENABLE_MAGNETISM);
934   if (disableMagnetismAction !== null
935       && disableMagnetismAction.getValue(AbstractAction.NAME) !== null
936       && enableMagnetismAction.getValue(AbstractAction.NAME) !== null) {
937     var enableDisableMagnetismButton = this.createToolBarButton(
938         preferences.isMagnetismEnabled() ? disableMagnetismAction : enableMagnetismAction, additionalClass);
939     preferences.addPropertyChangeListener("MAGNETISM_ENABLED",
940         new HomePane.MagnetismChangeListener(this, enableDisableMagnetismButton));
941     return enableDisableMagnetismButton;
942   } else {
943     return null;
944   }
945 }
946 
947 /**
948  * Preferences property listener bound to this component with a weak reference to avoid
949  * strong link between preferences and this component.
950  * @constructor
951  * @private
952  */
953 HomePane.MagnetismChangeListener = function MagnetismChangeListener(homePane, enableDisableMagnetismButton) {
954   this.enableDisableMagnetismButton = enableDisableMagnetismButton;
955   this.homePane = homePane;
956 }
957 
958 /**
959  * @ignore
960  */
961 HomePane.MagnetismChangeListener.prototype.propertyChange = function(ev) {
962   var homePane = this.homePane;
963   var preferences = ev.getSource();
964   var property = ev.getPropertyName();
965   if (homePane == null) {
966     preferences.removePropertyChangeListener(property, this);
967   } else {
968     this.enableDisableMagnetismButton.setAction(
969         preferences.isMagnetismEnabled()
970             ? homePane.getAction(HomeView.ActionType.DISABLE_MAGNETISM)
971             : homePane.getAction(HomeView.ActionType.ENABLE_MAGNETISM));
972   }
973 }
974 
975 /**
976  * Creates an action that is selected when all the text of the
977  * selected items in <code>home</code> use bold style.
978  * @param {HomeView.ActionType} actionType
979  * @param {Home} home
980  * @param {UserPreferences} preferences
981  * @param {Object} controller
982  * @param {string} method
983  * @return {ResourceAction}
984  * @private
985  */
986 HomePane.prototype.createBoldStyleAction = function(actionType, home, preferences, controller, method) {
987   var action = this.createAction(actionType, preferences, controller, method);
988   home.addSelectionListener({
989       selectionChanged: function(ev) {
990         // Find if selected items are all bold or not
991         var selectionBoldStyle = null;
992         var selectedItems = home.getSelectedItems();
993         for (var i = 0; i < selectedItems.length; i++) {
994           item = selectedItems [i];
995           var bold;
996           if (item instanceof Label) {
997             bold = this.isItemTextBold("Label", item.getStyle());
998           } else if (item instanceof HomePieceOfFurniture
999               && item.isVisible()) {
1000             bold = this.isItemTextBold("HomePieceOfFurniture", item.getNameStyle());
1001           } else if (item instanceof Room) {
1002             bold = this.isItemTextBold("Room", item.getNameStyle());
1003             if (bold != this.isItemTextBold("Room", item.getAreaStyle())) {
1004               bold = null;
1005             }
1006           } else if (item instanceof DimensionLine) {
1007             bold = this.isItemTextBold("DimensionLine", item.getLengthStyle());
1008           } else {
1009             continue;
1010           }
1011           if (selectionBoldStyle == null) {
1012             selectionBoldStyle = bold;
1013           } else if (bold == null || selectionBoldStyle != bold) {
1014             selectionBoldStyle = null;
1015             break;
1016           }
1017         }
1018         action.putValue(AbstractAction.SELECTED_KEY, selectionBoldStyle != null && selectionBoldStyle);
1019       },
1020       isItemTextBold: function(itemClass, textStyle) {
1021         if (textStyle == null) {
1022           textStyle = preferences.getDefaultTextStyle(itemClass);
1023         }
1024         return textStyle.isBold();
1025       }
1026     });
1027   return action;
1028 }
1029 
1030 /**
1031  * Creates an action which is selected when all the text of the
1032  * selected items in <code>home</code> use italic style.
1033  * @param {HomeView.ActionType} actionType
1034  * @param {Home} home
1035  * @param {UserPreferences} preferences
1036  * @param {Object} controller
1037  * @param {string} method
1038  * @return {ResourceAction}
1039  * @private
1040  */
1041 HomePane.prototype.createItalicStyleToggleModel = function(actionType, home, preferences, controller, method) {
1042   var action = this.createAction(actionType, preferences, controller, method);
1043   home.addSelectionListener({
1044       selectionChanged: function(ev) {
1045         // Find if selected items are all italic or not
1046         var selectionItalicStyle = null;
1047         var selectedItems = home.getSelectedItems();
1048         for (var i = 0; i < selectedItems.length; i++) {
1049           item = selectedItems [i];
1050           var italic;
1051           if (item instanceof Label) {
1052             italic = this.isItemTextItalic("Label", item.getStyle());
1053           } else if (item instanceof HomePieceOfFurniture
1054                      && item.isVisible()) {
1055             italic = this.isItemTextItalic("HomePieceOfFurniture", item.getNameStyle());
1056           } else if (item instanceof Room) {
1057             italic = this.isItemTextItalic("Room", item.getNameStyle());
1058             if (italic != this.isItemTextItalic("Room", item.getAreaStyle())) {
1059               italic = null;
1060             }
1061           } else if (item instanceof DimensionLine) {
1062             italic = this.isItemTextItalic("DimensionLine", item.getLengthStyle());
1063           } else {
1064             continue;
1065           }
1066           if (selectionItalicStyle == null) {
1067             selectionItalicStyle = italic;
1068           } else if (italic == null || selectionItalicStyle != italic) {
1069             selectionItalicStyle = null;
1070             break;
1071           }
1072         }
1073         action.putValue(AbstractAction.SELECTED_KEY, selectionItalicStyle != null && selectionItalicStyle);
1074       },
1075       isItemTextItalic: function(itemClass, textStyle) {
1076         if (textStyle == null) {
1077           textStyle = preferences.getDefaultTextStyle(itemClass);
1078         }
1079         return textStyle.isItalic();
1080       }
1081     });
1082   return action;
1083 }
1084 
1085 /**
1086  * Returns the tool bar displayed in this pane.
1087  * @param {Home} home
1088  * @param {UserPreferences} preferences
1089  * @param {HomeController} controller
1090  * @return {Object}
1091  * @private
1092  */
1093 HomePane.prototype.createToolBar = function(home, preferences, controller) {
1094   var applicationMenuToolBar = document.getElementById("application-menu-toolbar");
1095 
1096   if (applicationMenuToolBar != null) {
1097     this.startToolBarButtonGroup(applicationMenuToolBar);
1098     this.addActionToToolBar("SHOW_APPLICATION_MENU", applicationMenuToolBar);
1099     this.showApplicationMenuButton = applicationMenuToolBar.children[applicationMenuToolBar.children.length - 1].lastChild; 
1100   }
1101 
1102   var toolBar = document.getElementById("home-pane-toolbar"); 
1103   this.toolBarDefaultChildren = Array.prototype.slice.call(toolBar.children);
1104   this.startToolBarButtonGroup(toolBar);
1105   var fileButton = false;
1106   if (toolBar.classList.contains("new-home")) {
1107     this.addActionToToolBar(HomeView.ActionType.NEW_HOME, toolBar);
1108     fileButton = true;
1109   } 
1110   if (toolBar.classList.contains("open")) {
1111     this.addActionToToolBar(HomeView.ActionType.OPEN, toolBar);
1112     fileButton = true;
1113   } 
1114   if (toolBar.classList.contains("save")) {
1115     this.addActionToToolBar(HomeView.ActionType.SAVE, toolBar);
1116     fileButton = true;
1117   } 
1118   if (toolBar.classList.contains("save-as")) {
1119     this.addActionToToolBar(HomeView.ActionType.SAVE_AS, toolBar);
1120     fileButton = true;
1121   } 
1122   if (fileButton) {
1123     this.addSeparator(toolBar); 
1124   }
1125    
1126   this.addToggleActionToToolBar(HomeView.ActionType.VIEW_FROM_TOP, toolBar); 
1127   this.addToggleActionToToolBar(HomeView.ActionType.VIEW_FROM_OBSERVER, toolBar);
1128   this.addSeparator(toolBar);
1129 
1130   this.addActionToToolBar(HomeView.ActionType.UNDO, toolBar); 
1131   this.addActionToToolBar(HomeView.ActionType.REDO, toolBar); 
1132   this.addSeparator(toolBar); 
1133   
1134   this.addActionToToolBar(HomeView.ActionType.DELETE_SELECTION, toolBar); 
1135   this.addActionToToolBar(HomeView.ActionType.CUT, toolBar, "toolbar-optional"); 
1136   this.addActionToToolBar(HomeView.ActionType.COPY, toolBar); 
1137   this.addActionToToolBar(HomeView.ActionType.PASTE, toolBar); 
1138   this.addSeparator(toolBar);
1139   
1140   this.addActionToToolBar(HomeView.ActionType.ADD_HOME_FURNITURE, toolBar, "toolbar-optional");
1141   this.addSeparator(toolBar);
1142   
1143   this.addToggleActionToToolBar(HomeView.ActionType.SELECT, toolBar); 
1144   this.addToggleActionToToolBar(HomeView.ActionType.PAN, toolBar, "toolbar-optional"); 
1145   this.addToggleActionToToolBar(HomeView.ActionType.CREATE_WALLS, toolBar); 
1146   this.addToggleActionToToolBar(HomeView.ActionType.CREATE_ROOMS, toolBar); 
1147   this.addToggleActionToToolBar(HomeView.ActionType.CREATE_POLYLINES, toolBar); 
1148   this.addToggleActionToToolBar(HomeView.ActionType.CREATE_DIMENSION_LINES, toolBar); 
1149   this.addToggleActionToToolBar(HomeView.ActionType.CREATE_LABELS, toolBar); 
1150   this.addSeparator(toolBar);
1151   
1152   var enableDisableMagnetismButton = this.createEnableDisableMagnetismButton(preferences);
1153   if (enableDisableMagnetismButton !== null) {
1154     this.addButtonToToolBar(toolBar, enableDisableMagnetismButton);
1155   }
1156   var lockUnlockBasePlanButton = this.createLockUnlockBasePlanButton(home)
1157   if (lockUnlockBasePlanButton !== null) {
1158     this.addButtonToToolBar(toolBar, lockUnlockBasePlanButton);
1159   }
1160   this.addActionToToolBar(HomeView.ActionType.FLIP_HORIZONTALLY, toolBar);
1161   this.addActionToToolBar(HomeView.ActionType.FLIP_VERTICALLY, toolBar);
1162   this.addSeparator(toolBar);
1163   
1164   this.addActionToToolBar(HomeView.ActionType.INCREASE_TEXT_SIZE, toolBar);
1165   this.addActionToToolBar(HomeView.ActionType.DECREASE_TEXT_SIZE, toolBar);
1166   this.addToggleActionToToolBar(HomeView.ActionType.TOGGLE_BOLD_STYLE, toolBar);
1167   this.addToggleActionToToolBar(HomeView.ActionType.TOGGLE_ITALIC_STYLE, toolBar);
1168   this.addSeparator(toolBar);
1169 
1170   this.addActionToToolBar(HomeView.ActionType.ZOOM_IN, toolBar, "toolbar-optional");
1171   this.addActionToToolBar(HomeView.ActionType.ZOOM_OUT, toolBar, "toolbar-optional");
1172   this.addSeparator(toolBar);
1173   if (this.showApplicationMenuButton == null
1174       || !window.matchMedia("(hover: none), (pointer: coarse)").matches) {
1175     this.addActionToToolBar(HomeView.ActionType.ABOUT, toolBar);
1176     this.addSeparator(toolBar);
1177   }
1178 
1179   this.addActionToToolBar(HomeView.ActionType.PREFERENCES, toolBar); 
1180 
1181   return toolBar;
1182 }
1183 
1184 /**
1185  * Creates contextual menus for components within this home pane.
1186  * @param {Home} home
1187  * @param {UserPreferences} preferences
1188  * @private
1189  */
1190 HomePane.prototype.createPopupMenus = function(home, preferences) {
1191   var ActionType = HomeView.ActionType;
1192   var homePane = this;
1193   var controller = this.controller;
1194   
1195   if (this.showApplicationMenuButton == null
1196       || !window.matchMedia("(hover: none), (pointer: coarse)").matches) {
1197     // Catalog view popup menu
1198     var furnitureCatalogView = this.controller.getFurnitureCatalogController().getView();
1199     if (furnitureCatalogView != null) {
1200       this.furnitureCatalogPopupMenu = new JSPopupMenu(preferences, furnitureCatalogView.getHTMLElement(), 
1201           function(builder) {
1202             homePane.addActionToMenu(ActionType.ADD_HOME_FURNITURE, builder);
1203             homePane.addActionToMenu(ActionType.ADD_FURNITURE_TO_GROUP, builder);
1204           });
1205     }
1206   
1207     var furnitureView = this.controller.getFurnitureController().getView();
1208     // Furniture view popup menu
1209     if (furnitureView != null) {
1210       this.furniturePopupMenu = new JSPopupMenu(preferences, furnitureView.getHTMLElement(), 
1211           function(builder) {
1212             homePane.addActionToMenu(ActionType.CUT, builder);
1213             homePane.addActionToMenu(ActionType.COPY, builder);
1214             homePane.addActionToMenu(ActionType.PASTE, builder);
1215             homePane.addActionToMenu(ActionType.PASTE_TO_GROUP, builder);
1216             homePane.addActionToMenu(ActionType.PASTE_STYLE, builder);
1217             builder.addSeparator();
1218             homePane.addActionToMenu(ActionType.MODIFY_FURNITURE, builder);
1219             homePane.addActionToMenu(ActionType.GROUP_FURNITURE, builder);
1220             homePane.addActionToMenu(ActionType.UNGROUP_FURNITURE, builder);
1221             homePane.createAlignOrDistributeMenu(builder);
1222             homePane.addActionToMenu(ActionType.RESET_FURNITURE_ELEVATION, builder);
1223             builder.addSeparator();
1224             homePane.createFurnitureSortMenu(home, builder);
1225             homePane.createFurnitureDisplayPropertyMenu(home, builder);
1226           });
1227     }
1228   
1229     // Plan view popup menu
1230     var planView = this.controller.getPlanController().getView();
1231     if (planView != null) {
1232       this.planPopupMenu = new JSPopupMenu(preferences, planView.getHTMLElement(),
1233           function(builder) {
1234             homePane.addActionToMenu(ActionType.UNDO, builder);
1235             homePane.addActionToMenu(ActionType.REDO, builder);
1236             builder.addSeparator();
1237             homePane.addActionToMenu(ActionType.DELETE_SELECTION, builder);
1238             homePane.addActionToMenu(ActionType.CUT, builder);
1239             homePane.addActionToMenu(ActionType.COPY, builder);
1240             homePane.addActionToMenu(ActionType.PASTE, builder);
1241             homePane.addActionToMenu(ActionType.PASTE_STYLE, builder);
1242             builder.addSeparator();
1243             homePane.addActionToMenu(ActionType.SELECT_ALL, builder);
1244             homePane.addActionToMenu(ActionType.SELECT_ALL_AT_ALL_LEVELS, builder);
1245             builder.addSeparator();
1246             homePane.addActionToMenu(ActionType.MODIFY_FURNITURE, builder);
1247             homePane.addActionToMenu(ActionType.GROUP_FURNITURE, builder);
1248             homePane.addActionToMenu(ActionType.UNGROUP_FURNITURE, builder);
1249             homePane.addActionToMenu(ActionType.RESET_FURNITURE_ELEVATION, builder);
1250             builder.addSeparator();
1251             homePane.addActionToMenu(ActionType.MODIFY_COMPASS, builder);
1252             homePane.addActionToMenu(ActionType.MODIFY_WALL, builder);
1253             homePane.addActionToMenu(ActionType.JOIN_WALLS, builder);
1254             homePane.addActionToMenu(ActionType.REVERSE_WALL_DIRECTION, builder);
1255             homePane.addActionToMenu(ActionType.SPLIT_WALL, builder);
1256             homePane.addActionToMenu(ActionType.MODIFY_ROOM, builder);
1257             homePane.addActionToMenu(ActionType.MODIFY_POLYLINE, builder);
1258             homePane.addActionToMenu(ActionType.MODIFY_DIMENSION_LINE, builder);
1259             homePane.addActionToMenu(ActionType.MODIFY_LABEL, builder);
1260             builder.addSeparator();
1261             builder.addSubMenu(homePane.getMenuAction(HomePane.MenuActionType.MODIFY_TEXT_STYLE), function(builder) {
1262                 homePane.addActionToMenu(ActionType.INCREASE_TEXT_SIZE, builder);
1263                 homePane.addActionToMenu(ActionType.DECREASE_TEXT_SIZE, builder);
1264                 homePane.addActionToMenu(ActionType.TOGGLE_BOLD_STYLE, builder);
1265                 homePane.addActionToMenu(ActionType.TOGGLE_ITALIC_STYLE, builder);
1266               });
1267             builder.addSeparator();
1268             homePane.addActionToMenu(ActionType.IMPORT_BACKGROUND_IMAGE, builder);
1269             homePane.addActionToMenu(ActionType.MODIFY_BACKGROUND_IMAGE, builder);
1270             homePane.addActionToMenu(ActionType.HIDE_BACKGROUND_IMAGE, builder);
1271             homePane.addActionToMenu(ActionType.SHOW_BACKGROUND_IMAGE, builder);
1272             homePane.addActionToMenu(ActionType.DELETE_BACKGROUND_IMAGE, builder);
1273             builder.addSeparator();
1274             homePane.addActionToMenu(ActionType.ADD_LEVEL, builder);
1275             homePane.addActionToMenu(ActionType.ADD_LEVEL_AT_SAME_ELEVATION, builder);
1276             homePane.addActionToMenu(ActionType.MODIFY_LEVEL, builder);
1277             homePane.addActionToMenu(ActionType.DELETE_LEVEL, builder);
1278           });
1279     }
1280   
1281     // 3D view popup menu
1282     var view3D = this.controller.getHomeController3D().getView();
1283     if (view3D != null) {
1284       this.view3DPopupMenu = new JSPopupMenu(preferences, view3D.getHTMLElement(), 
1285           function(builder) {
1286             homePane.addActionToMenu(ActionType.VIEW_FROM_TOP, builder);
1287             homePane.addActionToMenu(ActionType.VIEW_FROM_OBSERVER, builder);
1288             homePane.addActionToMenu(ActionType.MODIFY_OBSERVER, builder);
1289             homePane.addActionToMenu(ActionType.STORE_POINT_OF_VIEW, builder);
1290             var storedCameras = home.getStoredCameras();
1291             if (storedCameras.length > 0) {
1292               var goToPointOfViewAction = homePane.getMenuAction(HomePane.MenuActionType.GO_TO_POINT_OF_VIEW);
1293               if (goToPointOfViewAction.getValue(AbstractAction.NAME) != null) {
1294                 builder.addSubMenu(goToPointOfViewAction, function(builder) {
1295                     var cameraMenuItemBuilder = function(camera) {
1296                         builder.addMenuItem(camera.getName(),
1297                             function() { 
1298                               controller.getHomeController3D().goToCamera(camera);
1299                             });
1300                       };
1301                     var storedCameras = home.getStoredCameras();
1302                     for (var i = 0; i < storedCameras.length; i++) {
1303                       cameraMenuItemBuilder(storedCameras[i]);
1304                     }
1305                   });
1306               }
1307               homePane.addActionToMenu(ActionType.DELETE_POINTS_OF_VIEW, builder);
1308             }
1309     
1310             builder.addSeparator();
1311             homePane.addActionToMenu(ActionType.DISPLAY_ALL_LEVELS, builder);
1312             homePane.addActionToMenu(ActionType.DISPLAY_SELECTED_LEVEL, builder);
1313             homePane.addActionToMenu(ActionType.MODIFY_3D_ATTRIBUTES, builder);
1314           });
1315     }
1316   } else {
1317     // Menu button popup menu
1318     new JSPopupMenu(preferences, this.showApplicationMenuButton, 
1319         function(builder) {
1320           var toolBar = document.getElementById("home-pane-toolbar"); 
1321           this.toolBarDefaultChildren = Array.prototype.slice.call(toolBar.children);
1322           var fileButton = false;
1323           if (toolBar.classList.contains("new-home")) {
1324             homePane.addActionToMenu(HomeView.ActionType.NEW_HOME, builder);
1325             fileButton = true;
1326           } 
1327           if (toolBar.classList.contains("open")) {
1328             homePane.addActionToMenu(HomeView.ActionType.OPEN, builder);
1329             fileButton = true;
1330           } 
1331           if (toolBar.classList.contains("save")) {
1332             homePane.addActionToMenu(HomeView.ActionType.SAVE, builder);
1333             fileButton = true;
1334           } 
1335           if (toolBar.classList.contains("save-as")) {
1336             homePane.addActionToMenu(HomeView.ActionType.SAVE_AS, builder);
1337             fileButton = true;
1338           } 
1339           if (fileButton) {
1340             builder.addSeparator(); 
1341           }
1342           homePane.addActionToMenu(ActionType.DELETE_SELECTION, builder);
1343           homePane.addActionToMenu(ActionType.CUT, builder);
1344           homePane.addActionToMenu(ActionType.COPY, builder);
1345           homePane.addActionToMenu(ActionType.PASTE, builder);
1346           homePane.addActionToMenu(ActionType.PASTE_TO_GROUP, builder);
1347           homePane.addActionToMenu(ActionType.PASTE_STYLE, builder);
1348           builder.addSeparator();
1349           homePane.addActionToMenu(ActionType.SELECT_ALL, builder);
1350           homePane.addActionToMenu(ActionType.SELECT_ALL_AT_ALL_LEVELS, builder);
1351           builder.addSeparator();
1352           homePane.addActionToMenu(ActionType.ADD_HOME_FURNITURE, builder);
1353           homePane.addActionToMenu(ActionType.ADD_FURNITURE_TO_GROUP, builder);
1354           builder.addSeparator();
1355           homePane.addActionToMenu(ActionType.MODIFY_FURNITURE, builder);
1356           homePane.addActionToMenu(ActionType.GROUP_FURNITURE, builder);
1357           homePane.addActionToMenu(ActionType.UNGROUP_FURNITURE, builder);
1358           homePane.createAlignOrDistributeMenu(builder);
1359           homePane.addActionToMenu(ActionType.RESET_FURNITURE_ELEVATION, builder);
1360           var furnitureView = controller.getFurnitureController().getView();
1361           if (furnitureView != null 
1362               && window.getComputedStyle(furnitureView.getHTMLElement())["display"] != "none") {
1363             homePane.createFurnitureSortMenu(home, builder);
1364             homePane.createFurnitureDisplayPropertyMenu(home, builder);
1365           }
1366           builder.addSeparator();
1367           homePane.addActionToMenu(ActionType.SELECT, builder);
1368           homePane.addActionToMenu(ActionType.CREATE_WALLS, builder);
1369           homePane.addActionToMenu(ActionType.CREATE_ROOMS, builder);
1370           homePane.addActionToMenu(ActionType.CREATE_POLYLINES, builder);
1371           homePane.addActionToMenu(ActionType.CREATE_DIMENSION_LINES, builder);
1372           homePane.addActionToMenu(ActionType.CREATE_LABELS, builder);
1373           builder.addSeparator();
1374           homePane.addActionToMenu(ActionType.MODIFY_COMPASS, builder);
1375           homePane.addActionToMenu(ActionType.MODIFY_WALL, builder);
1376           homePane.addActionToMenu(ActionType.JOIN_WALLS, builder);
1377           homePane.addActionToMenu(ActionType.REVERSE_WALL_DIRECTION, builder);
1378           homePane.addActionToMenu(ActionType.SPLIT_WALL, builder);
1379           homePane.addActionToMenu(ActionType.MODIFY_ROOM, builder);
1380           homePane.addActionToMenu(ActionType.MODIFY_POLYLINE, builder);
1381           homePane.addActionToMenu(ActionType.MODIFY_DIMENSION_LINE, builder);
1382           homePane.addActionToMenu(ActionType.MODIFY_LABEL, builder);
1383           builder.addSeparator();
1384           homePane.addActionToMenu(ActionType.IMPORT_BACKGROUND_IMAGE, builder);
1385           homePane.addActionToMenu(ActionType.MODIFY_BACKGROUND_IMAGE, builder);
1386           homePane.addActionToMenu(ActionType.HIDE_BACKGROUND_IMAGE, builder);
1387           homePane.addActionToMenu(ActionType.SHOW_BACKGROUND_IMAGE, builder);
1388           homePane.addActionToMenu(ActionType.DELETE_BACKGROUND_IMAGE, builder);
1389           builder.addSeparator();
1390           homePane.addActionToMenu(ActionType.ADD_LEVEL, builder);
1391           homePane.addActionToMenu(ActionType.ADD_LEVEL_AT_SAME_ELEVATION, builder);
1392           homePane.addActionToMenu(ActionType.MODIFY_LEVEL, builder);
1393           homePane.addActionToMenu(ActionType.DELETE_LEVEL, builder);
1394           builder.addSeparator();
1395           homePane.addActionToMenu(ActionType.MODIFY_OBSERVER, builder);
1396           homePane.addActionToMenu(ActionType.STORE_POINT_OF_VIEW, builder);
1397           var storedCameras = home.getStoredCameras();
1398           if (storedCameras.length > 0) {
1399             var goToPointOfViewAction = homePane.getMenuAction(HomePane.MenuActionType.GO_TO_POINT_OF_VIEW);
1400             if (goToPointOfViewAction.getValue(AbstractAction.NAME) != null) {
1401               builder.addSubMenu(goToPointOfViewAction, 
1402                   function(builder) {
1403                     var cameraMenuItemBuilder = function(camera) {
1404                         builder.addMenuItem(camera.getName(),
1405                             function() { 
1406                               controller.getHomeController3D().goToCamera(camera);
1407                             });
1408                       };
1409                 var storedCameras = home.getStoredCameras();
1410                 for (var i = 0; i < storedCameras.length; i++) {
1411                   cameraMenuItemBuilder(storedCameras[i]);
1412                 }
1413               });
1414             }
1415             homePane.addActionToMenu(ActionType.DELETE_POINTS_OF_VIEW, builder);
1416           }
1417           
1418           builder.addSeparator();
1419           homePane.addActionToMenu(ActionType.DISPLAY_ALL_LEVELS, builder);
1420           homePane.addActionToMenu(ActionType.DISPLAY_SELECTED_LEVEL, builder);
1421           homePane.addActionToMenu(ActionType.MODIFY_3D_ATTRIBUTES, builder);
1422           builder.addSeparator();
1423           homePane.addActionToMenu(ActionType.ABOUT, builder);
1424         });
1425   } 
1426 }
1427 
1428 /**
1429  * Initializes pane splitters.
1430  * @private
1431  */
1432 HomePane.prototype.initSplitters = function() {
1433   var controller = this.controller;
1434 
1435   var furnitureCatalogView = controller.getFurnitureCatalogController().getView();
1436   var furnitureView = controller.getFurnitureController().getView();
1437   var planView = controller.getPlanController().getView();
1438   var view3D = controller.getHomeController3D().getView();
1439   
1440   var furniturePlanSplitterElement = document.getElementById("furniture-plan-splitter");
1441   var plan3DViewSplitterElement = document.getElementById("plan-3D-view-splitter");
1442   var catalogFurnitureSplitterElement = document.getElementById("catalog-furniture-splitter");
1443 
1444   this.furniturePlanSplitter = {
1445       element: furniturePlanSplitterElement,
1446       homePropertyName: HomePane.MAIN_PANE_DIVIDER_LOCATION_VISUAL_PROPERTY,
1447       firstGroupElement: document.getElementById("catalog-furniture-pane"),
1448       secondGroupElement: document.getElementById("plan-3D-view-pane"),
1449       isDisplayed: function() {
1450         return furniturePlanSplitterElement && furniturePlanSplitterElement.clientWidth > 0;
1451       },
1452       resizeListener: function(splitterPosition) {
1453         // Refresh 2D/3D plan views on resize
1454         planView.revalidate();
1455         view3D.revalidate();
1456       }
1457     };
1458 
1459   this.plan3DViewSplitter = {
1460       element: plan3DViewSplitterElement,
1461       homePropertyName: HomePane.PLAN_PANE_DIVIDER_LOCATION_VISUAL_PROPERTY,
1462       firstGroupElement: planView.getHTMLElement(),
1463       secondGroupElement: view3D.getHTMLElement(),
1464       isDisplayed: function() {
1465         return plan3DViewSplitterElement && plan3DViewSplitterElement.clientWidth > 0 && planView != null && view3D != null;
1466       },
1467       resizeListener: function(splitterPosition) {
1468         // Refresh 2D/3D plan views on resize
1469         planView.revalidate();
1470         view3D.revalidate();
1471       }
1472     };
1473 
1474   this.catalogFurnitureSplitter = {
1475       element: catalogFurnitureSplitterElement,
1476       homePropertyName: HomePane.CATALOG_PANE_DIVIDER_LOCATION_VISUAL_PROPERTY,
1477       firstGroupElement: furnitureCatalogView.getHTMLElement(),
1478       secondGroupElement: furnitureView.getHTMLElement(),
1479       isDisplayed: function() {
1480         return catalogFurnitureSplitterElement && catalogFurnitureSplitterElement.clientWidth > 0 && furnitureCatalogView != null && furnitureView != null;
1481       }
1482     };
1483 
1484   this.updateSplitters();
1485   
1486   // Dispatch a resize window event to configure HTML elements in split panes and their children
1487   var resizeEvent = window.document.createEvent('UIEvents'); 
1488   resizeEvent.initEvent('resize', true, true); 
1489   window.dispatchEvent(resizeEvent);
1490 }
1491 
1492 /**
1493  * @private
1494  */
1495 HomePane.prototype.updateSplitters = function() {
1496   var planView = this.controller.getPlanController().getView();
1497   var view3D = this.controller.getHomeController3D().getView();
1498   var furnitureCatalogView = this.controller.getFurnitureCatalogController().getView();
1499   // Plan 3D view splitter inverts its group depending on orientation
1500   if (furnitureCatalogView !== null 
1501       && furnitureCatalogView.getHTMLElement().getBoundingClientRect().top > view3D.getHTMLElement().getBoundingClientRect().top + 10) {
1502     this.plan3DViewSplitter.firstGroupElement = view3D.getHTMLElement();
1503     this.plan3DViewSplitter.secondGroupElement = planView.getHTMLElement();
1504   } else {
1505     this.plan3DViewSplitter.firstGroupElement = planView.getHTMLElement();
1506     this.plan3DViewSplitter.secondGroupElement = view3D.getHTMLElement();
1507   }
1508   this.updateSplitter(this.plan3DViewSplitter);
1509   this.updateSplitter(this.furniturePlanSplitter);
1510   this.updateSplitter(this.catalogFurnitureSplitter);
1511 }
1512 
1513 /**
1514  * Updates the given pane splitter.
1515  * @param {{
1516  *   element: HTMLElement,
1517  *   homePropertyName: string,
1518  *   firstGroupElement: HTMLElement,
1519  *   secondGroupElement: HTMLElement,
1520  *   isDisplayed: function(): boolean,
1521  *   resizeListener?: function(splitterPosition: number)
1522  * }} splitter
1523  * @private
1524  */
1525 HomePane.prototype.updateSplitter = function(splitter) {
1526   // Reset
1527   splitter.element.style.display = '';
1528   splitter.element.style.top = '';
1529   splitter.element.style.left = '';
1530   splitter.firstGroupElement.style.left = '';
1531   splitter.firstGroupElement.style.top = '';
1532   splitter.firstGroupElement.style.width = '';
1533   splitter.firstGroupElement.style.height = '';
1534   splitter.secondGroupElement.style.left = '';
1535   splitter.secondGroupElement.style.top = '';
1536   splitter.secondGroupElement.style.width = '';
1537   splitter.secondGroupElement.style.height = '';
1538 
1539   var displayed = splitter.isDisplayed();
1540   if (displayed) {
1541     splitter.element.style.display = 'block';
1542   } else {
1543     splitter.element.style.display = 'none';
1544   }
1545 
1546   if (splitter.mouseListener !== undefined 
1547       && splitter.mouseListener.mousePressed) {
1548     splitter.element.removeEventListener("mousedown", splitter.mouseListener.mousePressed, true);
1549     splitter.element.removeEventListener("touchstart", splitter.mouseListener.mousePressed, true);
1550     window.removeEventListener("resize", splitter.mouseListener.windowResized);
1551     delete splitter.mouseListener;
1552   }
1553 
1554   splitter.element.classList.remove("horizontal");
1555   splitter.element.classList.remove("vertical");
1556   var horizontal = splitter.element.clientWidth > splitter.element.clientHeight;
1557   if (horizontal) {
1558     splitter.element.classList.add("horizontal");
1559   } else {
1560     splitter.element.classList.add("vertical");
1561   }
1562 
1563   var initialSplitterPosition = this.home.getNumericProperty(splitter.homePropertyName);
1564   var positionStyleProperty = horizontal ? "top" : "left";
1565   var dimensionStyleProperty = horizontal ? "height" : "width";
1566   var dimensionProperty = horizontal ? "clientHeight" : "clientWidth";
1567   var pointerPositionProperty = horizontal ? "clientY" : "clientX";
1568   var offsetParent = splitter.firstGroupElement.offsetParent;
1569   var offsetProperty = horizontal ? "offsetTop" : "offsetLeft";
1570   var offsetTopFirst = offsetParent == document.body 
1571       ? splitter.firstGroupElement[offsetProperty] - offsetParent[offsetProperty] 
1572       : 0;
1573   var homePane = this;
1574 
1575   var mouseListener = {
1576       getSplitterPosition: function(ev) {
1577         var pointerCoordinatesObject = ev.touches && ev.touches.length > 0 
1578             ? ev.touches[0] : ev;
1579         return pointerCoordinatesObject[pointerPositionProperty] - offsetParent[offsetProperty];
1580       },
1581       setSplitterPosition: function(relativePosition) {
1582         // Prevent from moving splitter beyond limit (before first elements) 
1583         if (relativePosition < offsetTopFirst) {
1584           relativePosition = offsetTopFirst;
1585         }
1586         // Prevent from moving splitter beyond limit (farther than parents width or height) 
1587         if (relativePosition > offsetParent[dimensionProperty] - splitter.element[dimensionProperty]) {
1588           relativePosition = offsetParent[dimensionProperty] - splitter.element[dimensionProperty];
1589         }
1590         // Elements in first groups grow or shrink
1591         splitter.firstGroupElement.style[dimensionStyleProperty] = (relativePosition - offsetTopFirst) + "px";
1592         // Splitter moves to new mouse position
1593         splitter.element.style[positionStyleProperty] = relativePosition + "px";
1594         // Elements in second groups move & grow / shrink
1595         splitter.secondGroupElement.style[positionStyleProperty] = (relativePosition + splitter.element[dimensionProperty]) + "px";
1596         splitter.secondGroupElement.style[dimensionStyleProperty] = "calc(100% - " + (relativePosition + splitter.element[dimensionProperty]) + "px)";
1597       },
1598       mouseDragged: function(ev) {
1599         ev.stopImmediatePropagation();
1600         mouseListener.currentPosition = mouseListener.getSplitterPosition(ev);
1601         mouseListener.setSplitterPosition(mouseListener.currentPosition);
1602         if (splitter.resizeListener !== undefined) {
1603           splitter.resizeListener(mouseListener.currentPosition);
1604         }
1605       },
1606       mousePressed: function(ev) {
1607         ev.preventDefault();
1608         ev.stopImmediatePropagation();
1609         mouseListener.currentPosition = mouseListener.getSplitterPosition(ev);
1610         splitter.element.classList.add("moving");
1611         window.addEventListener("mousemove", mouseListener.mouseDragged, true);
1612         window.addEventListener("touchmove", mouseListener.mouseDragged, true);
1613         window.addEventListener("mouseup", mouseListener.windowMouseReleased, true);
1614         window.addEventListener("touchend", mouseListener.windowMouseReleased, true);
1615       },
1616       windowMouseReleased: function(ev) {
1617         ev.stopImmediatePropagation();
1618         splitter.element.classList.remove("moving");
1619         window.removeEventListener("mousemove", mouseListener.mouseDragged, true);
1620         window.removeEventListener("touchmove", mouseListener.mouseDragged, true);
1621         window.removeEventListener("mouseup", mouseListener.windowMouseReleased, true);
1622         window.removeEventListener("touchend", mouseListener.windowMouseReleased, true);
1623         homePane.controller.setHomeProperty(splitter.homePropertyName, mouseListener.currentPosition == null ? null : mouseListener.currentPosition.toString());
1624         if (splitter.resizeListener !== undefined) {
1625           splitter.resizeListener(mouseListener.currentPosition);
1626         }
1627       },
1628       windowResized: function(ev) {
1629         var splitterPosition = window.getComputedStyle(splitter.element)[positionStyleProperty];
1630         if (splitterPosition == null) {
1631           splitterPosition = "0px";
1632         }
1633         splitterPosition = splitterPosition.substring(0, splitterPosition.length - 2);
1634         if (splitterPosition > offsetParent[dimensionProperty] - splitter.element[dimensionProperty]) {
1635           mouseListener.setSplitterPosition(offsetParent[dimensionProperty] - splitter.element[dimensionProperty]);
1636         } 
1637       }
1638     };
1639 
1640   if (initialSplitterPosition != null) {
1641     if (displayed) {
1642       mouseListener.setSplitterPosition(initialSplitterPosition);
1643     }
1644     if (splitter.resizeListener !== undefined) {
1645       splitter.resizeListener(initialSplitterPosition);
1646     }
1647   }
1648   splitter.element.addEventListener("mousedown", mouseListener.mousePressed, true);
1649   splitter.element.addEventListener("touchstart", mouseListener.mousePressed, true);
1650   // Ensure splitter doesn't disappear after a window resize 
1651   window.addEventListener("resize", mouseListener.windowResized);
1652   splitter.mouseListener = mouseListener; 
1653 }
1654 
1655 /**
1656  * @private
1657  */
1658 HomePane.prototype.addOrientationChangeListener = function() {
1659   var homePane = this;
1660   var orientationListener = function(ev) {
1661       var planView = homePane.controller.getPlanController().getView();
1662       var view3D = homePane.controller.getHomeController3D().getView();
1663       if (planView != null && view3D != null) {
1664         var splitter = document.getElementById("plan-3D-view-splitter");
1665         splitter.removeAttribute("style");
1666         planView.getHTMLElement().removeAttribute("style");
1667         view3D.getHTMLElement().removeAttribute("style");
1668         planView.revalidate();
1669         view3D.revalidate();
1670       }
1671       setTimeout(function() {
1672           homePane.updateSplitters();
1673         }, 100);
1674     };
1675   this.resizeListener = function(ev) {
1676       if (window.matchMedia("(hover: none), (pointer: coarse)").matches) {
1677         orientationListener(ev);
1678       }
1679     };
1680   window.addEventListener("resize", this.resizeListener);
1681 }
1682 
1683 /** 
1684  * @private 
1685  */
1686 HomePane.prototype.startToolBarButtonGroup = function(toolBar) {
1687   var buttonGroup = document.createElement("span");
1688   toolBar.appendChild(buttonGroup);
1689   buttonGroup.classList.add("toolbar-button-group");
1690 }
1691 
1692 /** 
1693  * @private 
1694  */
1695 HomePane.prototype.addButtonToToolBar = function(toolBar, button) {
1696   if (toolBar.children.length === 0) {
1697     this.startToolBarButtonGroup(toolBar);
1698   }
1699   toolBar.children[toolBar.children.length - 1].appendChild(button);        
1700 }
1701 
1702 /**
1703  * Adds to tool bar the button matching the given <code>actionType</code>
1704  * and returns <code>true</code> if it was added.
1705  * @param {HomeView.ActionType} actionType
1706  * @param {javax.swing.JToolBar} toolBar
1707  * @private
1708  */
1709 HomePane.prototype.addToggleActionToToolBar = function(actionType, toolBar, additionalClass) {
1710   var action = this.getAction(actionType);
1711   if (action.getValue(AbstractAction.NAME) != null) {
1712     var button = this.createToolBarButton(action, additionalClass);
1713     if (action.getValue(AbstractAction.SELECTED_KEY)) {
1714       button.classList.add("selected");
1715     }
1716     action.addPropertyChangeListener(function(ev) {
1717         if (ev.getPropertyName() == AbstractAction.SELECTED_KEY) {
1718           if (ev.getNewValue()) {
1719             button.classList.add("selected");
1720           } else {
1721             button.classList.remove("selected");
1722           }
1723         }
1724       });
1725     button.addEventListener("click", function() {
1726         var group = action.getValue(ResourceAction.TOGGLE_BUTTON_GROUP);
1727         action.putValue(AbstractAction.SELECTED_KEY, group ? true : !action.getValue(AbstractAction.SELECTED_KEY));
1728       });
1729     this.addButtonToToolBar(toolBar, button);
1730   }
1731 }
1732 
1733 /**
1734  * Adds to tool bar the button matching the given <code>actionType</code>.
1735  * @param {HomeView.ActionType} actionType
1736  * @param {Object} toolBar
1737  * @param {string} otherClass additional CSS class
1738  * @private
1739  */
1740 HomePane.prototype.addActionToToolBar = function(actionType, toolBar, additionalClass) {
1741   var action = this.getAction(actionType);
1742   if (action.getValue(AbstractAction.NAME) != null) {
1743     this.addButtonToToolBar(toolBar, this.createToolBarButton(action, additionalClass));
1744   }
1745 }
1746 
1747 /**
1748  * Returns a button configured from the given <code>action</code>.
1749  * @param {HomeView.ResourceAction} action
1750  * @param {string} [additionalClass] additional CSS class
1751  * @return {HTMLButton} 
1752  * @private
1753  */
1754 HomePane.prototype.createToolBarButton = function(action, additionalClass) {
1755   var button = document.createElement("button");
1756   button.id = "toolbar-button-" + action.getValue(ResourceAction.RESOURCE_PREFIX);
1757   button.disabled = !action.isEnabled();
1758   button.tabIndex = -1;
1759   // Modify action with a setAction method which is also invoked elsewhere 
1760   button.setAction = function(newAction) {
1761       button.action = newAction;
1762       var iconUrl = newAction.getURL(ResourceAction.TOOL_BAR_ICON);
1763       if (!iconUrl) {
1764         iconUrl = newAction.getURL(AbstractAction.SMALL_ICON);
1765       }
1766       button.style.backgroundImage = "url('" + iconUrl + "')";
1767       button.style.backgroundPosition = "center";
1768       button.style.backgroundRepeat = "no-repeat";
1769       var shortDescription = newAction.getValue(AbstractAction.SHORT_DESCRIPTION);
1770       if (shortDescription) {
1771         button.title = shortDescription;
1772       }
1773     };
1774   button.setAction(action);
1775   button.classList.add("toolbar-button");
1776   if (action.getValue(ResourceAction.TOGGLE_BUTTON_GROUP)) {
1777     button.classList.add("toggle");
1778   }
1779   button.action = action;
1780   button.addEventListener("click", function(ev) {
1781       this.action.actionPerformed(ev);
1782     });
1783   var listener = {
1784         propertyChange: function(ev) {
1785           if (ev.getPropertyName() == "enabled") {
1786             button.disabled = !ev.getNewValue();
1787           } else if (ev.getPropertyName() == AbstractAction.SHORT_DESCRIPTION) {
1788             button.title = ev.getNewValue();
1789           }
1790         }
1791     };
1792   action.addPropertyChangeListener(listener);
1793   if (additionalClass) {
1794     button.classList.add(additionalClass);
1795   }
1796   return button;
1797 }
1798 
1799 /**
1800  * @private
1801  */
1802 HomePane.prototype.addSeparator = function(toolBar) {
1803   this.startToolBarButtonGroup(toolBar);
1804 }
1805   
1806 /**
1807  * Enables or disables the action matching <code>actionType</code>.
1808  * @param {HomeView.ActionType} actionType
1809  * @param {boolean} enabled
1810  */
1811 HomePane.prototype.setEnabled = function(actionType, enabled) {
1812   var action = this.getAction(actionType);
1813   if (action != null) {
1814     action.setEnabled(enabled);
1815   }
1816 }
1817 
1818 /**
1819  * Enables or disables the action matching <code>actionType</code>.
1820  * @param {String} actionType
1821  * @param {boolean} enabled
1822  */
1823 HomePane.prototype.setActionEnabled = function(actionType, enabled) {
1824   var action = this.getAction(actionType);
1825   if (action != null) {
1826     action.setEnabled(enabled);
1827   }
1828 }
1829 
1830 /**
1831  * Sets the <code>NAME</code> and <code>SHORT_DESCRIPTION</code> properties value
1832  * of undo and redo actions. If a parameter is null,
1833  * the properties will be reset to their initial values.
1834  * @param {string} undoText
1835  * @param {string} redoText
1836  */
1837 HomePane.prototype.setUndoRedoName = function(undoText, redoText) {
1838   // Localize undo / redo prefix
1839   this.setNameAndShortDescription(HomeView.ActionType.UNDO, 
1840       undoText != null ? undoText.replace(/^Undo/, this.preferences.getLocalizedString("AbstractUndoableEdit", "undo.textAndMnemonic")) : null);
1841   this.setNameAndShortDescription(HomeView.ActionType.REDO, 
1842       redoText != null ? redoText.replace(/^Redo/, this.preferences.getLocalizedString("AbstractUndoableEdit", "redo.textAndMnemonic")) : null);
1843 }
1844 
1845 /**
1846  * Sets the <code>NAME</code> and <code>SHORT_DESCRIPTION</code> properties value
1847  * matching <code>actionType</code>. If <code>name</code> is null,
1848  * the properties will be reset to their initial values.
1849  * @param {HomeView.ActionType} actionType
1850  * @param {string} name
1851  * @private
1852  */
1853 HomePane.prototype.setNameAndShortDescription = function(actionType, name) {
1854   var action = this.getAction(actionType);
1855   if (action != null) {
1856     if (name == null) {
1857       name = action.getValue(AbstractAction.DEFAULT);
1858     }
1859     action.putValue(AbstractAction.NAME, name);
1860     action.putValue(AbstractAction.SHORT_DESCRIPTION, name);
1861   }
1862 }
1863 
1864 /**
1865  * Enables or disables transfer between components.
1866  * @param {boolean} enabled
1867  */
1868 HomePane.prototype.setTransferEnabled = function(enabled) {
1869   var furnitureCatalogView = this.controller.getFurnitureCatalogController().getView();
1870   if (enabled
1871       && !this.transferHandlerEnabled) {
1872     if (furnitureCatalogView != null) {
1873       if (this.furnitureCatalogDragAndDropListener == null) {
1874         this.furnitureCatalogDragAndDropListener = this.createFurnitureCatalogMouseListener();
1875       }
1876       
1877       var pieceContainers = furnitureCatalogView.getHTMLElement().querySelectorAll(".furniture");
1878       if (OperatingSystem.isInternetExplorerOrLegacyEdge()
1879           && window.PointerEvent) {
1880         // Multi touch support for IE and Edge
1881         for (i = 0; i < pieceContainers.length; i++) {
1882           pieceContainers[i].addEventListener("pointerdown", this.furnitureCatalogDragAndDropListener.pointerPressed);
1883         }
1884         furnitureCatalogView.getHTMLElement().addEventListener("mousedown", this.furnitureCatalogDragAndDropListener.mousePressed);
1885         // Add pointermove and pointerup event listeners to window to capture pointer events out of the canvas 
1886         window.addEventListener("pointermove", this.furnitureCatalogDragAndDropListener.windowPointerMoved);
1887         window.addEventListener("pointerup", this.furnitureCatalogDragAndDropListener.windowPointerReleased);
1888       } else {
1889         for (i = 0; i < pieceContainers.length; i++) {
1890           pieceContainers[i].addEventListener("touchstart", this.furnitureCatalogDragAndDropListener.mousePressed);
1891         }
1892         window.addEventListener("touchmove", this.furnitureCatalogDragAndDropListener.mouseDragged);
1893         window.addEventListener("touchend", this.furnitureCatalogDragAndDropListener.windowMouseReleased);
1894         furnitureCatalogView.getHTMLElement().addEventListener("mousedown", this.furnitureCatalogDragAndDropListener.mousePressed);
1895         window.addEventListener("mousemove", this.furnitureCatalogDragAndDropListener.mouseDragged);
1896         window.addEventListener("mouseup", this.furnitureCatalogDragAndDropListener.windowMouseReleased);
1897       }
1898       furnitureCatalogView.getHTMLElement().addEventListener("contextmenu", this.furnitureCatalogDragAndDropListener.contextMenuDisplayed);
1899     }
1900     var homePane = this;
1901     this.furnitureCatalogListener = function(ev) {
1902         if (ev.getType() === CollectionEvent.Type.ADD
1903             && !homePane.furnitureCatalogListener.updater) {
1904           // Add listeners later in case more than one piece was added 
1905           homePane.furnitureCatalogListener.updater = function() {
1906               if (homePane.furnitureCatalogListener !== undefined) {
1907                 var pieceContainers = furnitureCatalogView.getHTMLElement().querySelectorAll(".furniture");
1908                 if (OperatingSystem.isInternetExplorerOrLegacyEdge()
1909                     && window.PointerEvent) {
1910                   for (i = 0; i < pieceContainers.length; i++) {
1911                     pieceContainers[i].addEventListener("pointerdown", homePane.furnitureCatalogDragAndDropListener.pointerPressed);
1912                   }
1913                 } else {
1914                   for (i = 0; i < pieceContainers.length; i++) {
1915                     pieceContainers[i].addEventListener("touchstart", homePane.furnitureCatalogDragAndDropListener.mousePressed);
1916                   }
1917                 }
1918                 delete homePane.furnitureCatalogListener.updater;
1919               }
1920             };
1921           setTimeout(homePane.furnitureCatalogListener.updater, 100);
1922         }
1923       };
1924     this.preferences.getFurnitureCatalog().addFurnitureListener(this.furnitureCatalogListener);
1925   } else if (!enabled
1926                && this.transferHandlerEnabled) {
1927     if (furnitureCatalogView != null) {
1928       var pieceContainers = furnitureCatalogView.getHTMLElement().querySelectorAll(".furniture");
1929       if (OperatingSystem.isInternetExplorerOrLegacyEdge()
1930           && window.PointerEvent) {
1931         for (i = 0; i < pieceContainers.length; i++) {
1932           pieceContainers[i].removeEventListener("pointerdown", this.furnitureCatalogDragAndDropListener.pointerPressed);            
1933         }
1934         furnitureCatalogView.getHTMLElement().removeEventListener("mousedown", this.furnitureCatalogDragAndDropListener.mousePressed);
1935         // Add pointermove and pointerup event listeners to window to capture pointer events out of the canvas 
1936         window.removeEventListener("pointermove", this.furnitureCatalogDragAndDropListener.windowPointerMoved);
1937         window.removeEventListener("pointerup", this.furnitureCatalogDragAndDropListener.windowPointerReleased);
1938       } else {
1939         for (i = 0; i < pieceContainers.length; i++) {
1940           pieceContainers[i].removeEventListener("touchstart", this.furnitureCatalogDragAndDropListener.mousePressed);
1941         }
1942         window.removeEventListener("touchmove", this.furnitureCatalogDragAndDropListener.mouseDragged);
1943         window.removeEventListener("touchend", this.furnitureCatalogDragAndDropListener.windowMouseReleased);
1944         furnitureCatalogView.getHTMLElement().removeEventListener("mousedown", this.furnitureCatalogDragAndDropListener.mousePressed);
1945         window.removeEventListener("mousemove", this.furnitureCatalogDragAndDropListener.mouseDragged);
1946         window.removeEventListener("mouseup", this.furnitureCatalogDragAndDropListener.windowMouseReleased);
1947       }
1948       furnitureCatalogView.getHTMLElement().removeEventListener("contextmenu", this.furnitureCatalogDragAndDropListener.contextMenuDisplayed);
1949     }
1950       this.preferences.getFurnitureCatalog().removeFurnitureListener(this.furnitureCatalogListener);
1951       delete this.furnitureCatalogListener;
1952   }
1953   this.transferHandlerEnabled = enabled;
1954 }
1955 
1956 /**
1957  * Returns a mouse listener for catalog that acts as catalog view, furniture view and plan transfer handlers
1958  * for drag and drop operations.
1959  * @return {javax.swing.event.MouseInputAdapter}
1960  * @private
1961  */
1962 HomePane.prototype.createFurnitureCatalogMouseListener = function() {
1963   var homePane = this;
1964   var mouseListener = {
1965       selectedPiece: null,
1966       previousCursor: null,
1967       previousView: null,
1968       escaped: false,
1969       draggedImage: null,
1970       pointerTouches: {},
1971       actionStartedInFurnitureCatalog: false,
1972       contextMenuEventType: false,
1973       mousePressed: function(ev) {
1974         if (!mouseListener.contextMenuEventType 
1975             && (ev.button === 0 || ev.targetTouches)) {
1976           if (!ev.target.classList.contains("selected")) {
1977            return;
1978           }
1979           ev.preventDefault();
1980           ev.stopPropagation();
1981           var selectedFurniture = homePane.controller.getFurnitureCatalogController().getSelectedFurniture();
1982           if (selectedFurniture.length > 0) {
1983             mouseListener.selectedPiece = selectedFurniture[0];
1984             mouseListener.previousCursor = null;
1985             mouseListener.previousView = null;
1986             mouseListener.escaped = false;
1987             
1988             homePane.inputMap ["ESCAPE"] = "EscapeDragFromFurnitureCatalog";
1989           }
1990           mouseListener.actionStartedInFurnitureCatalog = true;
1991         }
1992       },
1993       mouseDragged: function(ev) {
1994         if (!mouseListener.contextMenuEventType 
1995             && mouseListener.actionStartedInFurnitureCatalog
1996             && ((ev.buttons & 1) == 1 || ev.targetTouches)
1997             && mouseListener.selectedPiece != null) {
1998           ev.preventDefault();
1999           ev.stopPropagation();
2000 
2001           if (!mouseListener.escaped) {
2002             if (mouseListener.draggedImage == null) {
2003               var img = document.createElement("img");
2004               var originalIcon = homePane.controller.getFurnitureCatalogController().getView().getHTMLElement().querySelector(".furniture.selected .furniture-icon");
2005               img.src = originalIcon.src;
2006               var style = window.getComputedStyle(originalIcon);
2007               img.style.width = style.width;
2008               img.style.height = style.height;
2009               img.style.position = "absolute";
2010               img.style.opacity = 0.6;
2011               img.style.zIndex = 105;
2012               mouseListener.draggedImage = img;
2013               document.body.appendChild(img);
2014             }
2015             mouseListener.draggedImage.style.left = mouseListener.getCoordinates(ev).clientX + "px";
2016             mouseListener.draggedImage.style.top = mouseListener.getCoordinates(ev).clientY + "px";
2017           }
2018           
2019           var selectedLevel = homePane.home.getSelectedLevel();
2020           if (selectedLevel == null || selectedLevel.isViewable()) {
2021             var transferredFurniture = [homePane.controller.getFurnitureController().createHomePieceOfFurniture(mouseListener.selectedPiece)];
2022             var view;
2023             var pointInView = mouseListener.getPointInPlanView(ev, transferredFurniture);
2024             if (pointInView != null) {
2025               view = homePane.controller.getPlanController().getView();
2026             } else {
2027               view = homePane.controller.getFurnitureController().getView();
2028               pointInView = mouseListener.getPointInFurnitureView(ev);
2029             }
2030 
2031             if (mouseListener.previousView !== view) {
2032               if (mouseListener.previousView != null) {
2033                 if (mouseListener.previousView === homePane.controller.getPlanController().getView()
2034                     && !mouseListener.escaped) {
2035                   homePane.controller.getPlanController().stopDraggedItems();
2036                 }
2037                 var component = mouseListener.previousView;
2038                 if (component && typeof component.setCursor === "function") {
2039                   component.setCursor(mouseListener.previousCursor);
2040                 }
2041                 mouseListener.previousCursor = null;
2042                 mouseListener.previousView = null;
2043               }
2044               if (view != null) {
2045                 var component = view;
2046                 mouseListener.previousCursor = "default";
2047                 mouseListener.previousView = view;
2048                 if (!mouseListener.escaped) {
2049                   if (typeof component.setCursor === "function") {
2050                     component.setCursor("copy");
2051                   }
2052                   if (view === homePane.controller.getPlanController().getView()) {
2053                     homePane.controller.getPlanController().startDraggedItems(transferredFurniture, pointInView [0], pointInView [1]);
2054                   }
2055                 }
2056               }
2057             } else if (pointInView != null) {
2058               homePane.controller.getPlanController().moveMouse(pointInView [0], pointInView [1]);
2059             }
2060           }
2061         }
2062       },
2063       getPointInPlanView: function(ev, transferredFurniture) {
2064         var planView = homePane.controller.getPlanController().getView();
2065         if (planView != null) {
2066           var rect = planView.getHTMLElement().getBoundingClientRect();
2067           var coords = mouseListener.getCoordinates(ev);
2068           if (coords.clientX >= rect.left 
2069               && coords.clientX < rect.left + rect.width
2070               && coords.clientY >= rect.top 
2071               && coords.clientY < rect.top + rect.height) {
2072             return [planView.convertXPixelToModel(coords.clientX - rect.left), planView.convertYPixelToModel(coords.clientY - rect.top)];
2073           }
2074         }
2075         return null;
2076       },
2077       getPointInView3D: function(ev) {
2078         var view3D = homePane.controller.getHomeController3D().getView();
2079         if (view3D != null) {
2080           var rect = view3D.getHTMLElement().getBoundingClientRect();
2081           var coords = mouseListener.getCoordinates(ev);
2082           if (coords.clientX >= rect.left 
2083               && coords.clientX < rect.left + rect.width
2084               && coords.clientY >= rect.top 
2085               && coords.clientY < rect.top + rect.height) {
2086             return [coords.clientX - rect.left, coords.clientY - rect.top];
2087           }
2088         }
2089         return null;
2090       },
2091       getCoordinates: function(ev) {
2092         if (ev.targetTouches) {
2093           if (ev.targetTouches.length === 1) {
2094             return { clientX: ev.targetTouches[0].clientX, clientY: ev.targetTouches[0].clientY };
2095           } else if (ev.targetTouches.length === 0 && ev.changedTouches.length === 1) {
2096             return { clientX: ev.changedTouches[0].clientX, clientY: ev.changedTouches[0].clientY };
2097           }
2098         }
2099         return ev;
2100       },
2101       getPointInFurnitureView: function(ev) {
2102         var furnitureView = homePane.controller.getFurnitureController().getView();
2103         if (furnitureView != null) {
2104           var rect = furnitureView.getHTMLElement().getBoundingClientRect();
2105           var coords = mouseListener.getCoordinates(ev);
2106           if (coords.clientX >= rect.left && coords.clientX < rect.left + rect.width
2107               && coords.clientY >= rect.top && coords.clientY < rect.top + rect.height) {
2108             return [0, 0];
2109           }
2110         }
2111         return null;
2112       },
2113       contextMenuDisplayed: function(ev) {
2114         mouseListener.contextMenuEventType = true;
2115       },
2116       windowMouseReleased: function(ev) {
2117         if (mouseListener.actionStartedInFurnitureCatalog) {
2118           if (mouseListener.draggedImage != null) {
2119             document.body.removeChild(mouseListener.draggedImage);
2120             mouseListener.draggedImage = null;
2121           }
2122           if (!mouseListener.contextMenuEventType) {
2123             if ((ev.button === 0 || ev.targetTouches) && mouseListener.selectedPiece != null) {
2124               ev.preventDefault();
2125               if (!mouseListener.escaped) {
2126                 var selectedLevel = homePane.home.getSelectedLevel();
2127                 if (selectedLevel == null || selectedLevel.isViewable()) {
2128                   var transferredFurniture = [homePane.controller.getFurnitureController().createHomePieceOfFurniture(mouseListener.selectedPiece)];
2129                   var view;
2130                   var pointInView = mouseListener.getPointInPlanView(ev, transferredFurniture);
2131                   if (pointInView != null) {
2132                     homePane.controller.getPlanController().stopDraggedItems();
2133                     view = homePane.controller.getPlanController().getView();
2134                     homePane.controller.drop(transferredFurniture, view, pointInView [0], pointInView [1]);
2135                     var view = mouseListener.previousView;
2136                     if (view && typeof view.setCursor === "function") {
2137                       view.setCursor(this.previousCursor);
2138                     }
2139                   } else if (homePane.preferences.isEditingIn3DViewEnabled()) {
2140                     pointInView3D = mouseListener.getPointInView3D(ev);
2141                     if (pointInView3D !== null) {
2142                       view = homePane.controller.getHomeController3D().getView();
2143                       var dropLevel = homePane.getDropModelLevel(view, pointInView3D);
2144                       var dropLocation = homePane.getDropModelLocation(view, transferredFurniture, dropLevel, pointInView3D);
2145                       homePane.controller.drop(transferredFurniture, view, dropLevel,
2146                           dropLocation [0], dropLocation [1], dropLocation.length === 3 ? dropLocation [2] : null);
2147                     }
2148                   }
2149                 }
2150               }
2151             }
2152           }
2153         }
2154         mouseListener.selectedPiece = null;
2155         mouseListener.actionStartedInFurnitureCatalog = false;
2156         mouseListener.contextMenuEventType = false;
2157         delete homePane.inputMap ["ESCAPE"];
2158       },
2159       pointerPressed : function(ev) {
2160         if (ev.pointerType != "mouse") {
2161           // Multi touch support for IE and Edge
2162           mouseListener.copyPointerToTargetTouches(ev);
2163           return; // Don't support drag and drop from catalog on touch screens under IE/Edge
2164         }
2165         mouseListener.mousePressed(ev);
2166       },
2167       windowPointerMoved : function(ev) {
2168         if (ev.pointerType != "mouse") {
2169           // Multi touch support for IE and Edge
2170           mouseListener.copyPointerToTargetTouches(ev);
2171           return; // Don't support drag and drop from catalog on touch screens under IE/Edge
2172         }
2173         mouseListener.mouseDragged(ev);
2174       },
2175       windowPointerReleased : function(ev) {
2176         if (ev.pointerType != "mouse") {
2177           delete mouseListener.pointerTouches [ev.pointerId];
2178           return; // Don't support drag and drop from catalog on touch screens under IE/Edge
2179         }
2180         mouseListener.windowMouseReleased(ev);
2181       },
2182       copyPointerToTargetTouches : function(ev) {
2183         // Copy the IE and Edge pointer location to ev.targetTouches
2184         mouseListener.pointerTouches [ev.pointerId] = {clientX : ev.clientX, clientY : ev.clientY};
2185         ev.targetTouches = [];
2186         for (var attribute in mouseListener.pointerTouches) {
2187           if (mouseListener.pointerTouches.hasOwnProperty(attribute)) {
2188             ev.targetTouches.push(mouseListener.pointerTouches [attribute]);
2189           }
2190         }
2191       }
2192     };
2193   
2194    var escapeAction = {
2195      actionPerformed: function() {
2196         if (!mouseListener.escaped) {
2197           if (mouseListener.previousView != null) {
2198             if (mouseListener.previousView === homePane.controller.getPlanController().getView()) {
2199               homePane.controller.getPlanController().stopDraggedItems();
2200             }
2201             if (mouseListener.previousCursor != null && typeof mouseListener.previousView.setCursor === "function") {
2202               mouseListener.previousView.setCursor(mouseListener.previousCursor);
2203             }
2204           }
2205           mouseListener.escaped = true;
2206           if (mouseListener.draggedImage != null) {
2207             document.body.removeChild(mouseListener.draggedImage);
2208             mouseListener.draggedImage = null;
2209           }
2210         }
2211       }
2212     };
2213   this.getActionMap() ["EscapeDragFromFurnitureCatalog"] = escapeAction;
2214 
2215   return mouseListener;
2216 }
2217 
2218 /**
2219  * Returns the level where drop location should occur.
2220  * @private
2221  */
2222  HomePane.prototype.getDropModelLevel = function(destination, dropLocation) {
2223   if (destination instanceof HomeComponent3D) {
2224     var view3D = destination;
2225     var closestItem = view3D.getClosestSelectableItemAt(dropLocation [0], dropLocation [1]);
2226     var selectedLevel = this.home.getSelectedLevel();
2227     if (closestItem != null
2228         && typeof closestItem.isAtLevel === "function" // closestItem instanceof Elevatable
2229         && !closestItem.isAtLevel(selectedLevel)) {
2230       return closestItem.getLevel();
2231     }
2232   }
2233   return this.home.getSelectedLevel();
2234 }
2235   
2236 /**
2237  * Returns the drop location converted in model coordinates space.
2238  * @private
2239  */
2240  HomePane.prototype.getDropModelLocation = function(destination, transferedItems, dropLevel, dropLocation) {
2241   var floorLocation = [0, 0, 0];
2242   if (destination instanceof HomeComponent3D) {
2243     var view3D = destination;
2244     var closestItem = view3D.getClosestSelectableItemAt(dropLocation [0], dropLocation [1]);
2245     var floorElevation = 0;
2246     if (dropLevel != null) {
2247       floorElevation = dropLevel.getElevation();
2248     }
2249     if (closestItem instanceof HomePieceOfFurniture) {
2250       floorLocation = [closestItem.getX(), closestItem.getY()];
2251       if (transferedItems.length === 1
2252           && transferedItems [0] instanceof HomePieceOfFurniture) {
2253         var pointOnFloor = view3D.getVirtualWorldPointAt(dropLocation [0], dropLocation [1], floorElevation);
2254         var intersectionWithPieceMiddle = this.computeIntersection(pointOnFloor [0], pointOnFloor [1], this.home.getCamera().getX(), this.home.getCamera().getY(),
2255             floorLocation [0], floorLocation [1], floorLocation [0] + Math.cos(closestItem.getAngle()), floorLocation [1] + Math.sin(closestItem.getAngle()));
2256         if (java.awt.geom.Point2D.distance(intersectionWithPieceMiddle [0], intersectionWithPieceMiddle [1], closestItem.getX(), closestItem.getY()) < closestItem.getWidth() / 2) {
2257           floorLocation = intersectionWithPieceMiddle;
2258         }
2259         var transferedPiece = transferedItems [0];
2260         floorLocation [0] -= transferedPiece.getWidth() / 2;
2261         floorLocation [1] -= transferedPiece.getDepth() / 2;
2262         var elevation;
2263         if (closestItem instanceof HomeShelfUnit) {
2264           var camera = this.home.getCamera();
2265           var distancePointOnFloorToCamera = java.awt.geom.Point2D.distance(pointOnFloor [0], pointOnFloor [1], camera.getX(), camera.getY());
2266           var distancePointOnFloorToLocation = java.awt.geom.Point2D.distance(pointOnFloor [0], pointOnFloor [1], floorLocation [0], floorLocation [1]);
2267           var elevation = (camera.getZ() - (this.home.getSelectedLevel() !== null ? this.home.getSelectedLevel().getElevation() : 0))
2268               / distancePointOnFloorToCamera * distancePointOnFloorToLocation;
2269         } else if (closestItem.isHorizontallyRotated()) {
2270           elevation = closestItem.getElevation() + closestItem.getHeightInPlan();
2271         } else if (closestItem.getDropOnTopElevation() >= 0) {
2272           elevation = closestItem.getElevation() + closestItem.getHeight() * closestItem.getDropOnTopElevation();
2273         } else {
2274           elevation = 0;
2275         }
2276         floorLocation = [floorLocation [0], floorLocation [1], elevation];          
2277       }
2278     } else if (closestItem instanceof Wall
2279                 && closestItem.getArcExtent() === null
2280                 && transferedItems.length === 1) {
2281       var pointOnFloor = view3D.getVirtualWorldPointAt(dropLocation [0], dropLocation [1], floorElevation);
2282       // Compute intersection between camera - pointOnFloor line and left/right sides of the wall
2283       var wall = closestItem;
2284       var wallPoints = wall.getPoints();
2285       var leftSideIntersection = this.computeIntersection(pointOnFloor [0], pointOnFloor [1], this.home.getCamera().getX(), this.home.getCamera().getY(),
2286           wallPoints [0][0], wallPoints [0][1], wallPoints [1][0], wallPoints [1][1]);
2287       var rightSideIntersection = this.computeIntersection(pointOnFloor [0], pointOnFloor [1], this.home.getCamera().getX(), this.home.getCamera().getY(),
2288           wallPoints [3][0], wallPoints [3][1], wallPoints [2][0], wallPoints [2][1]);
2289       if (java.awt.geom.Point2D.distanceSq(this.home.getCamera().getX(), this.home.getCamera().getY(), leftSideIntersection [0], leftSideIntersection [1])
2290            < java.awt.geom.Point2D.distanceSq(this.home.getCamera().getX(), this.home.getCamera().getY(), rightSideIntersection [0], rightSideIntersection [1])) {
2291         floorLocation = leftSideIntersection;
2292       } else {
2293         floorLocation = rightSideIntersection;
2294       }
2295       if (transferedItems [0] instanceof HomePieceOfFurniture) {
2296         var transferedPiece = transferedItems [0];
2297         var wallYawAngle = Math.atan((wall.getYEnd() - wall.getYStart()) / (wall.getXEnd() - wall.getXStart()));
2298         floorLocation [0] -= transferedPiece.getWidth() / 2 * Math.cos(wallYawAngle);
2299         floorLocation [1] -= transferedPiece.getWidth() / 2 * Math.sin(wallYawAngle);
2300       }
2301     } else if (!this.home.isEmpty()) {
2302       floorLocation = view3D.getVirtualWorldPointAt(dropLocation [0], dropLocation [1], floorElevation);
2303       floorLocation = [floorLocation [0], floorLocation [1]];
2304       if (transferedItems.length === 1
2305           && transferedItems [0] instanceof HomePieceOfFurniture) {
2306         var transferedPiece = transferedItems [0];
2307         floorLocation [0] -= transferedPiece.getWidth() / 2;
2308         floorLocation [1] -= transferedPiece.getDepth() / 2;
2309       }
2310     }
2311   }
2312   return floorLocation;
2313 }
2314 
2315 /**
2316  * Returns the intersection point between the line joining the first two points and
2317  * the line joining the two last points.
2318  * @private
2319  */
2320  HomePane.prototype.computeIntersection = function(xPoint1, yPoint1, xPoint2, yPoint2,
2321                                                    xPoint3, yPoint3, xPoint4, yPoint4) {
2322   var x = xPoint2;
2323   var y = yPoint2;
2324   var alpha1 = (yPoint2 - yPoint1) / (xPoint2 - xPoint1);
2325   var alpha2 = (yPoint4 - yPoint3) / (xPoint4 - xPoint3);
2326   // If the two lines are not parallel
2327   if (alpha1 !== alpha2) {
2328     // If first line is vertical
2329     if (Math.abs(alpha1) > 4000)  {
2330       if (Math.abs(alpha2) < 4000) {
2331         x = xPoint1;
2332         var beta2  = yPoint4 - alpha2 * xPoint4;
2333         y = alpha2 * x + beta2;
2334       }
2335     // If second line is vertical
2336     } else if (Math.abs(alpha2) > 4000) {
2337       if (Math.abs(alpha1) < 4000) {
2338         x = xPoint3;
2339         var beta1  = yPoint2 - alpha1 * xPoint2;
2340         y = alpha1 * x + beta1;
2341       }
2342     } else {
2343       var sameSignum = alpha1 > 0 && alpha2 > 0 || alpha1 < 0 && alpha2 < 0;
2344       if (Math.abs(alpha1 - alpha2) > 1E-5
2345           && (!sameSignum || (Math.abs(alpha1) > Math.abs(alpha2)   ? alpha1 / alpha2   : alpha2 / alpha1) > 1.004)) {
2346         var beta1  = yPoint2 - alpha1 * xPoint2;
2347         var beta2  = yPoint4 - alpha2 * xPoint4;
2348         x = (beta2 - beta1) / (alpha1 - alpha2);
2349         y = alpha1 * x + beta1;
2350       }
2351     }
2352   }
2353   return [x, y];
2354 }
2355 
2356 /**
2357  * Detaches the given <code>view</code> from home view.
2358  * @param {Object} view
2359  * @ignore
2360  */
2361 HomePane.prototype.detachView = function(view) {
2362 }
2363 
2364 /**
2365  * Attaches the given <code>view</code> to home view.
2366  * @param {Object} view
2367  * @ignore
2368  */
2369 HomePane.prototype.attachView = function(view) {
2370 }
2371 
2372 /**
2373  * Displays a content chooser open dialog to choose the name of a home.
2374  * @return {string}
2375  * @ignore
2376  */
2377 HomePane.prototype.showOpenDialog = function() {
2378 }
2379 
2380 /**
2381  * Displays a dialog to let the user choose a home example.
2382  * @return {string}
2383  * @ignore
2384  */
2385 HomePane.prototype.showNewHomeFromExampleDialog = function() {
2386 }
2387   
2388 /**
2389  * Displays a dialog that lets user choose what he wants to do with a damaged home he tries to open it.
2390  * @return {HomeView.OpenDamagedHomeAnswer} {@link com.eteks.sweethome3d.viewcontroller.HomeView.OpenDamagedHomeAnswer#REMOVE_DAMAGED_ITEMS}
2391  * if the user chose to remove damaged items,
2392  * {@link com.eteks.sweethome3d.viewcontroller.HomeView.OpenDamagedHomeAnswer#REPLACE_DAMAGED_ITEMS}
2393  * if he doesn't want to replace damaged items by red images and red boxes,
2394  * or {@link com.eteks.sweethome3d.viewcontroller.HomeView.OpenDamagedHomeAnswer#DO_NOT_OPEN_HOME}
2395  * if he doesn't want to open damaged home.
2396  * @param {string} homeName
2397  * @param {Home} damagedHome
2398  * @param {Object[]} invalidContent
2399  * @ignore
2400  */
2401 HomePane.prototype.confirmOpenDamagedHome = function(homeName, damagedHome, invalidContent) {
2402   return true;
2403 }
2404 
2405 /**
2406  * Displays a content chooser open dialog to choose a language library.
2407  * @return {string}
2408  * @ignore
2409  */
2410 HomePane.prototype.showImportLanguageLibraryDialog = function() {
2411 }
2412 
2413 /**
2414  * Displays a dialog that lets user choose whether he wants to overwrite
2415  * an existing language library or not.
2416  * @param {string} languageLibraryName
2417  * @return {boolean}
2418  * @ignore
2419  */
2420 HomePane.prototype.confirmReplaceLanguageLibrary = function(languageLibraryName) {
2421   return true;
2422 }
2423 
2424 /**
2425  * Displays a content chooser open dialog to choose a furniture library.
2426  * @return {string}
2427  * @ignore
2428  */
2429 HomePane.prototype.showImportFurnitureLibraryDialog = function() {
2430 }
2431 
2432 /**
2433  * Displays a dialog that lets user choose whether he wants to overwrite
2434  * an existing furniture library or not.
2435  * @param {string} furnitureLibraryName
2436  * @return {boolean}
2437  * @ignore
2438  */
2439 HomePane.prototype.confirmReplaceFurnitureLibrary = function(furnitureLibraryName) {
2440   return true;
2441 }
2442 
2443 /**
2444  * Displays a content chooser open dialog to choose a textures library.
2445  * @return {string}
2446  * @ignore
2447  */
2448 HomePane.prototype.showImportTexturesLibraryDialog = function() {
2449 }
2450 
2451 /**
2452  * Displays a dialog that lets user choose whether he wants to overwrite
2453  * an existing textures library or not.
2454  * @param {string} texturesLibraryName
2455  * @return {boolean}
2456  * @ignore
2457  */
2458 HomePane.prototype.confirmReplaceTexturesLibrary = function(texturesLibraryName) {
2459   return true;
2460 }
2461 
2462 /**
2463  * Displays a dialog that lets user choose whether he wants to overwrite
2464  * an existing plug-in or not.
2465  * @param {string} pluginName
2466  * @return {boolean}
2467  * @ignore
2468  */
2469 HomePane.prototype.confirmReplacePlugin = function(pluginName) {
2470   return true;
2471 }
2472 
2473 /**
2474  * Displays a content chooser save dialog to choose the name of a home.
2475  * @param {string} homeName
2476  * @return {string}
2477  * @ignore
2478  */
2479 HomePane.prototype.showSaveDialog = function(homeName) {
2480   return null;
2481 }
2482 
2483 /**
2484  * Displays <code>message</code> in an error message box.
2485  * @param {string} message
2486  * @ignore
2487  */
2488 HomePane.prototype.showError = function(message) {
2489   alert(message.indexOf("<html>") < 0 ? message : message.replace(/\<\/?\w+(\s+\w+\=[\"\'][^\"\']+[\"\'])*\>/g, " ").replace(/\s+/g, " "));
2490 }
2491 
2492 /**
2493  * Displays <code>message</code> in a message box.
2494  * @param {string} message
2495  * @ignore
2496  */
2497 HomePane.prototype.showMessage = function(message) {
2498   alert(message.indexOf("<html>") < 0 ? message : message.replace(/\<\/?\w+(\s+\w+\=[\"\'][^\"\']+[\"\'])*\>/g, " ").replace(/\s+/g, " "));
2499 }
2500 
2501 /**
2502  * Displays the tip matching <code>actionTipKey</code> and
2503  * returns <code>true</code> if the user chose not to display again the tip.
2504  * @param {string} actionTipKey
2505  * @return {boolean}
2506  * @ignore
2507  */
2508 HomePane.prototype.showActionTipMessage = function(actionTipKey) {
2509   return false;
2510 }
2511 
2512 /**
2513  * Displays a dialog that lets user choose whether he wants to save
2514  * the current home or not.
2515  * @return {@link com.eteks.sweethome3d.viewcontroller.HomeView.SaveAnswer#CANCEL}
2516  * @param {string} homeName
2517  * @param {function} saveHome callback with a boolean parameter equal to true if the user confirmed to save
2518  * @ignore 
2519  */
2520 HomePane.prototype.confirmSave = function(homeName, saveHome) {
2521   var message;
2522   if (homeName != null) {
2523     message = this.preferences.getLocalizedString("HomePane", "confirmSave.message", '"' + homeName + '"');
2524   } else {
2525     message = this.preferences.getLocalizedString("HomePane", "confirmSave.message", " ");
2526   }
2527 
2528   var confirmSavingDialog = new JSDialog(this.preferences, 
2529       this.preferences.getLocalizedString("HomePane", "confirmSave.title"), 
2530       message.replace(/\<br\>/, " ") + "</font>", 
2531       { 
2532         size: "small",
2533         applier: function() {
2534           saveHome(true);
2535         }
2536       });
2537   confirmSavingDialog.findElement(".dialog-ok-button").innerHTML = 
2538       this.preferences.getLocalizedString("HomePane", "confirmSave.save");
2539   var cancelButton = confirmSavingDialog.findElement(".dialog-cancel-button");
2540   cancelButton.innerHTML = this.preferences.getLocalizedString("HomePane", "confirmSave.cancel");
2541   var doNotSaveButton = document.createElement("button");
2542   doNotSaveButton.innerHTML = this.preferences.getLocalizedString("HomePane", "confirmSave.doNotSave");
2543   confirmSavingDialog.registerEventListener(doNotSaveButton, "click", function() {
2544       confirmSavingDialog.close();
2545       saveHome(false);
2546     });
2547   cancelButton.parentElement.insertBefore(doNotSaveButton, cancelButton);
2548   confirmSavingDialog.displayView();
2549   return HomeView.SaveAnswer.CANCEL;
2550 }
2551 
2552 /**
2553  * Displays a dialog that let user choose whether he wants to save
2554  * a home that was created with a newer version of Sweet Home 3D.
2555  * @return {boolean} <code>true</code> if user confirmed to save.
2556  * @param {string} homeName
2557  * @ignore
2558  */
2559 HomePane.prototype.confirmSaveNewerHome = function(homeName) {
2560   return true;
2561 }
2562   
2563 /**
2564  * Displays a dialog that let user choose whether he wants to exit
2565  * application or not.
2566  * @return {boolean} <code>true</code> if user confirmed to exit.
2567  * @ignore
2568  */
2569 HomePane.prototype.confirmExit = function() {
2570   return true;
2571 }
2572 
2573 /**
2574  * Displays an about dialog.
2575  */
2576 HomePane.prototype.showAboutDialog = function() {
2577   var message = this.preferences.getLocalizedString("HomePane", "about.message", this.controller.getVersion());
2578   var template = "<table><tr><td><img src='"+ ZIPTools.getScriptFolder() + this.preferences.getLocalizedString("HomePane", "about.icon") + "'></td>"
2579                  + "<td>" + message + "</td></tr></table>";
2580   var aboutDialog = new JSDialog(this.preferences, 
2581       this.preferences.getLocalizedString("HomePane", "about.title"), 
2582       template, { size: "medium" });
2583   aboutDialog.getHTMLElement().classList.add("about-dialog");
2584   aboutDialog.displayView();
2585 }
2586 
2587 /**
2588  * Displays the given message and returns <code>false</code> if the user
2589  * doesn't want to be informed of the shown updates anymore.
2590  * @param {string} updatesMessage the message to display
2591  * @param {boolean} showOnlyMessage if <code>false</code> a check box proposing not to display
2592  * again shown updates will be shown.
2593  * @return {boolean}
2594  * @ignore
2595  */
2596 HomePane.prototype.showUpdatesMessage = function(updatesMessage, showOnlyMessage) {
2597   return false;
2598 }
2599 
2600 /**
2601  * Shows a print dialog to print the home displayed by this pane.
2602  * @return {function(): Object} a print task to execute or <code>null</code> if the user canceled print.
2603  * The <code>call</code> method of the returned task may throw a
2604  * {@link RecorderException} exception if print failed
2605  * or an {@link InterruptedRecorderException}
2606  * exception if it was interrupted.
2607  * @ignore
2608  */
2609 HomePane.prototype.showPrintDialog = function() {
2610   return null;
2611 }
2612 
2613 /**
2614  * Shows a content chooser save dialog to print a home in a PDF file.
2615  * @param {string} homeName
2616  * @return {string}
2617  * @ignore
2618  */
2619 HomePane.prototype.showPrintToPDFDialog = function(homeName) {
2620   return null;
2621 }
2622 
2623 /**
2624  * Prints a home to a given PDF file. This method may be overridden
2625  * to write to another kind of output stream.
2626  * @param {string} pdfFile
2627  * @ignore
2628  */
2629 HomePane.prototype.printToPDF = function(pdfFile) {
2630 }
2631 
2632 /**
2633  * Shows a content chooser save dialog to export furniture list in a CSV file.
2634  * @param {string} homeName
2635  * @return {string}
2636  * @ignore
2637  */
2638 HomePane.prototype.showExportToCSVDialog = function(homeName) {
2639   return null;
2640 }
2641 
2642 /**
2643  * Exports furniture list to a given CSV file.
2644  * @param {string} csvFile
2645  * @ignore
2646  */
2647 HomePane.prototype.exportToCSV = function(csvFile) {
2648 }
2649 
2650 /**
2651  * Shows a content chooser save dialog to export a home plan in a SVG file.
2652  * @param {string} homeName
2653  * @return {string}
2654  * @ignore
2655  */
2656 HomePane.prototype.showExportToSVGDialog = function(homeName) {
2657   return null;
2658 }
2659 
2660 /**
2661  * Exports the plan objects to a given SVG file.
2662  * @param {string} svgFile
2663  * @ignore
2664  */
2665 HomePane.prototype.exportToSVG = function(svgFile) {
2666 }
2667 
2668 /**
2669  * Shows a content chooser save dialog to export a 3D home in a OBJ file.
2670  * @param {string} homeName
2671  * @return {string}
2672  * @ignore
2673  */
2674 HomePane.prototype.showExportToOBJDialog = function(homeName) {
2675    return null;
2676 }
2677 
2678 /**
2679  * Exports to an OBJ file the objects of the 3D view created with the given factory.
2680  * @param {string} objFile
2681  * @param {Object} object3dFactory
2682  * @ignore
2683  */
2684 HomePane.prototype.exportToOBJ = function(objFile, object3dFactory) {
2685 }
2686 
2687 
2688 /**
2689  * Displays a dialog that let user choose whether he wants to delete
2690  * the selected furniture from catalog or not.
2691  * @return {boolean} <code>true</code> if user confirmed to delete.
2692  * @ignore
2693  */
2694 HomePane.prototype.confirmDeleteCatalogSelection = function() {
2695   return true;
2696 }
2697 
2698 /**
2699  * Displays a dialog that lets the user choose a name for the current camera.
2700  * @return {null} the chosen name or <code>null</code> if the user canceled.
2701  * @param {string} cameraName default name
2702  * @ignore
2703  */
2704 HomePane.prototype.showStoreCameraDialog = function(cameraName) {
2705   return prompt(this.preferences.getLocalizedString("HomePane", "showStoreCameraDialog.message"), cameraName);
2706 }
2707 
2708 /**
2709  * Displays a dialog showing the list of cameras stored in home
2710  * and returns <code>null</code> to delete selected cameras asynchronously.
2711  */
2712 HomePane.prototype.showDeletedCamerasDialog = function() {
2713   var homePane = this;
2714   var storedCameras = this.home.getStoredCameras();
2715 
2716   function JSConfirmDeleteCamerasDialog() {
2717     JSDialog.call(this, homePane.preferences,
2718         "@{HomePane.showDeletedCamerasDialog.title}",
2719         "<div>@{HomePane.confirmDeleteCameras.message}</div>",
2720         {
2721           applier: function(dialog) {
2722             homePane.controller.getHomeController3D().deleteCameras(dialog.selectedCameras);
2723           },
2724         });
2725     
2726     var confirmDialog = this;
2727     var cancelButton = this.findElement(".dialog-cancel-button");
2728     this.registerEventListener(cancelButton, "click", function(ev) {
2729         confirmDialog.cancel();
2730       });
2731     var okButtons = this.findElements(".dialog-ok-button");
2732     this.registerEventListener(okButtons, "click", function(ev) {
2733         confirmDialog.validate();
2734       });
2735   }
2736   JSConfirmDeleteCamerasDialog.prototype = Object.create(JSDialog.prototype);
2737   JSConfirmDeleteCamerasDialog.prototype.constructor = JSConfirmDeleteCamerasDialog;
2738 
2739   JSConfirmDeleteCamerasDialog.prototype.appendButtons = function(buttonsPanel) {
2740     buttonsPanel.innerHTML = JSComponent.substituteWithLocale(this.preferences,
2741         "<button class='dialog-cancel-button'>@{HomePane.confirmDeleteCameras.cancel}</button>" +
2742         "<button class='dialog-ok-button'>@{HomePane.confirmDeleteCameras.delete}</button>");
2743   }
2744 
2745   var html = "<div>@{HomePane.showDeletedCamerasDialog.message}</div><br />";
2746   for (var i = 0; i < storedCameras.length; i++) {
2747     html += "<div><label><input type='checkbox' value='" + i + "' />" + storedCameras[i].getName() + "</label></div>";
2748   }
2749 
2750   function JSDeleteCamerasDialog() {
2751     JSDialog.call(this, homePane.preferences,
2752       "@{HomePane.showDeletedCamerasDialog.title}",
2753       html,
2754       {
2755         applier: function(dialog) {
2756           var checkboxes = dialog.findElements("input[type='checkbox']:checked");
2757           var selectedCameras = [];
2758           for (var i = 0; i < checkboxes.length; i++) {
2759             var cameraIndex = parseInt(checkboxes[i].value);
2760             var camera = storedCameras[cameraIndex];
2761             selectedCameras.push(camera);
2762           }
2763 
2764           if (selectedCameras.length > 0) {
2765             var confirmDialog = new JSConfirmDeleteCamerasDialog();
2766             confirmDialog.selectedCameras = selectedCameras;
2767             confirmDialog.displayView();
2768           }
2769         },
2770       });
2771   }
2772   JSDeleteCamerasDialog.prototype = Object.create(JSDialog.prototype);
2773   JSDeleteCamerasDialog.prototype.constructor = JSDeleteCamerasDialog;
2774 
2775   var dialog = new JSDeleteCamerasDialog();
2776   dialog.displayView();
2777   return null;
2778 }
2779 
2780 /**
2781  * Returns <code>true</code> if clipboard doesn't contain data that
2782  * components are able to handle.
2783  * @return {boolean}
2784  */
2785 HomePane.prototype.isClipboardEmpty = function() {
2786   return this.clipboardEmpty;
2787 }
2788 
2789 /**
2790  * Returns the list of selectable items that are currently in clipboard
2791  * or <code>null</code> if clipboard doesn't contain any selectable item.
2792  * @return {Object[]}
2793  */
2794 HomePane.prototype.getClipboardItems = function() {
2795   return this.clipboard;
2796 }
2797 
2798 /**
2799  * Execute <code>runnable</code> asynchronously in the thread
2800  * that manages toolkit events.
2801  * @param {function} runnable
2802  * @ignore
2803  */
2804 HomePane.prototype.invokeLater = function(runnable) {
2805   setTimeout(runnable);
2806 }
2807 
2808 /**
2809  * Removes components added to this pane and their listeners.
2810  */
2811 HomePane.prototype.dispose = function() {
2812   this.setTransferEnabled(false);
2813   
2814   var furnitureCatalogView = this.controller.getFurnitureCatalogController().getView();
2815   var furnitureView = this.controller.getFurnitureController().getView();
2816   var planView = this.controller.getPlanController().getView();
2817   var view3D = this.controller.getHomeController3D().getView();
2818   if (view3D != null) {
2819     view3D.dispose();
2820   }
2821   if (planView != null) {
2822     planView.dispose();
2823   }
2824   if (furnitureView != null) {
2825     furnitureView.dispose();
2826   }
2827   if (furnitureCatalogView != null) {
2828     furnitureCatalogView.dispose();
2829   }
2830   
2831   if (this.view3DPopupMenu != null) {
2832     document.body.removeChild(this.view3DPopupMenu.getHTMLElement());
2833   }
2834   if (this.planPopupMenu != null) {
2835     document.body.removeChild(this.planPopupMenu.getHTMLElement());
2836   }
2837   if (this.furniturePopupMenu != null) {
2838     document.body.removeChild(this.furniturePopupMenu.getHTMLElement());
2839   }
2840   if (this.furnitureCatalogPopupMenu != null) {
2841     document.body.removeChild(this.furnitureCatalogPopupMenu.getHTMLElement());
2842   }
2843   
2844   if (this.levelSelector) {
2845     while (this.levelSelector.children.length > 0) {
2846       this.levelSelector.removeChild(this.levelSelector.children[0]);
2847     }
2848     this.levelSelector.removeEventListener("change", this.levelSelectorChangeListener);
2849   }
2850   
2851   document.removeEventListener("keydown", this.keydownListener);
2852   
2853   window.removeEventListener("resize", this.resizeListener);
2854   var splitters = [this.catalogFurnitureSplitter, this.furniturePlanSplitter, this.plan3DViewSplitter];
2855   for (var i = 0; i < splitters.length; i++) {
2856     var splitter = splitters [i];
2857     splitter.element.removeEventListener("mousedown", splitter.mouseListener.mousePressed, true);
2858     splitter.element.removeEventListener("touchstart", splitter.mouseListener.mousePressed, true);
2859     window.removeEventListener("resize", splitter.mouseListener.windowResized);
2860   }
2861   var applicationMenuToolBar = document.getElementById("application-menu-toolbar");
2862   if (applicationMenuToolBar != null) {
2863     var toolBarChildren = applicationMenuToolBar.children;
2864     for (var i = toolBarChildren.length - 1; i >= 0; i--) {
2865       applicationMenuToolBar.removeChild(toolBarChildren [i]);
2866     } 
2867   }
2868   var toolBar = document.getElementById("home-pane-toolbar");
2869   toolBarChildren = toolBar.children;
2870   for (var i = toolBarChildren.length - 1; i >= 0; i--) {
2871     if (this.toolBarDefaultChildren.indexOf(toolBarChildren [i]) < 0) {
2872       toolBar.removeChild(toolBarChildren [i]);
2873     }
2874   } 
2875   this.getHTMLElement().removeEventListener("focusin", this.focusListener);
2876   this.preferences.removePropertyChangeListener("VALUE_ADDED_TAX_ENABLED", this.preferencesChangeListener);
2877   this.preferences.removePropertyChangeListener("CURRENCY", this.preferencesChangeListener);
2878   if (this.furnitureCatalogListener != null) {
2879     this.preferences.getFurnitureCatalog().removeFurnitureListener(this.furnitureCatalogListener);
2880   }
2881 }
2882