1 /**
  2   SC.bodyOverflowArbitrator is a central object responsible for controlling the overflow on the body element.
  3 
  4   Use it from views that would otherwise set the body overflow style directly; call requestHidden to ask for
  5   the body to have overflow:hidden, and call requestVisible to ask for the body to be visible.
  6 
  7   Call withdrawRequest to register that you no longer have any interest in the body overflow setting. Don't
  8   forget to do this, or your object could be affecting the body overflow long after it's no longer relevant.
  9 
 10   When calling requestHidden, requestVisible, and withdrawRequest, pass your object as the first argument so
 11   that its request can be associated with its GUID.
 12 
 13   When calling requestHidden or requestVisible, you may optionally pass true as a second argument to signify
 14   that your desire for hidden or visible overflow is important. An important visible-request will override
 15   any other, but an important hidden-request will override a normal visible-request. A normal visible-request
 16   will in turn override a normal hidden-request.
 17 */
 18 SC.bodyOverflowArbitrator = SC.Object.create(/**@scope SC.bodyOverflowArbitrator.prototype */{
 19   /** Request that the body be given overflow:hidden;. Pass your object, then (optionally) true to confer importance. */
 20   requestHidden: function (from, important) { this._makeRequest(from, -1 - 9 * !!important); },
 21 
 22   /** Request that the body be given overflow:visible;. Pass your object, then (optionally) true to confer importance. */
 23   requestVisible: function (from, important) { this._makeRequest(from, 1 + 9 * !!important); },
 24 
 25   /** State that your object no longer cares about the body overflow. */
 26   withdrawRequest: function (from) {
 27     // Fast path!
 28     if (!from) return;
 29 
 30     var guid = SC.guidFor(from),
 31       currentRequest = this._requests[guid];
 32 
 33     if (currentRequest) {
 34       delete this._requests[guid];
 35       this.setOverflow();
 36     }
 37   },
 38 
 39   /** Perform the action of setting the overflow depending on what requests are currently registered. Does nothing if there are no requests. */
 40   setOverflow: function () {
 41     var overflow = this._decideOverflow();
 42 
 43     if (overflow !== undefined) document.body.style.overflow = overflow ? "auto" : "hidden";
 44     // console.log("Body Overflow Arbitrator now decides "+(overflow===undefined?"that overflow is unimportant.":"to use overflow:"+(overflow===true?'visible':"hidden")+";")+" Requests are:",this._requests);
 45   },
 46 
 47   /** @private */
 48   _makeRequest: function (from, value) {
 49     // Fast path!
 50     if (!from) return;
 51 
 52     var guid = SC.guidFor(from),
 53       currentRequest = this._requests[guid];
 54 
 55     if (currentRequest != value) {
 56       this._requests[guid] = value;
 57       this.setOverflow();
 58     }
 59   },
 60 
 61   /** @private */
 62   _requests: {},
 63 
 64   /** @private */
 65   _decideOverflow: function () {
 66     var haveHidden, haveVisible, haveImportantHidden, haveImportantVisible,
 67         reqs = this._requests, req;
 68 
 69     for (var i in reqs) {
 70       if ((req = reqs[i]) < 0) haveHidden = YES;
 71       if (req < -1) haveImportantHidden = YES;
 72       if (req > 0) haveVisible = YES;
 73       if (req > 1) haveImportantVisible = YES;
 74     }
 75     if (haveImportantVisible) return YES;              //important-visible takes all.
 76     if (haveVisible && haveImportantHidden) return NO; //important-hidden beats regular-visible.
 77     if (haveVisible) return YES;                       //regular-visible beats regular-hidden
 78     if (haveHidden) return NO;                         //if there is a hidden, it can win now.
 79 
 80     return undefined;                                  //if nobody cared, return undefined to prevent work.
 81   }
 82 });
 83