browser/components/tabview/trench.js

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

     1 /* This Source Code Form is subject to the terms of the Mozilla Public
     2  * License, v. 2.0. If a copy of the MPL was not distributed with this
     3  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     5 // **********
     6 // Title: trench.js
     8 // ##########
     9 // Class: Trench
    10 //
    11 // Class for drag-snapping regions; called "trenches" as they are long and narrow.
    13 // Constructor: Trench
    14 //
    15 // Parameters:
    16 //   element - the DOM element for Item (GroupItem or TabItem) from which the trench is projected
    17 //   xory - either "x" or "y": whether the trench's <position> is along the x- or y-axis.
    18 //     In other words, if "x", the trench is vertical; if "y", the trench is horizontal.
    19 //   type - either "border" or "guide". Border trenches mark the border of an Item.
    20 //     Guide trenches extend out (unless they are intercepted) and act as "guides".
    21 //   edge - which edge of the Item that this trench corresponds to.
    22 //     Either "top", "left", "bottom", or "right".
    23 function Trench(element, xory, type, edge) {
    24   //----------
    25   // Variable: id
    26   // (integer) The id for the Trench. Set sequentially via <Trenches.nextId>
    27   this.id = Trenches.nextId++;
    29   // ---------
    30   // Variables: Initial parameters
    31   //   element - (DOMElement)
    32   //   parentItem - <Item> which projects this trench; to be set with setParentItem
    33   //   xory - (string) "x" or "y"
    34   //   type - (string) "border" or "guide"
    35   //   edge - (string) "top", "left", "bottom", or "right"
    36   this.el = element;
    37   this.parentItem = null;
    38   this.xory = xory; // either "x" or "y"
    39   this.type = type; // "border" or "guide"
    40   this.edge = edge; // "top", "left", "bottom", or "right"
    42   this.$el = iQ(this.el);
    44   //----------
    45   // Variable: dom
    46   // (array) DOM elements for visible reflexes of the Trench
    47   this.dom = [];
    49   //----------
    50   // Variable: showGuide
    51   // (boolean) Whether this trench will project a visible guide (dotted line) or not.
    52   this.showGuide = false;
    54   //----------
    55   // Variable: active
    56   // (boolean) Whether this trench is currently active or not.
    57   // Basically every trench aside for those projected by the Item currently being dragged
    58   // all become active.
    59   this.active = false;
    60   this.gutter = Items.defaultGutter;
    62   //----------
    63   // Variable: position
    64   // (integer) position is the position that we should snap to.
    65   this.position = 0;
    67   //----------
    68   // Variables: some Ranges
    69   //   range - (<Range>) explicit range; this is along the transverse axis
    70   //   minRange - (<Range>) the minimum active range
    71   //   activeRange - (<Range>) the currently active range
    72   this.range = new Range(0,10000);
    73   this.minRange = new Range(0,0);
    74   this.activeRange = new Range(0,10000);
    75 };
    77 Trench.prototype = {
    78   // ----------
    79   // Function: toString
    80   // Prints [Trench edge type (parentItem)] for debug use
    81   toString: function Trench_toString() {
    82     return "[Trench " + this.edge + " " + this.type +
    83            (this.parentItem ? " (" + this.parentItem + ")" : "") +
    84            "]";
    85   },
    87   //----------
    88   // Variable: radius
    89   // (integer) radius is how far away we should snap from
    90   get radius() this.customRadius || Trenches.defaultRadius,
    92   setParentItem: function Trench_setParentItem(item) {
    93     if (!item.isAnItem) {
    94       Utils.assert(false, "parentItem must be an Item");
    95       return false;
    96     }
    97     this.parentItem = item;
    98     return true;
    99   },
   101   //----------
   102   // Function: setPosition
   103   // set the trench's position.
   104   //
   105   // Parameters:
   106   //   position - (integer) px center position of the trench
   107   //   range - (<Range>) the explicit active range of the trench
   108   //   minRange - (<Range>) the minimum range of the trench
   109   setPosition: function Trench_setPosition(position, range, minRange) {
   110     this.position = position;
   112     var page = Items.getPageBounds(true);
   114     // optionally, set the range.
   115     if (Utils.isRange(range)) {
   116       this.range = range;
   117     } else {
   118       this.range = new Range(0, (this.xory == 'x' ? page.height : page.width));
   119     }
   121     // if there's a minRange, set that too.
   122     if (Utils.isRange(minRange))
   123       this.minRange = minRange;
   125     // set the appropriate bounds as a rect.
   126     if (this.xory == "x") // vertical
   127       this.rect = new Rect(this.position - this.radius, this.range.min, 2 * this.radius, this.range.extent);
   128     else // horizontal
   129       this.rect = new Rect(this.range.min, this.position - this.radius, this.range.extent, 2 * this.radius);
   131     this.show(); // DEBUG
   132   },
   134   //----------
   135   // Function: setActiveRange
   136   // set the trench's currently active range.
   137   //
   138   // Parameters:
   139   //   activeRange - (<Range>)
   140   setActiveRange: function Trench_setActiveRange(activeRange) {
   141     if (!Utils.isRange(activeRange))
   142       return false;
   143     this.activeRange = activeRange;
   144     if (this.xory == "x") { // horizontal
   145       this.activeRect = new Rect(this.position - this.radius, this.activeRange.min, 2 * this.radius, this.activeRange.extent);
   146       this.guideRect = new Rect(this.position, this.activeRange.min, 0, this.activeRange.extent);
   147     } else { // vertical
   148       this.activeRect = new Rect(this.activeRange.min, this.position - this.radius, this.activeRange.extent, 2 * this.radius);
   149       this.guideRect = new Rect(this.activeRange.min, this.position, this.activeRange.extent, 0);
   150     }
   151     return true;
   152   },
   154   //----------
   155   // Function: setWithRect
   156   // Set the trench's position using the given rect. We know which side of the rect we should match
   157   // because we've already recorded this information in <edge>.
   158   //
   159   // Parameters:
   160   //   rect - (<Rect>)
   161   setWithRect: function Trench_setWithRect(rect) {
   163     if (!Utils.isRect(rect))
   164       Utils.error('argument must be Rect');
   166     // First, calculate the range for this trench.
   167     // Border trenches are always only active for the length of this range.
   168     // Guide trenches, however, still use this value as its minRange.
   169     if (this.xory == "x")
   170       var range = new Range(rect.top - this.gutter, rect.bottom + this.gutter);
   171     else
   172       var range = new Range(rect.left - this.gutter, rect.right + this.gutter);
   174     if (this.type == "border") {
   175       // border trenches have a range, so set that too.
   176       if (this.edge == "left")
   177         this.setPosition(rect.left - this.gutter, range);
   178       else if (this.edge == "right")
   179         this.setPosition(rect.right + this.gutter, range);
   180       else if (this.edge == "top")
   181         this.setPosition(rect.top - this.gutter, range);
   182       else if (this.edge == "bottom")
   183         this.setPosition(rect.bottom + this.gutter, range);
   184     } else if (this.type == "guide") {
   185       // guide trenches have no range, but do have a minRange.
   186       if (this.edge == "left")
   187         this.setPosition(rect.left, false, range);
   188       else if (this.edge == "right")
   189         this.setPosition(rect.right, false, range);
   190       else if (this.edge == "top")
   191         this.setPosition(rect.top, false, range);
   192       else if (this.edge == "bottom")
   193         this.setPosition(rect.bottom, false, range);
   194     }
   195   },
   197   //----------
   198   // Function: show
   199   //
   200   // Show guide (dotted line), if <showGuide> is true.
   201   //
   202   // If <Trenches.showDebug> is true, we will draw the trench. Active portions are drawn with 0.5
   203   // opacity. If <active> is false, the entire trench will be
   204   // very translucent.
   205   show: function Trench_show() { // DEBUG
   206     if (this.active && this.showGuide) {
   207       if (!this.dom.guideTrench)
   208         this.dom.guideTrench = iQ("<div/>").addClass('guideTrench').css({id: 'guideTrench'+this.id});
   209       var guideTrench = this.dom.guideTrench;
   210       guideTrench.css(this.guideRect);
   211       iQ("body").append(guideTrench);
   212     } else {
   213       if (this.dom.guideTrench) {
   214         this.dom.guideTrench.remove();
   215         delete this.dom.guideTrench;
   216       }
   217     }
   219     if (!Trenches.showDebug) {
   220       this.hide(true); // true for dontHideGuides
   221       return;
   222     }
   224     if (!this.dom.visibleTrench)
   225       this.dom.visibleTrench = iQ("<div/>")
   226         .addClass('visibleTrench')
   227         .addClass(this.type) // border or guide
   228         .css({id: 'visibleTrench'+this.id});
   229     var visibleTrench = this.dom.visibleTrench;
   231     if (!this.dom.activeVisibleTrench)
   232       this.dom.activeVisibleTrench = iQ("<div/>")
   233         .addClass('activeVisibleTrench')
   234         .addClass(this.type) // border or guide
   235         .css({id: 'activeVisibleTrench'+this.id});
   236     var activeVisibleTrench = this.dom.activeVisibleTrench;
   238     if (this.active)
   239       activeVisibleTrench.addClass('activeTrench');
   240     else
   241       activeVisibleTrench.removeClass('activeTrench');
   243     visibleTrench.css(this.rect);
   244     activeVisibleTrench.css(this.activeRect || this.rect);
   245     iQ("body").append(visibleTrench);
   246     iQ("body").append(activeVisibleTrench);
   247   },
   249   //----------
   250   // Function: hide
   251   // Hide the trench.
   252   hide: function Trench_hide(dontHideGuides) {
   253     if (this.dom.visibleTrench)
   254       this.dom.visibleTrench.remove();
   255     if (this.dom.activeVisibleTrench)
   256       this.dom.activeVisibleTrench.remove();
   257     if (!dontHideGuides && this.dom.guideTrench)
   258       this.dom.guideTrench.remove();
   259   },
   261   //----------
   262   // Function: rectOverlaps
   263   // Given a <Rect>, compute whether it overlaps with this trench. If it does, return an
   264   // adjusted ("snapped") <Rect>; if it does not overlap, simply return false.
   265   //
   266   // Note that simply overlapping is not all that is required to be affected by this function.
   267   // Trenches can only affect certain edges of rectangles... for example, a "left"-edge guide
   268   // trench should only affect left edges of rectangles. We don't snap right edges to left-edged
   269   // guide trenches. For border trenches, the logic is a bit different, so left snaps to right and
   270   // top snaps to bottom.
   271   //
   272   // Parameters:
   273   //   rect - (<Rect>) the rectangle in question
   274   //   stationaryCorner   - which corner is stationary? by default, the top left.
   275   //                        "topleft", "bottomleft", "topright", "bottomright"
   276   //   assumeConstantSize - (boolean) whether the rect's dimensions are sacred or not
   277   //   keepProportional - (boolean) if we are allowed to change the rect's size, whether the
   278   //                                dimensions should scaled proportionally or not.
   279   //
   280   // Returns:
   281   //   false - if rect does not overlap with this trench
   282   //   newRect - (<Rect>) an adjusted version of rect, if it is affected by this trench
   283   rectOverlaps: function Trench_rectOverlaps(rect,stationaryCorner,assumeConstantSize,keepProportional) {
   284     var edgeToCheck;
   285     if (this.type == "border") {
   286       if (this.edge == "left")
   287         edgeToCheck = "right";
   288       else if (this.edge == "right")
   289         edgeToCheck = "left";
   290       else if (this.edge == "top")
   291         edgeToCheck = "bottom";
   292       else if (this.edge == "bottom")
   293         edgeToCheck = "top";
   294     } else { // if trench type is guide or barrier...
   295       edgeToCheck = this.edge;
   296     }
   298     rect.adjustedEdge = edgeToCheck;
   300     switch (edgeToCheck) {
   301       case "left":
   302         if (this.ruleOverlaps(rect.left, rect.yRange)) {
   303           if (stationaryCorner.indexOf('right') > -1)
   304             rect.width = rect.right - this.position;
   305           rect.left = this.position;
   306           return rect;
   307         }
   308         break;
   309       case "right":
   310         if (this.ruleOverlaps(rect.right, rect.yRange)) {
   311           if (assumeConstantSize) {
   312             rect.left = this.position - rect.width;
   313           } else {
   314             var newWidth = this.position - rect.left;
   315             if (keepProportional)
   316               rect.height = rect.height * newWidth / rect.width;
   317             rect.width = newWidth;
   318           }
   319           return rect;
   320         }
   321         break;
   322       case "top":
   323         if (this.ruleOverlaps(rect.top, rect.xRange)) {
   324           if (stationaryCorner.indexOf('bottom') > -1)
   325             rect.height = rect.bottom - this.position;
   326           rect.top = this.position;
   327           return rect;
   328         }
   329         break;
   330       case "bottom":
   331         if (this.ruleOverlaps(rect.bottom, rect.xRange)) {
   332           if (assumeConstantSize) {
   333             rect.top = this.position - rect.height;
   334           } else {
   335             var newHeight = this.position - rect.top;
   336             if (keepProportional)
   337               rect.width = rect.width * newHeight / rect.height;
   338             rect.height = newHeight;
   339           }
   340           return rect;
   341         }
   342     }
   344     return false;
   345   },
   347   //----------
   348   // Function: ruleOverlaps
   349   // Computes whether the given "rule" (a line segment, essentially), given by the position and
   350   // range arguments, overlaps with the current trench. Note that this function assumes that
   351   // the rule and the trench are in the same direction: both horizontal, or both vertical.
   352   //
   353   // Parameters:
   354   //   position - (integer) a position in px
   355   //   range - (<Range>) the rule's range
   356   ruleOverlaps: function Trench_ruleOverlaps(position, range) {
   357     return (this.position - this.radius < position &&
   358            position < this.position + this.radius &&
   359            this.activeRange.overlaps(range));
   360   },
   362   //----------
   363   // Function: adjustRangeIfIntercept
   364   // Computes whether the given boundary (given as a position and its active range), perpendicular
   365   // to the trench, intercepts the trench or not. If it does, it returns an adjusted <Range> for
   366   // the trench. If not, it returns false.
   367   //
   368   // Parameters:
   369   //   position - (integer) the position of the boundary
   370   //   range - (<Range>) the target's range, on the trench's transverse axis
   371   adjustRangeIfIntercept: function Trench_adjustRangeIfIntercept(position, range) {
   372     if (this.position - this.radius > range.min && this.position + this.radius < range.max) {
   373       var activeRange = new Range(this.activeRange);
   375       // there are three ways this can go:
   376       // 1. position < minRange.min
   377       // 2. position > minRange.max
   378       // 3. position >= minRange.min && position <= minRange.max
   380       if (position < this.minRange.min) {
   381         activeRange.min = Math.min(this.minRange.min,position);
   382       } else if (position > this.minRange.max) {
   383         activeRange.max = Math.max(this.minRange.max,position);
   384       } else {
   385         // this should be impossible because items can't overlap and we've already checked
   386         // that the range intercepts.
   387       }
   388       return activeRange;
   389     }
   390     return false;
   391   },
   393   //----------
   394   // Function: calculateActiveRange
   395   // Computes and sets the <activeRange> for the trench, based on the <GroupItems> around.
   396   // This makes it so trenches' active ranges don't extend through other groupItems.
   397   calculateActiveRange: function Trench_calculateActiveRange() {
   399     // set it to the default: just the range itself.
   400     this.setActiveRange(this.range);
   402     // only guide-type trenches need to set a separate active range
   403     if (this.type != 'guide')
   404       return;
   406     var groupItems = GroupItems.groupItems;
   407     var trench = this;
   408     groupItems.forEach(function(groupItem) {
   409       if (groupItem.isDragging) // floating groupItems don't block trenches
   410         return;
   411       if (trench.el == groupItem.container) // groupItems don't block their own trenches
   412         return;
   413       var bounds = groupItem.getBounds();
   414       var activeRange = new Range();
   415       if (trench.xory == 'y') { // if this trench is horizontal...
   416         activeRange = trench.adjustRangeIfIntercept(bounds.left, bounds.yRange);
   417         if (activeRange)
   418           trench.setActiveRange(activeRange);
   419         activeRange = trench.adjustRangeIfIntercept(bounds.right, bounds.yRange);
   420         if (activeRange)
   421           trench.setActiveRange(activeRange);
   422       } else { // if this trench is vertical...
   423         activeRange = trench.adjustRangeIfIntercept(bounds.top, bounds.xRange);
   424         if (activeRange)
   425           trench.setActiveRange(activeRange);
   426         activeRange = trench.adjustRangeIfIntercept(bounds.bottom, bounds.xRange);
   427         if (activeRange)
   428           trench.setActiveRange(activeRange);
   429       }
   430     });
   431   }
   432 };
   434 // ##########
   435 // Class: Trenches
   436 // Singelton for managing all <Trench>es.
   437 var Trenches = {
   438   // ---------
   439   // Variables:
   440   //   nextId - (integer) a counter for the next <Trench>'s <Trench.id> value.
   441   //   showDebug - (boolean) whether to draw the <Trench>es or not.
   442   //   defaultRadius - (integer) the default radius for new <Trench>es.
   443   //   disabled - (boolean) whether trench-snapping is disabled or not.
   444   nextId: 0,
   445   showDebug: false,
   446   defaultRadius: 10,
   447   disabled: false,
   449   // ---------
   450   // Variables: snapping preferences; used to break ties in snapping.
   451   //   preferTop - (boolean) prefer snapping to the top to the bottom
   452   //   preferLeft - (boolean) prefer snapping to the left to the right
   453   preferTop: true,
   454   get preferLeft() { return !UI.rtl; },
   456   trenches: [],
   458   // ----------
   459   // Function: toString
   460   // Prints [Trenches count=count] for debug use
   461   toString: function Trenches_toString() {
   462     return "[Trenches count=" + this.trenches.length + "]";
   463   },
   465   // ---------
   466   // Function: getById
   467   // Return the specified <Trench>.
   468   //
   469   // Parameters:
   470   //   id - (integer)
   471   getById: function Trenches_getById(id) {
   472     return this.trenches[id];
   473   },
   475   // ---------
   476   // Function: register
   477   // Register a new <Trench> and returns the resulting <Trench> ID.
   478   //
   479   // Parameters:
   480   // See the constructor <Trench.Trench>'s parameters.
   481   //
   482   // Returns:
   483   //   id - (int) the new <Trench>'s ID.
   484   register: function Trenches_register(element, xory, type, edge) {
   485     var trench = new Trench(element, xory, type, edge);
   486     this.trenches[trench.id] = trench;
   487     return trench.id;
   488   },
   490   // ---------
   491   // Function: registerWithItem
   492   // Register a whole set of <Trench>es using an <Item> and returns the resulting <Trench> IDs.
   493   //
   494   // Parameters:
   495   //   item - the <Item> to project trenches
   496   //   type - either "border" or "guide"
   497   //
   498   // Returns:
   499   //   ids - array of the new <Trench>es' IDs.
   500   registerWithItem: function Trenches_registerWithItem(item, type) {
   501     var container = item.container;
   502     var ids = {};
   503     ids.left = Trenches.register(container,"x",type,"left");
   504     ids.right = Trenches.register(container,"x",type,"right");
   505     ids.top = Trenches.register(container,"y",type,"top");
   506     ids.bottom = Trenches.register(container,"y",type,"bottom");
   508     this.getById(ids.left).setParentItem(item);
   509     this.getById(ids.right).setParentItem(item);
   510     this.getById(ids.top).setParentItem(item);
   511     this.getById(ids.bottom).setParentItem(item);
   513     return ids;
   514   },
   516   // ---------
   517   // Function: unregister
   518   // Unregister one or more <Trench>es.
   519   //
   520   // Parameters:
   521   //   ids - (integer) a single <Trench> ID or (array) a list of <Trench> IDs.
   522   unregister: function Trenches_unregister(ids) {
   523     if (!Array.isArray(ids))
   524       ids = [ids];
   525     var self = this;
   526     ids.forEach(function(id) {
   527       self.trenches[id].hide();
   528       delete self.trenches[id];
   529     });
   530   },
   532   // ---------
   533   // Function: activateOthersTrenches
   534   // Activate all <Trench>es other than those projected by the current element.
   535   //
   536   // Parameters:
   537   //   element - (DOMElement) the DOM element of the Item being dragged or resized.
   538   activateOthersTrenches: function Trenches_activateOthersTrenches(element) {
   539     this.trenches.forEach(function(t) {
   540       if (t.el === element)
   541         return;
   542       if (t.parentItem && (t.parentItem.isAFauxItem || t.parentItem.isDragging))
   543         return;
   544       t.active = true;
   545       t.calculateActiveRange();
   546       t.show(); // debug
   547     });
   548   },
   550   // ---------
   551   // Function: disactivate
   552   // After <activateOthersTrenches>, disactivates all the <Trench>es again.
   553   disactivate: function Trenches_disactivate() {
   554     this.trenches.forEach(function(t) {
   555       t.active = false;
   556       t.showGuide = false;
   557       t.show();
   558     });
   559   },
   561   // ---------
   562   // Function: hideGuides
   563   // Hide all guides (dotted lines) en masse.
   564   hideGuides: function Trenches_hideGuides() {
   565     this.trenches.forEach(function(t) {
   566       t.showGuide = false;
   567       t.show();
   568     });
   569   },
   571   // ---------
   572   // Function: snap
   573   // Used to "snap" an object's bounds to active trenches and to the edge of the window.
   574   // If the meta key is down (<Key.meta>), it will not snap but will still enforce the rect
   575   // not leaving the safe bounds of the window.
   576   //
   577   // Parameters:
   578   //   rect               - (<Rect>) the object's current bounds
   579   //   stationaryCorner   - which corner is stationary? by default, the top left.
   580   //                        "topleft", "bottomleft", "topright", "bottomright"
   581   //   assumeConstantSize - (boolean) whether the rect's dimensions are sacred or not
   582   //   keepProportional   - (boolean) if we are allowed to change the rect's size, whether the
   583   //                                  dimensions should scaled proportionally or not.
   584   //
   585   // Returns:
   586   //   (<Rect>) - the updated bounds, if they were updated
   587   //   false - if the bounds were not updated
   588   snap: function Trenches_snap(rect,stationaryCorner,assumeConstantSize,keepProportional) {
   589     // hide all the guide trenches, because the correct ones will be turned on later.
   590     Trenches.hideGuides();
   592     var updated = false;
   593     var updatedX = false;
   594     var updatedY = false;
   596     var snappedTrenches = {};
   598     for (var i in this.trenches) {
   599       var t = this.trenches[i];
   600       if (!t.active)
   601         continue;
   602       // newRect will be a new rect, or false
   603       var newRect = t.rectOverlaps(rect,stationaryCorner,assumeConstantSize,keepProportional);
   605       if (newRect) { // if rectOverlaps returned an updated rect...
   607         if (assumeConstantSize && updatedX && updatedY)
   608           break;
   609         if (assumeConstantSize && updatedX && (newRect.adjustedEdge == "left"||newRect.adjustedEdge == "right"))
   610           continue;
   611         if (assumeConstantSize && updatedY && (newRect.adjustedEdge == "top"||newRect.adjustedEdge == "bottom"))
   612           continue;
   614         rect = newRect;
   615         updated = true;
   617         // register this trench as the "snapped trench" for the appropriate edge.
   618         snappedTrenches[newRect.adjustedEdge] = t;
   620         // if updatedX, we don't need to update x any more.
   621         if (newRect.adjustedEdge == "left" && this.preferLeft)
   622           updatedX = true;
   623         if (newRect.adjustedEdge == "right" && !this.preferLeft)
   624           updatedX = true;
   626         // if updatedY, we don't need to update x any more.
   627         if (newRect.adjustedEdge == "top" && this.preferTop)
   628           updatedY = true;
   629         if (newRect.adjustedEdge == "bottom" && !this.preferTop)
   630           updatedY = true;
   632       }
   633     }
   635     if (updated) {
   636       rect.snappedTrenches = snappedTrenches;
   637       return rect;
   638     }
   639     return false;
   640   },
   642   // ---------
   643   // Function: show
   644   // <Trench.show> all <Trench>es.
   645   show: function Trenches_show() {
   646     this.trenches.forEach(function(t) {
   647       t.show();
   648     });
   649   },
   651   // ---------
   652   // Function: toggleShown
   653   // Toggle <Trenches.showDebug> and trigger <Trenches.show>
   654   toggleShown: function Trenches_toggleShown() {
   655     this.showDebug = !this.showDebug;
   656     this.show();
   657   }
   658 };

mercurial