1 /* See license.txt for terms of usage */
  2 
  3 define([
  4     "firebug/lib/object",
  5     "firebug/lib/locale",
  6     "firebug/firebug",
  7     "firebug/lib/dom",
  8     "firebug/chrome/menu",
  9 ],
 10 function(Obj, Locale, Firebug, Dom, Menu) {
 11 
 12 // ************************************************************************************************
 13 // Constants
 14 
 15 const Cc = Components.classes;
 16 const Ci = Components.interfaces;
 17 
 18 const MAX_HISTORY_MENU_ITEMS = 15;
 19 
 20 // ************************************************************************************************
 21 
 22 /**
 23  * @class Support for back and forward pattern for navigating within Firebug UI (panels).
 24  */
 25 Firebug.NavigationHistory = Obj.extend(Firebug.Module,
 26 {
 27     dispatchName: "navigationHistory",
 28 
 29     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 30     // Extending Module
 31 
 32     initContext: function(context, persistedState)
 33     {
 34         Firebug.Module.initContext.apply(this, arguments);
 35 
 36         // Initialize context members.
 37         context.navigationHistory = [];
 38         context.navigationHistoryIndex = 0;
 39 
 40         if (persistedState && persistedState.navigationHistory)
 41         {
 42             context.navigationHistory = persistedState.navigationHistory;
 43             context.navigationHistoryIndex = persistedState.navigationHistoryIndex;
 44         }
 45     },
 46 
 47     destroyContext: function(context, persistedState)
 48     {
 49         Firebug.Module.destroyContext.apply(this, arguments);
 50 
 51         if (persistedState)
 52         {
 53             persistedState.navigationHistory = context.navigationHistory;
 54             persistedState.navigationHistoryIndex = context.navigationHistoryIndex;
 55         }
 56     },
 57 
 58     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 59     // History popup menu
 60 
 61     onPopupShowing: function(popup, context)
 62     {
 63         var currIndex = this.getCurrentIndex(context);
 64 
 65         if (FBTrace.DBG_HISTORY)
 66             FBTrace.sysout("history.onPopupShowing; " + currIndex + ", " + context.getName(), context);
 67 
 68         Dom.eraseNode(popup);
 69 
 70         var list = this.getHistory(context);
 71 
 72         // Don't display the popup for a single item.
 73         var count = list.length;
 74         if (count <= 1)
 75             return false;
 76 
 77         var maxItems = MAX_HISTORY_MENU_ITEMS;
 78         var half = Math.floor(maxItems / 2);
 79         var start = Math.max(currIndex - half, 0);
 80         var end = Math.min(start == 0 ? maxItems : currIndex + half + 1, count);
 81 
 82         if (end == count)
 83             start = Math.max(count - maxItems, 0);
 84 
 85         var tooltipBack = Locale.$STR("firebug.history.Go back to this panel");
 86         var tooltipCurrent = Locale.$STR("firebug.history.Stay on this panel");
 87         var tooltipForward = Locale.$STR("firebug.history.Go forward to this panel");
 88 
 89         for (var i=end-1; i>=start; i--)
 90         {
 91             var historyItem = list[i];
 92             var panelType = Firebug.getPanelType(historyItem.panelName);
 93             var label = Firebug.getPanelTitle(panelType);
 94             if (historyItem.location && historyItem.location.url)
 95                 label += " - " + historyItem.location.url;
 96 
 97             var menuInfo = {
 98                 label: label,
 99                 nol10n: true,
100                 className: "menuitem-iconic fbURLMenuItem",
101             };
102 
103             if (i < currIndex)
104             {
105                 menuInfo.className += " navigationHistoryMenuItemBack";
106                 menuInfo.tooltiptext = tooltipBack;
107             }
108             else if (i == currIndex)
109             {
110                 menuInfo.type = "radio";
111                 menuInfo.checked = "true";
112                 menuInfo.className = "navigationHistoryMenuItemCurrent";
113                 menuInfo.tooltiptext = tooltipCurrent;
114             }
115             else
116             {
117                 menuInfo.className += " navigationHistoryMenuItemForward";
118                 menuInfo.tooltiptext = tooltipForward;
119             }
120 
121             var menuItem = Menu.createMenuItem(popup, menuInfo);
122             menuItem.repObject = location;
123             menuItem.setAttribute("index", i);
124         }
125 
126         return true;
127     },
128 
129     onHistoryCommand: function(event, context)
130     {
131         var menuItem = event.target;
132         var index = menuItem.getAttribute("index");
133         if (!index)
134             return false;
135 
136         this.gotoHistoryIndex(context, index);
137         return true;
138     },
139 
140     goBack: function(context)
141     {
142         var currIndex = this.getCurrentIndex(context);
143 
144         if (FBTrace.DBG_HISTORY)
145             FBTrace.sysout("history.goBack; " + currIndex + ", " + context.getName(), context);
146 
147         this.gotoHistoryIndex(context, currIndex - 1);
148     },
149 
150     goForward: function(context)
151     {
152         var currIndex = this.getCurrentIndex(context);
153 
154         if (FBTrace.DBG_HISTORY)
155             FBTrace.sysout("history.goForward; " + currIndex + ", " + context.getName(), context);
156 
157         this.gotoHistoryIndex(context, currIndex + 1);
158     },
159 
160     gotoHistoryIndex: function(context, index)
161     {
162         var list = this.getHistory(context);
163         if (index < 0 || index >= list.length)
164             return;
165 
166         var historyItem = list[index];
167 
168         try
169         {
170             this.navInProgress = true;
171             Firebug.chrome.navigate(historyItem.location, historyItem.panelName);
172             context.navigationHistoryIndex = index;
173         }
174         catch (e)
175         {
176         }
177         finally
178         {
179             this.navInProgress = false;
180         }
181 
182         this.updateButtons(context);
183     },
184 
185 
186     updateButtons: function(context)
187     {
188         var list = this.getHistory(context);
189 
190         var backButton = Firebug.chrome.$("fbNavigateBackButton");
191         var forwardButton = Firebug.chrome.$("fbNavigateForwardButton");
192 
193         backButton.setAttribute("disabled", "true");
194         forwardButton.setAttribute("disabled", "true");
195 
196         if (list.length <= 1)
197             return;
198 
199         var currIndex = this.getCurrentIndex(context);
200 
201         if (currIndex > 0)
202             backButton.removeAttribute("disabled");
203 
204         if (currIndex < list.length-1)
205             forwardButton.removeAttribute("disabled");
206     },
207 
208     getHistory: function(context)
209     {
210         if (!context.navigationHistory)
211             context.navigationHistory = [];
212 
213         return context.navigationHistory;
214     },
215 
216     getCurrentIndex: function(context)
217     {
218         if (typeof(context.navigationHistoryIndex) == "undefined")
219             context.navigationHistoryIndex = 0;
220 
221         return context.navigationHistoryIndex;
222     },
223 
224     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
225     // UI Listener
226 
227     onPanelNavigate: function(location, panel)
228     {
229         var context = panel.context;
230         var currIndex = this.getCurrentIndex(context);
231 
232         if (FBTrace.DBG_HISTORY)
233             FBTrace.sysout("history.onPanelNavigate; " + currIndex + ", " +
234                 "Panel: " + (panel ? panel.name : "Unknown Panel") + ", " +
235                 "Location: " + (location ? location.url : "No Location") + ", " +
236                 context.getName());
237 
238         // The panel must be always there
239         if (!panel)
240             return;
241 
242         // Ignore side panel navigation.
243         if (panel.parentPanel)
244             return;
245 
246         // The user is navigating using the history UI, this action doesn't affect
247         // the history list.
248         if (this.navInProgress)
249             return;
250 
251         var list = this.getHistory(context);
252 
253         // If the last item in the history is the same bail out.
254         var lastHistoryItem = list.length ? list[list.length-1] : null;
255         if (lastHistoryItem && lastHistoryItem.panelName == panel.name &&
256             lastHistoryItem.location == location)
257             return;
258 
259         if (lastHistoryItem && lastHistoryItem.location && location &&
260             lastHistoryItem.location.url == location.url)
261             return;
262 
263         // If the panel is the same, bail out.
264         var currHistoryItem = list.length ? list[currIndex] : null;
265         if (currHistoryItem && currHistoryItem.panelName == panel.name &&
266             currHistoryItem.location == location)
267             return;
268 
269         // Remove forward history.
270         list.splice(currIndex+1, list.length-(currIndex+1));
271 
272         // New back history record.
273         list.push({panelName: panel.name, location: location});
274         context.navigationHistoryIndex = list.length-1;
275 
276         if (FBTrace.DBG_HISTORY)
277             FBTrace.sysout("history.onPanelNavigate; New history record created " + currIndex +
278                 ", " + panel.name + ", " + (location ? location.url : "No Location"), list);
279 
280         // Update back and forward buttons in the UI.
281         this.updateButtons(context);
282     }
283 });
284 
285 // ************************************************************************************************
286 // Registration
287 
288 Firebug.registerModule(Firebug.NavigationHistory);
289 Firebug.registerUIListener(Firebug.NavigationHistory);
290 
291 return Firebug.NavigationHistory;
292 
293 // ************************************************************************************************
294 });
295