<commit_before>/*!
 * SubEtha Window Information
 * http://github.com/bemson/subetha-client-winfo
 *
 * Copyright, Bemi Faison
 * Released under the MIT License
 */
/*
Use this plugin observe changes in all windows in the network.
*/
/* jshint browser:true */
/* global define, require, self */
!function (inAMD, inCJS, scope, undefined) {

  function initSubEthaWinfo() {

    var
      subetha = ((inCJS || inAMD) ? require('subetha') : scope.Subetha),
      monitor = new subetha.Client(),

      protoHas = Object.prototype.hasOwnProperty,
      sharedHeadObj,
      monitoring,
      positionWatchInt,
      finalResizeInt,
      winMetrics,
      windowId,
      lastCoord,

      doc = scope.document,
      docBody,
      docHead,

      // array of windows for public consumption
      winAry = [],
      // collection of window objects for private management
      winObjs = {},

      // utility
      bind = scope.attachEvent ?
        function (object, eventName, callback) {
          object.attachEvent('on' + eventName, callback);
        } :
        function (object, eventName, callback) {
          object.addEventListener(eventName, callback, false);
        },
      unbind = scope.attachEvent ?
        function (object, eventName, callback) {
          object.detachEvent('on' + eventName, callback);
        } :
        function (object, eventName, callback) {
          object.removeEventListener(eventName, callback, false);
        },
      next =
        // use setImmediate
        (
          typeof setImmediate === 'function' &&
          setImmediate
        ) ||
        // use nextTick (for nodeJS only)
        (inCJS && process.nextTick) ||
        // fallback to slower setTimeout call
        function (fn) {
          setTimeout(fn, 0);
        }
    ;

    // UTILITY

    // shallow object merge
    function mix(base) {
      var
        argIdx = 1,
        source,
        member;

      for (; source = arguments[argIdx]; argIdx++) {
        for (member in source) {
          if (protoHas.call(source, member)) {
            base[member] = source[member];
          }
        }
      }
      return base;
    }

    function ArrayIndexOf(ary, item) {
      var
        idx = -1,
        ln = ary.length;
      while (ln--) {
        if (ary[ln] === item) {
          idx = ln;
          break;
        }
      }
      return idx;
    }

    // determine if the window has focus
    function hasFocus() {
      var hiddenPropName;

      if (typeof doc.hidden !== "undefined") {
          hiddenPropName = "hidden";
      } else if (typeof doc.mozHidden !== "undefined") {
          hiddenPropName = "mozHidden";
      } else if (typeof doc.msHidden !== "undefined") {
          hiddenPropName = "msHidden";
      } else if (typeof doc.webkitHidden !== "undefined") {
          hiddenPropName = "webkitHidden";
      }

      if (hiddenPropName && (hiddenPropName in doc)) {
        // has focus when hidden is false
        return !doc[hiddenPropName];
      } else {
        // use fallback API
        return doc.hasFocus();
      }
    }

    // FUNCTIONS

    function startMonitor() {

      // exit if already monitoring
      if (monitoring) {
        return;
      }

      monitoring = 1;

      // listen to window events
      rigWindow();

      // start watching window, if focused
      if (hasFocus()) {
        watchWindowPosition();
      }
    }

    function stopMonitor() {
      if (!monitoring) {
        return;
      }
      monitoring = 0;

      unrigWindow();
      unwatchWindowPosition();
    }

    function rigWindow() {
      // starts watching window motion, when this window is focused
      bind(scope, 'focus', onFocus);
      // stops watching window motion, when this window is blurred
      bind(scope, 'blur', onBlur);
      // always observe scroll changes
      bind(scope, 'scroll', onScroll);
      // always observe resize changes
      bind(scope, 'resize', onResize);
    }

    function unrigWindow() {
      unbind(scope, 'focus', onFocus);
      unbind(scope, 'blur', onBlur);
      unbind(scope, 'scroll', onScroll);
      unbind(scope, 'resize', onResize);
    }

    function isActiveMonitor() {
      return sharedHeadObj.mid == monitor.id;
    }

    function getDocHeight() {
      return scope.innerHeight;
    }

    function getDocWidth() {
      return scope.innerWidth;
    }

    function getDocScroll() {
      return {
        scrollx: scope.pageXOffset,
        scrolly: scope.pageYOffset
      };
    }

    function getBrowserHeight() {
      return scope.outerHeight;
    }

    function getBrowserWidth() {
      return scope.outerWidth;
    }

    function getWindowPosition() {
      var
        bx = scope.screenX,
        by = scope.screenY;

      return {
        x: bx,
        y: by + (scope.outerHeight - scope.innerHeight),
        bx: bx,
        by: by,
      };
    }

    function updateResizeMetrics() {
      updateWindowMetrics(mix(getWindowDimensions(), getWindowPosition()));
    }

    function getWindowDimensions() {
      return {
        width: getDocWidth(),
        height: getDocHeight(),
        bwidth: getBrowserWidth(),
        bheight: getBrowserHeight()
      };
    }

    function watchWindowPosition() {
      unwatchWindowPosition();
      positionWatchInt = setInterval(checkWindowPosition, 100);
    }

    function unwatchWindowPosition() {
      clearInterval(positionWatchInt);
    }

    function checkWindowPosition() {
      var curCoord = getWindowPosition();

      if (
        !lastCoord ||
        lastCoord.x != curCoord.x ||
        lastCoord.y != curCoord.y
      ) {
        lastCoord = curCoord;
        // passing width and height, since those may have updated as well
        updateWindowMetrics(mix(lastCoord, getWindowDimensions()));
      }
    }

    // start watching for window changes
    function onFocus() {
      // start watching window
      watchWindowPosition();
      // broadcast focus change
      updateWindowMetrics({focus: true});
    }

    // stop watching for window changes
    function onBlur() {
      // stop watching window position
      unwatchWindowPosition();
      // broadcast focus change
      updateWindowMetrics({focus: false});
    }

    // update scroll position
    function onScroll() {
      // broadcast scroll change
      updateWindowMetrics(getDocScroll());
    }

    // update changed dimensions
    function onResize() {
      // kill final resize
      clearTimeout(finalResizeInt);
      // retrieve all, since resizing can occur from any corner
      updateResizeMetrics();
      // final resize
      finalResizeInt = setTimeout(updateResizeMetrics, 50);
    }

    function fireAddWindowEvent(wid, winfo) {
      winAry.fire('add', wid, winfo);
    }

    function fireRemoveWindowEvent(wid, winfo) {
      winAry.fire('remove', wid, winfo);
    }

    function fireUpdateWindowEvent(wid, winfo, changes) {
      winAry.fire('update', wid, winfo, changes);
    }

    function updateWindowMetrics(data) {
      var
        changed = {},
        deets = winMetrics.deets,
        key,
        value,
        hasChanges
      ;
      // keep changed values
      for (key in data) {
        if (
          protoHas.call(data, key) &&
          (
            !protoHas.call(deets, key) ||
            deets[key] != (value = data[key])
          )
        ) {
          // capture changed value
          changed[key] =
          // update metric
          deets[key] =
            value;
          // not that there is something to send
          hasChanges = 1;
        }
      }

      // exit if nothing has changed
      if (!hasChanges) {
        return;
      }

      // broadcast changed/new values
      sendWindowMetrics(changed);

      // notify self
      next(function () {
        fireUpdateWindowEvent(windowId, deets, changed);
      });
    }

    function sendWindowMetrics(data, target) {
      monitor._transmit('subetha/winfo', target, {
        id: windowId,
        cnt: sharedHeadObj.cnt,
        deets: data
      });
    }

    // IMPLEMENTATION

    // add event emitter and expose winfo
    mix(winAry, subetha.EventEmitter.prototype);
    subetha.winfo = winAry;

    if (scope == self) {

      // setup monitor
      monitor.open('window-information@public')
        .on('::connect', function () {
          var me = this;

          docBody = doc.body;
          docHead = doc.head;

          // resolve tracker for all window monitors
          if (!protoHas.call(docHead, '_winfo')) {
            // init shared tracker
            docHead._winfo = {
              // window id
              wid: subetha.guid(),
              // id of monitor tracking this window
              mid: me.id,
              // number of monitors for this window
              cnt: 0
            };
          }

          // alias shared object
          sharedHeadObj = docHead._winfo;
          // increment monitor count
          sharedHeadObj.cnt++;

          // alias window id
          windowId = sharedHeadObj.wid;

          if (isActiveMonitor()) {
            // create and add winMetrics to public array
            winMetrics =
            winObjs[windowId] = {
              id: windowId,
              cnt: sharedHeadObj.cnt,
              deets: mix(
                {
                  focus: hasFocus()
                },
                getWindowPosition(),
                getWindowDimensions(),
                getDocScroll()
              )
            };

            winAry[0] = winMetrics.deets;

            // start watching window
            startMonitor();

            // announce this window to self next
            next(function () {
              fireAddWindowEvent(windowId, winMetrics.deets);
            });
            setTimeout(function () {
              // introduce this window to peers
              sendWindowMetrics(winMetrics.deets);
            },5);
          }
        })
        .on('::disconnect', function () {
          // clean up & destroy monitor
          monitor.off();

          // decrement number of monitors for this page
          sharedHeadObj.cnt--;

          // if this monitor was currently tracking this window
          if (isActiveMonitor()) {
            // stop watching the window
            stopMonitor();
          }

          // if no other monitors remain...
          if (!sharedHeadObj.cnt) {
            // remove winfo tracker from HEAD node
            delete docHead._winfo;
            // clear public array
            winAry.length = 0;
          }

          // nullify local reference
          sharedHeadObj = 0;
        })
        .on('::join', function (peer, exists) {
          // exit pre-existing peers
          if (exists) {
            return;
          }

          // send metrics to peer
          sendWindowMetrics(winMetrics.deets, peer);
        })
        .on('::drop', function (peer) {
          var
            wid = peer.wid,
            winfo,
            idx;

          // exit if there is no window information associated with this peer
          if (!wid || !protoHas.call(winObjs, wid)) {
            return;
          }
          winfo = winObjs[wid];

          // decrement number of monitors
          winfo.cnt--;

          // if this peer was monitoring this window...
          if (sharedHeadObj.mid == peer.id) {
            // set monitor self
            sharedHeadObj.mid = this.id;
            // use winfo as winMetric
            winMetrics = winfo;
            // get updated count
            // this has already been decremented by the departing monitor
            winMetrics.cnt = sharedHeadObj.cnt;
            // start watching window
            startMonitor();
          } else if (!winfo.cnt) { // take action when there are no more monitors for this window
            // remove deets from public array
            if (~(idx = ArrayIndexOf(winAry, winfo.deets))) {
              winAry.splice(idx, 1);
            }
            // remove window info object
            delete winObjs[wid];
            // announce removal of window
            fireRemoveWindowEvent(wid, winfo.deets);
          }
        })
      ;

      // add subetha/winfo type
      /*
      payload
      {
        id: // window id
        cnt: // number of monitors for this window
        deets: { // updated window information
          x: // x coord
          y: // y coord
          width: // doc width
          height: // doc height
          bwidth: // browser width
          bheight: // browser height
          scrollx: // horizontal scroll position
          scrolly: // vertical scroll position
          hasscrollx: // flag when has horizontal scroll bar
          hasscrolly: // flag when has vertical scroll bar
          focus: // focus (truthy)
        }
      }
      */
      subetha.msgType['subetha/winfo'] = function (client, peer, payload) {
        var
          deets,
          winfo,
          wid,
          adding;

        // exit when payload is invalid
        if (
          !payload ||
          typeof payload != 'object' ||
          typeof payload.cnt != 'number' ||
          typeof (wid = payload.id) != 'string' ||
          typeof (deets = payload.deets) != 'object' ||
          !deets
        ) {
          return;
        }

        // if there is no window info object for this window
        if (protoHas.call(winObjs, wid)) {
          winfo = winObjs[wid];
        } else {
          // init window information object - start with id
          winfo =
          winObjs[wid] =
            {
              id: wid,
              deets: {
                id: wid
              }
            };
          // add deets to public array
          winAry.push(winfo.deets);
          // link window to this peer - this way we can check the window when this peer disconnects
          peer.wid = wid;
          // flag that this update is for a new window
          adding = 1;
        }

        // update window details
        mix(winfo.deets, deets);
        // update meta-data
        winfo.cnt = payload.cnt;

        if (adding) {
          fireAddWindowEvent(wid, winfo.deets);
        } else {
          fireUpdateWindowEvent(wid, winfo.deets, deets);
        }
      };

    } else {

      // Can't work in iframe until we can identify which iframes belong to which window
      winAry.unsupported = 1;

    }

    return subetha;
  }

  // initialize and expose module, based on the environment
  if (inAMD) {
    define(initSubEthaWinfo);
  } else if (inCJS) {
    module.exports = initSubEthaWinfo();
  } else if (scope.Subetha && !scope.Subetha.winfo) {
    // tack on to existing namespace
    initSubEthaWinfo();
  }
}(
  typeof define === 'function',
  typeof exports != 'undefined',
  this
);<commit_msg>Auto-reconnect. API to start and stop monitoring<commit_after>/*!
 * SubEtha Window Information
 * http://github.com/bemson/subetha-client-winfo
 *
 * Copyright, Bemi Faison
 * Released under the MIT License
 */
/*
Use this plugin observe changes in all windows in the network.
*/
/* jshint browser:true */
/* global define, require, self */
!function (inAMD, inCJS, scope, undefined) {

  function initSubEthaWinfo() {

    var
      subetha = ((inCJS || inAMD) ? require('subetha') : scope.Subetha),
      monitor = new subetha.Client(),

      protoHas = Object.prototype.hasOwnProperty,
      sharedHeadObj,
      monitoring,
      positionWatchInt,
      finalResizeInt,
      winMetrics,
      windowId,
      lastCoord,
      autoReconnect = 1,

      doc = scope.document,
      docBody,
      docHead,

      // array of windows for public consumption
      winAry = [],
      // collection of window objects for private management
      winObjs = {},

      // utility
      bind = scope.attachEvent ?
        function (object, eventName, callback) {
          object.attachEvent('on' + eventName, callback);
        } :
        function (object, eventName, callback) {
          object.addEventListener(eventName, callback, false);
        },
      unbind = scope.attachEvent ?
        function (object, eventName, callback) {
          object.detachEvent('on' + eventName, callback);
        } :
        function (object, eventName, callback) {
          object.removeEventListener(eventName, callback, false);
        },
      next =
        // use setImmediate
        (
          typeof setImmediate === 'function' &&
          setImmediate
        ) ||
        // use nextTick (for nodeJS only)
        (inCJS && process.nextTick) ||
        // fallback to slower setTimeout call
        function (fn) {
          setTimeout(fn, 0);
        }
    ;

    // UTILITY

    // shallow object merge
    function mix(base) {
      var
        argIdx = 1,
        source,
        member;

      for (; source = arguments[argIdx]; argIdx++) {
        for (member in source) {
          if (protoHas.call(source, member)) {
            base[member] = source[member];
          }
        }
      }
      return base;
    }

    function ArrayIndexOf(ary, item) {
      var
        idx = -1,
        ln = ary.length;
      while (ln--) {
        if (ary[ln] === item) {
          idx = ln;
          break;
        }
      }
      return idx;
    }

    function removeFromArray(ary, item) {
      var idx = ArrayIndexOf(ary, item);
      if (~idx) {
        ary.splice(idx, 1);
      }
    }

    // determine if the window has focus
    function hasFocus() {
      var hiddenPropName;

      if (typeof doc.hidden !== "undefined") {
          hiddenPropName = "hidden";
      } else if (typeof doc.mozHidden !== "undefined") {
          hiddenPropName = "mozHidden";
      } else if (typeof doc.msHidden !== "undefined") {
          hiddenPropName = "msHidden";
      } else if (typeof doc.webkitHidden !== "undefined") {
          hiddenPropName = "webkitHidden";
      }

      if (hiddenPropName && (hiddenPropName in doc)) {
        // has focus when hidden is false
        return !doc[hiddenPropName];
      } else {
        // use fallback API
        return doc.hasFocus();
      }
    }

    // FUNCTIONS

    function openMonitor() {
      monitor.open('window-information@public');
    }

    function startMonitor() {

      // exit if already monitoring
      if (monitoring) {
        return;
      }

      monitoring = 1;

      // listen to window events
      rigWindow();

      // start watching window, if focused
      if (hasFocus()) {
        watchWindowPosition();
      }
    }

    function stopMonitor() {
      if (!monitoring) {
        return;
      }
      monitoring = 0;

      unrigWindow();
      unwatchWindowPosition();
    }

    function rigWindow() {
      // starts watching window motion, when this window is focused
      bind(scope, 'focus', onFocus);
      // stops watching window motion, when this window is blurred
      bind(scope, 'blur', onBlur);
      // always observe scroll changes
      bind(scope, 'scroll', onScroll);
      // always observe resize changes
      bind(scope, 'resize', onResize);
    }

    function unrigWindow() {
      unbind(scope, 'focus', onFocus);
      unbind(scope, 'blur', onBlur);
      unbind(scope, 'scroll', onScroll);
      unbind(scope, 'resize', onResize);
    }

    function isActiveMonitor() {
      return sharedHeadObj.mid == monitor.id;
    }

    function getDocHeight() {
      return scope.innerHeight;
    }

    function getDocWidth() {
      return scope.innerWidth;
    }

    function getDocScroll() {
      return {
        scrollx: scope.pageXOffset,
        scrolly: scope.pageYOffset
      };
    }

    function getBrowserHeight() {
      return scope.outerHeight;
    }

    function getBrowserWidth() {
      return scope.outerWidth;
    }

    function getWindowPosition() {
      var
        bx = scope.screenX,
        by = scope.screenY;

      return {
        x: bx,
        y: by + (scope.outerHeight - scope.innerHeight),
        bx: bx,
        by: by,
      };
    }

    function updateResizeMetrics() {
      updateWindowMetrics(mix(getWindowDimensions(), getWindowPosition()));
    }

    function getWindowDimensions() {
      return {
        width: getDocWidth(),
        height: getDocHeight(),
        bwidth: getBrowserWidth(),
        bheight: getBrowserHeight()
      };
    }

    function watchWindowPosition() {
      unwatchWindowPosition();
      positionWatchInt = setInterval(checkWindowPosition, 100);
    }

    function unwatchWindowPosition() {
      clearInterval(positionWatchInt);
    }

    function checkWindowPosition() {
      var curCoord = getWindowPosition();

      if (
        !lastCoord ||
        lastCoord.x != curCoord.x ||
        lastCoord.y != curCoord.y
      ) {
        lastCoord = curCoord;
        // passing width and height, since those may have updated as well
        updateWindowMetrics(mix(lastCoord, getWindowDimensions()));
      }
    }

    // start watching for window changes
    function onFocus() {
      // start watching window
      watchWindowPosition();
      // broadcast focus change
      updateWindowMetrics({focus: true});
    }

    // stop watching for window changes
    function onBlur() {
      // stop watching window position
      unwatchWindowPosition();
      // broadcast focus change
      updateWindowMetrics({focus: false});
    }

    // update scroll position
    function onScroll() {
      // broadcast scroll change
      updateWindowMetrics(getDocScroll());
    }

    // update changed dimensions
    function onResize() {
      // kill final resize
      clearTimeout(finalResizeInt);
      // retrieve all, since resizing can occur from any corner
      updateResizeMetrics();
      // final resize
      finalResizeInt = setTimeout(updateResizeMetrics, 50);
    }

    function fireAddWindowEvent(wid, winfo) {
      winAry.fire('add', wid, winfo);
    }

    function fireRemoveWindowEvent(wid, winfo) {
      winAry.fire('remove', wid, winfo);
    }

    function fireUpdateWindowEvent(wid, winfo, changes) {
      winAry.fire('update', wid, winfo, changes);
    }

    function updateWindowMetrics(data) {
      var
        changed = {},
        deets = winMetrics.deets,
        key,
        value,
        hasChanges
      ;
      // keep changed values
      for (key in data) {
        if (
          protoHas.call(data, key) &&
          (
            !protoHas.call(deets, key) ||
            deets[key] != (value = data[key])
          )
        ) {
          // capture changed value
          changed[key] =
          // update metric
          deets[key] =
            value;
          // not that there is something to send
          hasChanges = 1;
        }
      }

      // exit if nothing has changed
      if (!hasChanges) {
        return;
      }

      // broadcast changed/new values
      sendWindowMetrics(changed);

      // notify self
      next(function () {
        fireUpdateWindowEvent(windowId, deets, changed);
      });
    }

    function sendWindowMetrics(data, target) {
      monitor._transmit('subetha/winfo', target, {
        id: windowId,
        cnt: sharedHeadObj.cnt,
        deets: data
      });
    }

    function removeAllWindows() {
      var
        windoid,
        wid;

      for (wid in winObjs) {
        if (protoHas.call(winObjs, wid)) {
          windoid = winObjs[wid];
          fireRemoveWindowEvent(windoid.id, windoid.deets);
        }
      }
      winObjs = {};
      winAry.length = 0;
    }

    // IMPLEMENTATION

    // enchance winfo array
    mix(winAry,
      // add event emitter and expose winfo
      subetha.EventEmitter.prototype,
      {
        // stop monitoring windows
        stop: function () {
          autoReconnect = 0;
          monitor.close();
        },
        start: openMonitor
      }
    );
    // expose public array
    subetha.winfo = winAry;

    if (scope == self) {

      // simple flag indicating whether winfo can work
      winAry.unsupported = 0;

      // setup monitor
      monitor
        .on('::connect', function () {
          var me = this;

          docBody = doc.body;
          docHead = doc.head;

          // resolve tracker for all window monitors
          if (!protoHas.call(docHead, '_winfo')) {
            // init shared tracker
            docHead._winfo = {
              // window id
              wid: subetha.guid(),
              // id of monitor tracking this window
              mid: me.id,
              // number of monitors for this window
              cnt: 0
            };
          }

          // alias shared object
          sharedHeadObj = docHead._winfo;
          // increment monitor count
          sharedHeadObj.cnt++;

          // alias window id
          windowId = sharedHeadObj.wid;

          if (isActiveMonitor()) {
            // create and add winMetrics to public array
            winMetrics =
            winObjs[windowId] = {
              id: windowId,
              cnt: sharedHeadObj.cnt,
              deets: mix(
                {
                  focus: hasFocus()
                },
                getWindowPosition(),
                getWindowDimensions(),
                getDocScroll()
              )
            };

            winAry[0] = winMetrics.deets;

            // start watching window
            startMonitor();

            // announce this window to self next
            next(function () {
              fireAddWindowEvent(windowId, winMetrics.deets);
            });
            setTimeout(function () {
              // introduce this window to peers
              sendWindowMetrics(winMetrics.deets);
            },5);
          }
        })
        .on('::disconnect', function () {
          // decrement number of monitors for this page
          sharedHeadObj.cnt--;

          // if this monitor was currently tracking this window
          if (isActiveMonitor()) {
            // stop watching the window
            stopMonitor();
          }

          // if no other monitors remain...
          if (!sharedHeadObj.cnt) {
            // // remove winfo tracker from HEAD node
            delete docHead._winfo;
            // remove winMetrics' details from public array
            removeFromArray(winAry, winMetrics.deets);
            if (autoReconnect) {
              // monitor should always be active?
              next(openMonitor);
            }
            autoReconnect = 1;
            removeAllWindows();
          }

          // nullify local reference
          sharedHeadObj = 0;
        })
        .on('::join', function (peer, exists) {
          // exit pre-existing peers
          if (exists) {
            return;
          }

          // send metrics to peer
          sendWindowMetrics(winMetrics.deets, peer);
        })
        .on('::drop', function (peer) {
          var
            wid = peer.wid,
            winfo;

          // exit if there is no window information associated with this peer
          if (!wid || !protoHas.call(winObjs, wid)) {
            return;
          }
          winfo = winObjs[wid];

          // decrement number of monitors
          winfo.cnt--;

          // if this peer was monitoring this window...
          if (sharedHeadObj.mid == peer.id) {
            // set monitor self
            sharedHeadObj.mid = this.id;
            // use winfo as winMetric
            winMetrics = winfo;
            // get updated count
            // this has already been decremented by the departing monitor
            winMetrics.cnt = sharedHeadObj.cnt;
            // start watching window
            startMonitor();
          } else if (!winfo.cnt) { // take action when there are no more monitors for this window
            // remove deets from public array
            removeFromArray(winAry, winfo.deets);
            // remove window info object
            delete winObjs[wid];
            // announce removal of window
            fireRemoveWindowEvent(wid, winfo.deets);
          }
        })
      ;

      // add subetha/winfo type
      /*
      payload
      {
        id: // window id
        cnt: // number of monitors for this window
        deets: { // updated window information
          x: // x coord
          y: // y coord
          width: // doc width
          height: // doc height
          bwidth: // browser width
          bheight: // browser height
          scrollx: // horizontal scroll position
          scrolly: // vertical scroll position
          hasscrollx: // flag when has horizontal scroll bar
          hasscrolly: // flag when has vertical scroll bar
          focus: // focus (truthy)
        }
      }
      */
      subetha.msgType['subetha/winfo'] = function (client, peer, payload) {
        var
          deets,
          winfo,
          wid,
          adding;

        // exit when payload is invalid
        if (
          !payload ||
          typeof payload != 'object' ||
          typeof payload.cnt != 'number' ||
          typeof (wid = payload.id) != 'string' ||
          typeof (deets = payload.deets) != 'object' ||
          !deets
        ) {
          return;
        }

        // if there is no window info object for this window
        if (protoHas.call(winObjs, wid)) {
          winfo = winObjs[wid];
        } else {
          // init window information object - start with id
          winfo =
          winObjs[wid] =
            {
              id: wid,
              deets: {
                id: wid
              }
            };
          // add deets to public array
          winAry.push(winfo.deets);
          // link window to this peer - this way we can check the window when this peer disconnects
          peer.wid = wid;
          // flag that this update is for a new window
          adding = 1;
        }

        // update window details
        mix(winfo.deets, deets);
        // update meta-data
        winfo.cnt = payload.cnt;

        if (adding) {
          fireAddWindowEvent(wid, winfo.deets);
        } else {
          fireUpdateWindowEvent(wid, winfo.deets, deets);
        }
      };

      // connect to network
      openMonitor();

    } else {

      // Can't work in iframe until we can identify which iframes belong to which window
      winAry.unsupported = 1;

    }

    return subetha;
  }

  // initialize and expose module, based on the environment
  if (inAMD) {
    define(initSubEthaWinfo);
  } else if (inCJS) {
    module.exports = initSubEthaWinfo();
  } else if (scope.Subetha && !scope.Subetha.winfo) {
    // tack on to existing namespace
    initSubEthaWinfo();
  }
}(
  typeof define === 'function',
  typeof exports != 'undefined',
  this
);<|endoftext|>


<commit_before>//
// Expanders for core syntaxes
//
import { List, Cons, isPair, isList } from "../pair.js"
import { Sym } from "../symbol.js"
import { isIdentifier, unwrapSyntax } from "./syntactic_closure.js"

const expandLambda = async ([form, xp]) => {
  const err = new BiwaError("malformed lambda", form);
  const l = form.to_array();
  if (l.length < 3) throw err;
  const formals = form.cadr(err);
  if (!isPair(formals)) throw err;
  const body = form.cddr(err);

  const newEnv = env.extend(formal-list);

  // TODO

  return Cons(Sym("lambda"),
           Cons(newFormals, newBody));
};

const expandIf = async ([form, xp]) => {
  const l = form.to_array();
  switch (l.length) {
    case 3:
      return List(Sym("if"), await xp.expand(l[1]), await xp.expand(l[2]));
    case 4:
      return List(Sym("if"), await xp.expand(l[1]), await xp.expand(l[2]), await xp.expand(l[3]));
    default:
      throw new BiwaError("malformed if", form);
  }
};

const expandQuote = async ([form, xp]) => {
  const l = form.to_array();
  switch (l.length) {
    case 2:
      return List(Sym("quote"), unwrapSyntax(l[1]));
    default:
      throw new BiwaError("malformed quote", form);
  }
};

const expandSet = async ([form, xp]) => {
  const l = form.to_array();
  switch (l.length) {
    case 3:
      return List(Sym("set!"), await xp.expand(l[1]), await xp.expand(l[2]));
    default:
      throw new BiwaError("malformed set!", form);
  }
};

const expandCallCc = async ([form, xp]) => {
  const l = form.to_array();
  switch (l.length) {
    case 2:
      return List(Sym("call/cc"), await xp.expand(l[1]));
    default:
      throw new BiwaError("malformed call/cc", form);
  }
};

const expandBegin = async ([form, xp]) => {
  const body = await form.cdr.mapAsync(item => xp.expand(item));
  return Cons(Sym("begin"), body);
};

// Destructively modifies `env`
const expandDefine = async ([form, xp, env]) => {
  const l = form.to_array();
  switch (l.length) {
    case 3:
      const [_, formal, expr] = l;
      if (!isIdentifier(formal)) {
        throw new BiwaError("malformed define", form);
      }
      env.extend(formal);
      const id = await xp.expand(formal);
      const body = env.isToplevel() ? (await xp.expand(expr))
                                    : expr; // Expand later on
      return List(Sym("define"), id, body);
    default:
      throw new BiwaError("malformed define", form);
  }
};

// TODO
// const expandDefineRecordType
// const expandParameterize
// const expandDefineSyntax
// const expandLetSyntax
// const expandLetrecSyntax
// const expandSyntaxError
// const expandInclude
// const expandIncludeCi
// const expandIfExpand
// const expandCaseLambda

// Install core expanders into `lib`
const installCore = (lib) => {
  lib.exportMacro(Sym("lambda"), expandLambda);
  lib.exportMacro(Sym("if"), expandIf);
  lib.exportMacro(Sym("quote"), expandQuote);
  lib.exportMacro(Sym("set!"), expandSet);
  lib.exportMacro(Sym("call/cc"), expandCallCc);
  lib.exportMacro(Sym("begin"), expandBegin);
  lib.exportMacro(Sym("define"), expandDefine);
};
export { installCore };
<commit_msg>refactor: Sort by alphabetic order<commit_after>//
// Expanders for core syntaxes
//
import { List, Cons, isPair, isList } from "../pair.js"
import { Sym } from "../symbol.js"
import { isIdentifier, unwrapSyntax } from "./syntactic_closure.js"

// `begin`
const expandBegin = async ([form, xp]) => {
  const body = await form.cdr.mapAsync(item => xp.expand(item));
  return Cons(Sym("begin"), body);
};

// `call/cc`
const expandCallCc = async ([form, xp]) => {
  const l = form.to_array();
  switch (l.length) {
    case 2:
      return List(Sym("call/cc"), await xp.expand(l[1]));
    default:
      throw new BiwaError("malformed call/cc", form);
  }
};

// `define`
// Destructively modifies `env`
const expandDefine = async ([form, xp, env]) => {
  const l = form.to_array();
  switch (l.length) {
    case 3:
      const [_, formal, expr] = l;
      if (!isIdentifier(formal)) {
        throw new BiwaError("malformed define", form);
      }
      env.extend(formal);
      const id = await xp.expand(formal);
      const body = env.isToplevel() ? (await xp.expand(expr))
                                    : expr; // Expand later on
      return List(Sym("define"), id, body);
    default:
      throw new BiwaError("malformed define", form);
  }
};

// `if`
const expandIf = async ([form, xp]) => {
  const l = form.to_array();
  switch (l.length) {
    case 3:
      return List(Sym("if"), await xp.expand(l[1]), await xp.expand(l[2]));
    case 4:
      return List(Sym("if"), await xp.expand(l[1]), await xp.expand(l[2]), await xp.expand(l[3]));
    default:
      throw new BiwaError("malformed if", form);
  }
};

// `lambda`
const expandLambda = async ([form, xp]) => {
  const err = new BiwaError("malformed lambda", form);
  const l = form.to_array();
  if (l.length < 3) throw err;
  const formals = form.cadr(err);
  if (!isPair(formals)) throw err;
  const body = form.cddr(err);

  const newEnv = env.extend(formal-list);

  // TODO

  return Cons(Sym("lambda"),
           Cons(newFormals, newBody));
};

// `quote`
const expandQuote = async ([form, xp]) => {
  const l = form.to_array();
  switch (l.length) {
    case 2:
      return List(Sym("quote"), unwrapSyntax(l[1]));
    default:
      throw new BiwaError("malformed quote", form);
  }
};

// `set!`
const expandSet = async ([form, xp]) => {
  const l = form.to_array();
  switch (l.length) {
    case 3:
      return List(Sym("set!"), await xp.expand(l[1]), await xp.expand(l[2]));
    default:
      throw new BiwaError("malformed set!", form);
  }
};

// TODO
// const expandDefineRecordType
// const expandParameterize
// const expandDefineSyntax
// const expandLetSyntax
// const expandLetrecSyntax
// const expandSyntaxError
// const expandInclude
// const expandIncludeCi
// const expandIfExpand
// const expandCaseLambda

// Install core expanders into `lib`
const installCore = (lib) => {
  lib.exportMacro(Sym("begin"), expandBegin);
  lib.exportMacro(Sym("call/cc"), expandCallCc);
  lib.exportMacro(Sym("define"), expandDefine);
  lib.exportMacro(Sym("if"), expandIf);
  lib.exportMacro(Sym("lambda"), expandLambda);
  lib.exportMacro(Sym("quote"), expandQuote);
  lib.exportMacro(Sym("set!"), expandSet);
};
export { installCore };
<|endoftext|>


<commit_before>// Welcome!
// Add your github user if you accepted the challenge!

var players = [
    'raphamorim',
    'israelst',
    'guisouza',
    'khaosdoctor',
    'afonsopacifer',
    'rafaelfragosom',
    'paulinhoerry',
    'enieber',
    'alanrsoares',
    'viniciusdacal',
    'thiagosantana',
    'danilorb',
    'douglasPinheiro',
    'michelwilhelm',
    'rafaelstz',
    'luhad',
    'pablobfonseca',
    'willianjusten',
    'vitorleal',
    'IAMFELIPEMATTOS',
    'JulianSansat',
    'brenopolanski',
    'Pompeu',
    'filipedeschamps',
    'felipesousa',
    'gpedro',
    'leaofelipe',
    'wedneyyuri',
    'jonathanveg',
    'filipe1309',
    'afgoulart',
    'helquisson',
    'vulcanobr',
    'guilouro',
    'pedropolisenso',
    'jozadaquebatista',
    'cyberglot',
    'guilhermeepaixaoo',
    'morvanabonin',
    'andrecgro',
    'thulioph',
    'lucasviola',
    'iceberg20',
    'hugolnx',
    'rafael-neri',
    'chocsx',
    'rtancman',
    'welksonramos',
    'igorsantos07',
    'marabesi',
    'diegosaraujo',
    'mauriciojunior',
    'Rodrigo54',
    'woliveiras',
    'AgtLucas',
    'fredericksilva',
    'theandersonn',
    'jpklzm',
    'edueo',
    'vicnicius',
    'jhonmike',
    'fdaciuk',
    'cesardeazevedo',
    'vinimdocarmo',
    'deseao',
    'henriquebelfort',
    'mabrasil',
    'MacgyverMartins',
    'jackmakiyama',
    'hocraveiro',
    'Gcampes',
    'Nipher',
    'lnlwd',
    'mshmeirelles',
    'altemberg',
    'lucasmaiaesilva',
    'zephyru5',
    'mvfsilva',
    'anselmbradford',
    'raulsenaferreira',
    'leonardosoares',
    'CruiseDevice',
    'jianweichuah',
    'andreabadesso',
    'johnynogueira',
    'karlpatrickespiritu',
    'Molax',
    'chroda',
    'Adejair',
    'gkal19',
    'naltun',
    'shyam12860',
    'snaka',
    'jeanbauer',
    'PabloDinella',
    'matheus-manoel',
    'onlurking',
    'lazarofl',
    'ricardosllm',
    'Dborah',
    'cstipkovic',
    'matheuswd',
    'hevertoncastro',
    'alexwbuschle',
    'diogomoretti',
    'willianschenkel',
    'gabrieldeveloper',
    'hammy25',
    'riquellopes',
    'grvcoelho',
    'sujcho',
    'jakedex',
    'SambitAcharya',
    'caarlos0',
    'andreicek',
    'lccezinha',
    'deividi86',
    'renanidc',
    'CesarNobre',
    'gnomex',
    'xrexonx',
    'adichat',
    'Khwilo',
    'lamenezes',
    'RodrigoDev',
    'Cigarent',
    'medeirosthiiago',
    'orlandojsjr',
    'yuribrunetto',
    'adekola',
    'tkovs',
    'echessa',
    'guicheffer',
    'felladrin',
    'gustavoteodoro',
    'MirzaI',
    'mikesprague',
    'cauealves',
    'nbfontana',
    'matheuspoleza',
    'eltonvs',
    'duyetdev',
    'diego-soares',
    'suuuzi',
    'ahmed',
    'danilovaz',
    'yurialessandro',
    'SaiUpadhyayula',
    'felipemfp',
    'chicobentojr',
    'MariiaKravtsova',
    'alessandroanjos',
    'fauverism',
    'hemersonvianna',
    'igoreliasm',
    'shokun39',
    'shakegioh',
    'crazysj',
    'devmath',
    'lsgoulart',
    'fernandoguedes',
    'lai32290',
    'arthurvasconcelos',
    'luancuba',
];

module.exports = players;
<commit_msg>Add Alesshh to the challenge<commit_after>// Welcome!
// Add your github user if you accepted the challenge!

var players = [
    'raphamorim',
    'israelst',
    'guisouza',
    'khaosdoctor',
    'afonsopacifer',
    'rafaelfragosom',
    'paulinhoerry',
    'enieber',
    'alanrsoares',
    'viniciusdacal',
    'thiagosantana',
    'danilorb',
    'douglasPinheiro',
    'michelwilhelm',
    'rafaelstz',
    'luhad',
    'pablobfonseca',
    'willianjusten',
    'vitorleal',
    'IAMFELIPEMATTOS',
    'JulianSansat',
    'brenopolanski',
    'Pompeu',
    'filipedeschamps',
    'felipesousa',
    'gpedro',
    'leaofelipe',
    'wedneyyuri',
    'jonathanveg',
    'filipe1309',
    'afgoulart',
    'helquisson',
    'vulcanobr',
    'guilouro',
    'pedropolisenso',
    'jozadaquebatista',
    'cyberglot',
    'guilhermeepaixaoo',
    'morvanabonin',
    'andrecgro',
    'thulioph',
    'lucasviola',
    'iceberg20',
    'hugolnx',
    'rafael-neri',
    'chocsx',
    'rtancman',
    'welksonramos',
    'igorsantos07',
    'marabesi',
    'diegosaraujo',
    'mauriciojunior',
    'Rodrigo54',
    'woliveiras',
    'AgtLucas',
    'fredericksilva',
    'theandersonn',
    'jpklzm',
    'edueo',
    'vicnicius',
    'jhonmike',
    'fdaciuk',
    'cesardeazevedo',
    'vinimdocarmo',
    'deseao',
    'henriquebelfort',
    'mabrasil',
    'MacgyverMartins',
    'jackmakiyama',
    'hocraveiro',
    'Gcampes',
    'Nipher',
    'lnlwd',
    'mshmeirelles',
    'altemberg',
    'lucasmaiaesilva',
    'zephyru5',
    'mvfsilva',
    'anselmbradford',
    'raulsenaferreira',
    'leonardosoares',
    'CruiseDevice',
    'jianweichuah',
    'andreabadesso',
    'johnynogueira',
    'karlpatrickespiritu',
    'Molax',
    'chroda',
    'Adejair',
    'gkal19',
    'naltun',
    'shyam12860',
    'snaka',
    'jeanbauer',
    'PabloDinella',
    'matheus-manoel',
    'onlurking',
    'lazarofl',
    'ricardosllm',
    'Dborah',
    'cstipkovic',
    'matheuswd',
    'hevertoncastro',
    'alexwbuschle',
    'diogomoretti',
    'willianschenkel',
    'gabrieldeveloper',
    'hammy25',
    'riquellopes',
    'grvcoelho',
    'sujcho',
    'jakedex',
    'SambitAcharya',
    'caarlos0',
    'andreicek',
    'lccezinha',
    'deividi86',
    'renanidc',
    'CesarNobre',
    'gnomex',
    'xrexonx',
    'adichat',
    'Khwilo',
    'lamenezes',
    'RodrigoDev',
    'Cigarent',
    'medeirosthiiago',
    'orlandojsjr',
    'yuribrunetto',
    'adekola',
    'tkovs',
    'echessa',
    'guicheffer',
    'felladrin',
    'gustavoteodoro',
    'MirzaI',
    'mikesprague',
    'cauealves',
    'nbfontana',
    'matheuspoleza',
    'eltonvs',
    'duyetdev',
    'diego-soares',
    'suuuzi',
    'ahmed',
    'danilovaz',
    'yurialessandro',
    'SaiUpadhyayula',
    'felipemfp',
    'chicobentojr',
    'MariiaKravtsova',
    'alessandroanjos',
    'fauverism',
    'hemersonvianna',
    'igoreliasm',
    'shokun39',
    'shakegioh',
    'crazysj',
    'devmath',
    'lsgoulart',
    'fernandoguedes',
    'lai32290',
    'arthurvasconcelos',
    'luancuba',
    'alesshh'
];

module.exports = players;
<|endoftext|>


<commit_before>// Welcome!
// Add your github user if you accepted the challenge!

var players = [
    'raphamorim',
    'israelst',
    'afonsopacifer',
    'rafaelfragosom',
    'paulinhoerry',
    'enieber',
    'alanrsoares',
    'viniciusdacal',
    'thiagosantana',
    'brunodsgn',
    'danilorb',
    'douglasPinheiro',
    'michelwilhelm',
    'rafaelstz',
    'andersonweb',
    'luhad',
    'pablobfonseca',
    'willianjusten',
    'vitorleal',
    'IAMFELIPEMATTOS',
    'JulianSansat',
    'brenopolanski',
    'Pompeu',
    'filipedeschamps',
    'felipesousa',
    'gpedro',
    'leaofelipe',
    'wedneyyuri',
    'jonathanveg',
    'filipe1309',
    'afgoulart',
    'helquisson',
    'vulcanobr',
    'guilouro',
    'pedropolisenso',
    'jozadaquebatista',
    'cyberglot',
    'guilhermeepaixaoo',
    'morvanabonin',
    'andrecgro',
    'thulioph',
    'lucasviola',
    'iceberg20',
    'hugolnx',
    'rafael-neri',
    'chocsx',
    'rtancman',
    'welksonramos',
    'igorsantos07',
    'marabesi',
    'diegosaraujo',
    'mauriciojunior',
    'Rodrigo54',
    'woliveiras',
    'AgtLucas',
    'fredericksilva',
    'theandersonn',
    'jpklzm',
    'edueo',
    'vicnicius',
    'jhonmike',
    'fdaciuk',
    'cesardeazevedo',
    'vinimdocarmo',
    'deseao',
    'henriquebelfort',
    'mabrasil',
    'MacgyverMartins',
    'jackmakiyama',
    'hocraveiro',
    'Gcampes',
    'Nipher',
    'abysmum',
    'lnlwd',
    'mshmeirelles',
    'altemberg',
    'lucasmaiaesilva',
    'zephyru5',
    'afang',
    'mvfsilva',
    'anselmbradford',
    'raulsenaferreira',
    'leonardosoares',
    'CruiseDevice',
    'jianweichuah',
    'andreabadesso',
    'johnynogueira',
    'karlpatrickespiritu',
    'Molax',
    'chroda',
    'Adejair',
    'gkal19',
    'naltun',
    'shyam12860',
    'snaka',
    'jeanbauer',
    'PabloDinella',
    'matheus-manoel',
    'onlurking',
    'lazarofl',
    'ricardosllm',
];

module.exports = players;
<commit_msg>acrescentando meu nome na lista<commit_after>// Welcome!
// Add your github user if you accepted the challenge!

var players = [
    'raphamorim',
    'israelst',
    'afonsopacifer',
    'rafaelfragosom',
    'paulinhoerry',
    'enieber',
    'alanrsoares',
    'viniciusdacal',
    'thiagosantana',
    'brunodsgn',
    'danilorb',
    'douglasPinheiro',
    'michelwilhelm',
    'rafaelstz',
    'andersonweb',
    'luhad',
    'pablobfonseca',
    'willianjusten',
    'vitorleal',
    'IAMFELIPEMATTOS',
    'JulianSansat',
    'brenopolanski',
    'Pompeu',
    'filipedeschamps',
    'felipesousa',
    'gpedro',
    'leaofelipe',
    'wedneyyuri',
    'jonathanveg',
    'filipe1309',
    'afgoulart',
    'helquisson',
    'vulcanobr',
    'guilouro',
    'pedropolisenso',
    'jozadaquebatista',
    'cyberglot',
    'guilhermeepaixaoo',
    'morvanabonin',
    'andrecgro',
    'thulioph',
    'lucasviola',
    'iceberg20',
    'hugolnx',
    'rafael-neri',
    'chocsx',
    'rtancman',
    'welksonramos',
    'igorsantos07',
    'marabesi',
    'diegosaraujo',
    'mauriciojunior',
    'Rodrigo54',
    'woliveiras',
    'AgtLucas',
    'fredericksilva',
    'theandersonn',
    'jpklzm',
    'edueo',
    'vicnicius',
    'jhonmike',
    'fdaciuk',
    'cesardeazevedo',
    'vinimdocarmo',
    'deseao',
    'henriquebelfort',
    'mabrasil',
    'MacgyverMartins',
    'jackmakiyama',
    'hocraveiro',
    'Gcampes',
    'Nipher',
    'abysmum',
    'lnlwd',
    'mshmeirelles',
    'altemberg',
    'lucasmaiaesilva',
    'zephyru5',
    'afang',
    'mvfsilva',
    'anselmbradford',
    'raulsenaferreira',
    'leonardosoares',
    'CruiseDevice',
    'jianweichuah',
    'andreabadesso',
    'johnynogueira',
    'karlpatrickespiritu',
    'Molax',
    'chroda',
    'Adejair',
    'gkal19',
    'naltun',
    'shyam12860',
    'snaka',
    'jeanbauer',
    'PabloDinella',
    'matheus-manoel',
    'onlurking',
    'lazarofl',
    'ricardosllm',
    'Dborah',
];

module.exports = players;
<|endoftext|>


<commit_before>import test from 'tape';
import { evaluation } from '../';

test('Evaluation', t => {
  t.plan(20);

  t.equal(evaluation('bar[0]')({bar: [0, 1, 2]}), 0, 'index 0');
  t.equal(evaluation('bar[1]')({bar: [0, 1, 2]}), 1, 'index x');
  t.equal(evaluation('bar[9]')({bar: [0, 1, 2]}), undefined, 'index overflow');

  t.equal(evaluation('bar[-0]')({bar: [0, 1, 2]}), 2, 'reverse index 0');
  t.equal(evaluation('bar[-1]')({bar: [0, 1, 2]}), 1, 'reverse index x');
  t.equal(evaluation('bar[-9]')({bar: [0, 1, 2]}), undefined, 'reverse index overflow');

  t.equal(evaluation('length')('abc'), 3, 'access string length');
  t.equal(evaluation('foo.length')({foo: 'abc'}), 3, 'access string length deep');
  t.equal(evaluation('length')([0, 1, 2]), 3, 'access array length');
  t.equal(evaluation('bar.length')({bar: [0, 1, 2]}), 3, 'access array length deep');

  t.equal(evaluation('[1]')([0, 3, 2]), 3, 'index x from array');
  t.equal(evaluation('[-0]')([0, 3, 2]), 2, 'index x from array reversed');

  t.equal(evaluation()(2), 2, 'by undefined');

  const deep = evaluation('foo.bar[3]');

  t.equal(deep(), undefined, 'missing object');
  t.equal(deep({}), undefined, 'missing key: level 1');
  t.equal(deep({foo: true}), undefined, 'missing key: level 2');
  t.equal(deep({foo: {bar: true}}), undefined, 'missing key: bool');
  t.equal(deep({foo: {bar: {baz: 2}}}), undefined, 'missing key: num');
  t.equal(deep({foo: {bar: []}}), undefined, 'missing array value');

  t.throws(() => {
    evaluation(2)({bar: []});
  }, /bad access path/, 'throw on truthy non-strings');
});
<commit_msg>adding index test for string<commit_after>import test from 'tape';
import { evaluation } from '../';

test('Evaluation', t => {
  t.plan(22);

  t.equal(evaluation('bar[0]')({bar: [0, 1, 2]}), 0, 'index 0');
  t.equal(evaluation('bar[1]')({bar: [0, 1, 2]}), 1, 'index x');
  t.equal(evaluation('bar[9]')({bar: [0, 1, 2]}), undefined, 'index overflow');

  t.equal(evaluation('bar[-0]')({bar: [0, 1, 2]}), 2, 'reverse index 0');
  t.equal(evaluation('bar[-1]')({bar: [0, 1, 2]}), 1, 'reverse index x');
  t.equal(evaluation('bar[-9]')({bar: [0, 1, 2]}), undefined, 'reverse index overflow');

  t.equal(evaluation('length')('abc'), 3, 'access string length');
  t.equal(evaluation('foo.length')({foo: 'abc'}), 3, 'access string length deep');
  t.equal(evaluation('length')([0, 1, 2]), 3, 'access array length');
  t.equal(evaluation('bar.length')({bar: [0, 1, 2]}), 3, 'access array length deep');

  t.equal(evaluation('[1]')([0, 3, 2]), 3, 'index x from array');
  t.equal(evaluation('[-0]')([0, 3, 2]), 2, 'index x from array reversed');

  t.equal(evaluation('[0]')('boy'), 'b', 'index x from string');
  t.equal(evaluation('[-1]')('boy'), 'o', 'index x from string reversed');

  t.equal(evaluation()(2), 2, 'by undefined');

  const deep = evaluation('foo.bar[3]');

  t.equal(deep(), undefined, 'missing object');
  t.equal(deep({}), undefined, 'missing key: level 1');
  t.equal(deep({foo: true}), undefined, 'missing key: level 2');
  t.equal(deep({foo: {bar: true}}), undefined, 'missing key: bool');
  t.equal(deep({foo: {bar: {baz: 2}}}), undefined, 'missing key: num');
  t.equal(deep({foo: {bar: []}}), undefined, 'missing array value');

  t.throws(() => {
    evaluation(2)({bar: []});
  }, /bad access path/, 'throw on truthy non-strings');
});
<|endoftext|>


<commit_before>var camel = require('camel-case');

/**
 * Map of module names to tuples of short alias name and require definition.
 * Long alias names will be generated automatically based on the module name.
 *
 * @type {Object}
 */
var ALIASES = {
  'dot-case':         ['dot', require('dot-case')],
  'swap-case':        ['swap', require('swap-case')],
  'path-case':        ['path', require('path-case')],
  'upper-case':       ['upper', require('upper-case')],
  'lower-case':       ['lower', require('lower-case')],
  'camel-case':       ['camel', require('camel-case')],
  'snake-case':       ['snake', require('snake-case')],
  'title-case':       ['title', require('title-case')],
  'param-case':       ['param', require('param-case')],
  'pascal-case':      ['pascal', require('pascal-case')],
  'constant-case':    ['constant', require('constant-case')],
  'sentence-case':    ['sentence', require('sentence-case')],
  'is-upper-case':    ['isUpper', require('is-upper-case')],
  'is-lower-case':    ['isLower', require('is-lower-case')],
  'upper-case-first': ['ucFirst', require('upper-case-first')],
};

/**
 * Automatically alias all modules and short names.
 */
Object.keys(ALIASES).forEach(function (name) {
  module.exports[camel(name)] = module.exports[ALIASES[name][0]] = ALIASES[name][1];
});
<commit_msg>no computation, just straight exports :ok::sheep:<commit_after>/**
 * Straight exports with short alias, to support browserify.
 *
 */
exports.dot      = exports.dotCase        = require('dot-case')
exports.swap     = exports.swapCase       = require('swap-case')
exports.path     = exports.pathCase       = require('path-case')
exports.upper    = exports.upperCase      = require('upper-case')
exports.lower    = exports.lowerCase      = require('lower-case')
exports.camel    = exports.camelCase      = require('camel-case')
exports.snake    = exports.snakeCase      = require('snake-case')
exports.title    = exports.titleCase      = require('title-case')
exports.param    = exports.paramCase      = require('param-case')
exports.pascal   = exports.pascalCase     = require('pascal-case')
exports.constant = exports.constantCase   = require('constant-case')
exports.sentence = exports.sentenceCase   = require('sentence-case')
exports.isUpper  = exports.isUpperCase    = require('is-upper-case')
exports.isLower  = exports.isLowerCase    = require('is-lower-case')
exports.ucFirst  = exports.upperCaseFirst = require('upper-case-first')
<|endoftext|>


<commit_before>import { setupMultistoreRoutes } from '@vue-storefront/core/lib/multistore'
import App from './App.vue'
import routes from './router'
import Vue from 'vue'
import VueProgressBar from 'vue-progressbar'
import '@vue-storefront/core/lib/passive-listeners'
import { RouterManager } from '@vue-storefront/core/lib/router-manager'
import { once } from '@vue-storefront/core/helpers'
import { registerModule } from '@vue-storefront/module'
// temp
import { ExampleModule } from 'packages/cli/boilerplates/module'
once('__VUE_EXTEND_DROPPOINT_VPB__', () => {
  Vue.use(VueProgressBar)
})

const themeEntry = App
function initTheme (app, router, store, config, ssrContext) {
  // if youre' runing multistore setup this is copying the routed above adding the 'storeCode' prefix to the urls and the names of the routes
  // You can do it on your own and then be able to customize the components used for example for German storeView checkout
  // To do so please execlude the desired storeView from the config.storeViews.mapStoreUrlsFor and map the urls by Your own like:
  // { name: 'de-checkout', path: '/checkout', component: CheckoutCustomized },
  registerModule(ExampleModule, {
    message: 'Hello World!'
  })
  setupMultistoreRoutes(config, router, routes)
  RouterManager.addRoutes(routes, router)
}

export {
  themeEntry,
  initTheme
}
<commit_msg>remove demo module<commit_after>import { setupMultistoreRoutes } from '@vue-storefront/core/lib/multistore'
import App from './App.vue'
import routes from './router'
import Vue from 'vue'
import VueProgressBar from 'vue-progressbar'
import '@vue-storefront/core/lib/passive-listeners'
import { RouterManager } from '@vue-storefront/core/lib/router-manager'
import { once } from '@vue-storefront/core/helpers'

once('__VUE_EXTEND_DROPPOINT_VPB__', () => {
  Vue.use(VueProgressBar)
})

const themeEntry = App
function initTheme (app, router, store, config, ssrContext) {
  // if youre' runing multistore setup this is copying the routed above adding the 'storeCode' prefix to the urls and the names of the routes
  // You can do it on your own and then be able to customize the components used for example for German storeView checkout
  // To do so please execlude the desired storeView from the config.storeViews.mapStoreUrlsFor and map the urls by Your own like:
  // { name: 'de-checkout', path: '/checkout', component: CheckoutCustomized },
  setupMultistoreRoutes(config, router, routes)
  RouterManager.addRoutes(routes, router)
}

export {
  themeEntry,
  initTheme
}
<|endoftext|>


<commit_before>'use babel'

import {React} from 'react-for-atom'
import classNames from 'classnames'

export default React.createClass({

  propTypes: {
    children: React.PropTypes.array,
    file: React.PropTypes.object,
    isSelected: React.PropTypes.bool,
    index: React.PropTypes.number,
    selectIndexBus: React.PropTypes.object
  },

  render () {
    return (

        {this.props.children}

    )
  },

  _onClick (file) {
    this.props.selectIndexBus.push(this.props.index)
  }

})
<commit_msg>:fire: Unused arg<commit_after>'use babel'

import {React} from 'react-for-atom'
import classNames from 'classnames'

export default React.createClass({

  propTypes: {
    children: React.PropTypes.array,
    file: React.PropTypes.object,
    isSelected: React.PropTypes.bool,
    index: React.PropTypes.number,
    selectIndexBus: React.PropTypes.object
  },

  render () {
    return (

        {this.props.children}

    )
  },

  _onClick () {
    this.props.selectIndexBus.push(this.props.index)
  }

})
<|endoftext|>


<commit_before>'use babel';

/* @flow*/

import { Observable } from 'rxjs';
import { TEST, TEST_LAST, TEST_PROJECT, errorAction } from '../actions';

export default function testLast(action$: Observable) {
  return action$.ofType(TEST_LAST)
    .switchMap(() => action$.ofType(TEST || TEST_PROJECT)
      .last()
      .switchMap((action) => {
        console.log(action);
        return Observable.empty();
      }))
    .catch(err => Observable.of(errorAction(err)));
}
<commit_msg>refactor<commit_after>'use babel';

/* @flow*/

import { Observable } from 'rxjs';
import { START_TEST, TEST_LAST, errorAction } from '../actions';

export default function testLast(action$: Observable) {
  return action$.ofType(TEST_LAST)
    .switchMap(() => action$.ofType(START_TEST)
      .last()
      .switchMap((action) => {
        console.log(action);
        return Observable.empty();
      }))
    .catch(err => Observable.of(errorAction(err)));
}
<|endoftext|>


<commit_before>'use strict';

var _ = require('lodash');
var util = require('util');
var BaseRelation = require('./base');
var attr = require('../model/attr').fn;
var property = require('../util/property').fn;
var inflection = require('../util/inflection');

_.str = require('underscore.string');

/**
 * The belongs to property for models.
 *
 * Documentation forthcoming.
 *
 * For example:
 *
 *     var Article = Model.extend({
 *       author: belongsTo('user', { foreignKey: 'user_id', primaryKey: 'id' })
 *     });
 *
 * @since 1.0
 * @public
 * @constructor BelongsTo
 * @extends BaseRelation
 */
var BelongsTo = BaseRelation.extend(/** @lends BelongsTo# */ {
  init: function(name, modelClass, relatedModel, options) {
    this._super(name, modelClass);
    this._relatedModel = _.isString(relatedModel) ?
      modelClass.db.model(relatedModel) : relatedModel;
    this._options = options || {};
    this._itemCacheKey = '_' + this._name;
    this._itemObjectsQueryCacheKey =
      '_' + _.str.camelize(this._name + '_objectsQueryCache');
  },

  /**
   * Documentation forthcoming.
   *
   * @since 1.0
   * @private
   * @type {String}
   */
  inverse: property(function() {
    if (!this._inverse) {
      // using underscored ensures that the class name is properly lowercased
      var modelNameUnderscored = _.str.underscored(this._modelClass.__name__);
      var modelNameUnderscoredPlural =
        inflection.pluralize(modelNameUnderscored);
      this._inverse = this._options.inverse ||
        _.str.camelize(modelNameUnderscoredPlural);
    }
    return this._inverse;
  }),

  /**
   * Documentation forthcoming.
   *
   * @since 1.0
   * @private
   * @type {String}
   */
  primaryKey: property(function() {
    if (!this._primaryKey) {
      this._primaryKey = this._options.primaryKey || 'id';
    }
    return this._primaryKey;
  }),

  /**
   * Documentation forthcoming.
   *
   * @since 1.0
   * @private
   * @type {String}
   */
  foreignKey: property(function() {
    if (!this._foreignKey) {
      this._foreignKey = this._options.foreignKey ||
        _.str.underscored(this._name + '_id');
    }
    return this._foreignKey;
  }),

  /**
   * Override of {@link BaseRelation#template}.
   *
   * @since 1.0
   * @protected
   * @method
   * @see {@link BaseRelation#template}
   */
  template: function(key) {
    return this._super(key)
      .replace('<foreignKey>', _.str.camelize(this.foreignKey));
  }

});

/**
 * This wrapper method is used to immediately invalidate the `objectsQuery`
 * cache when performing any sort of update to related objects.
 *
 * The resulting wrapper function expects to be called with the first argument
 * being the model instance on which it is operating. It will use this to
 * invalidate the `objectsQuery` cache. It also expects that it will be used as
 * an instance method on a `BelongsTo` relation.
 *
 * @since 1.0
 * @private
 * @method BelongsTo~invalidatesQuery
 * @param {Function} fn The function to wrap.
 * @return {Function} The wrapper function.
 * @see {@link BelongsTo#_cacheObjectsQuery}
 */
var invalidatesQuery = function(fn) {
  return function(instance) {
    this._cacheObjectsQuery(instance, undefined);
    return fn.apply(this, arguments);
  };
};

BelongsTo.reopen(/** @lends BelongsTo# */ {

  /**
   * The identity property for this relation.
   *
   * This property allows access to the underlying relation object, allowing
   * you to access details about how the relation was created/configured. This
   * method is currently only intended for internal use, but if you have a
   * reason for it to be documented publicly, please create an issue on GitHub
   * and let us know.
   *
   * It is accessible on an individual model via `<singular>Relation`. For
   * instance, an article that belongs to an author would cause this method to
   * get triggered via `article.authorRelation`.
   *
   * The naming conventions are set forth in {@link BelongsTo.methods}.
   *
   * @since 1.0
   * @protected
   * @method
   * @param {Model} instance The model instance on which to operate.
   * @see {@link BaseRelation#methods}
   */
  identity: function(/*instance*/) {
    return this;
  },

  /**
   * The item property for this relation.
   *
   * This property allows access to the cached object that has been fetched
   * for a specific model in a given relation. Before the cache has been
   * filled, accessing this property will throw an exception.
   *
   * It is accessible on an individual model via `<singular>` and via
   * `get<Singular>`. For instance, an article that belongs to an author would
   * cause this method to get triggered via `article.author` or
   * `article.getAuthor`.
   *
   * The naming conventions are set forth in {@link BelongsTo.methods}.
   *
   * @since 1.0
   * @protected
   * @method
   * @param {Model} instance The model instance on which to operate.
   * @see {@link BaseRelation#methods}
   */
  item: function(instance) {
    var result = instance[this._itemCacheKey];
    if (!result) {
      throw new Error(util.format('The relation "%s" has not yet been ' +
        'loaded.', this._name));
    }
    return result;
  },

  /**
   * Update the {@link BelongsTo#item} cache to contain a new value.
   *
   * @since 1.0
   * @protected
   * @method
   * @param {Model} instance The model instance on which to operate.
   * @param {Array} value The new value for the item cache.
   */
  _cacheItem: function(instance, value) {
    instance[this._itemCacheKey] = value[0];
  },

  /**
   * The item property setter for this relation.
   *
   * This property allows altering the associated object of a specific model in
   * a given relation.
   *
   * It is accessible on an individual model via assignment with `<singular>`
   * and via `set<Singular>`. For instance, an article that belongs to an
   * author would cause this method to get triggered via
   * `article.author = '...'` or `article.setAuthor`.
   *
   * The naming conventions are set forth in {@link BelongsTo.methods}.
   *
   * @since 1.0
   * @protected
   * @method
   * @param {Model} instance The model instance on which to operate.
   * @see {@link BaseRelation#methods}
   */
  setItem: function(instance, value) {
    instance[this._itemCacheKey] = value;
  },

  objectsQuery: function(instance) {
    var cacheName = this._itemObjectsQueryCacheKey;
    if (!instance[cacheName]) {
      var where = _.object([[this.primaryKey, instance.attrs[this.foreignKey]]]);
      var cacheResult = _.bind(this._cacheItem, this, instance);
      instance[cacheName] = this._relatedModel
        .objects.where(where)
        .on('result', cacheResult);
    }
    return instance[cacheName];
  },

  /**
   * Update the {@link BelongsTo#objectsQuery} cache to contain a new value.
   *
   * @since 1.0
   * @protected
   * @method
   * @param {Model} instance The model instance on which to operate.
   * @param {Object} value The new value.
   */
  _cacheObjectsQuery: function(instance, value) {
    instance[this._itemObjectsQueryCacheKey] = value;
  },

  /**
   * The create object method for this relation.
   *
   * This simply creates a new object that has the appropriate relation data
   * already set.
   *
   * This method invalidates the {@link BelongsTo#objectsQuery} cache and sets
   * the created object as the {@link BelongsTo#item}.
   *
   * It is accessible on an individual model via `create<Singular>`. For
   * instance, an article that belongs to an author would cause this method to
   * get triggered via `article.createAuthor`.
   *
   * The naming conventions are set forth in {@link BelongsTo.methods}.
   *
   * @since 1.0
   * @protected
   * @method
   * @param {Model} instance The model instance on which to operate.
   * @param {Array} args The arguments to the method.
   * @see {@link BaseRelation#methods}
   */
  create: invalidatesQuery(function(instance, args) {
    var relatedModel = this._relatedModel;
    var result = relatedModel.create.apply(relatedModel, args);
    // TODO: add to inverse here or in `setItem`?
    this.setItem(instance, result);
    return result;
  }),

  fetchItem: function(instance) {
    return this.objectsQuery(instance).execute()
    .then(function(result) {

      return result[0];
    });
  },

  storeItem: function() {

  }

});

BelongsTo.reopenClass(/** @lends BelongsTo */ {
  methods: {
    '<singular>': BaseRelation.property('item', 'setItem'),
    '<singular>Relation': BaseRelation.property('identity'),
    'get<Singular>': BaseRelation.method('item'),
    'set<Singular>': BaseRelation.method('setItem'),
    'create<Singular>': BaseRelation.method('create'),
    'fetch<Singular>': BaseRelation.method('fetchItem'),
    'store<Singular>': BaseRelation.method('storeItem'),
    '<foreignKey>': function(relation) {
      return attr(relation.foreignKey);
    },
  }
});

module.exports = BelongsTo.reopenClass({ __name__: 'BelongsTo' });
<commit_msg>Renamed methods.<commit_after>'use strict';

var _ = require('lodash');
var util = require('util');
var BaseRelation = require('./base');
var attr = require('../model/attr').fn;
var property = require('../util/property').fn;
var inflection = require('../util/inflection');

_.str = require('underscore.string');

/**
 * The belongs to property for models.
 *
 * Documentation forthcoming.
 *
 * For example:
 *
 *     var Article = Model.extend({
 *       author: belongsTo('user', { foreignKey: 'user_id', primaryKey: 'id' })
 *     });
 *
 * @since 1.0
 * @public
 * @constructor BelongsTo
 * @extends BaseRelation
 */
var BelongsTo = BaseRelation.extend(/** @lends BelongsTo# */ {
  init: function(name, modelClass, relatedModel, options) {
    this._super(name, modelClass);
    this._relatedModel = _.isString(relatedModel) ?
      modelClass.db.model(relatedModel) : relatedModel;
    this._options = options || {};
    this._itemCacheKey = '_' + this._name;
    this._itemObjectsQueryCacheKey =
      '_' + _.str.camelize(this._name + '_objectsQueryCache');
  },

  /**
   * Documentation forthcoming.
   *
   * @since 1.0
   * @private
   * @type {String}
   */
  inverse: property(function() {
    if (!this._inverse) {
      // using underscored ensures that the class name is properly lowercased
      var modelNameUnderscored = _.str.underscored(this._modelClass.__name__);
      var modelNameUnderscoredPlural =
        inflection.pluralize(modelNameUnderscored);
      this._inverse = this._options.inverse ||
        _.str.camelize(modelNameUnderscoredPlural);
    }
    return this._inverse;
  }),

  /**
   * Documentation forthcoming.
   *
   * @since 1.0
   * @private
   * @type {String}
   */
  primaryKey: property(function() {
    if (!this._primaryKey) {
      this._primaryKey = this._options.primaryKey || 'id';
    }
    return this._primaryKey;
  }),

  /**
   * Documentation forthcoming.
   *
   * @since 1.0
   * @private
   * @type {String}
   */
  foreignKey: property(function() {
    if (!this._foreignKey) {
      this._foreignKey = this._options.foreignKey ||
        _.str.underscored(this._name + '_id');
    }
    return this._foreignKey;
  }),

  /**
   * Override of {@link BaseRelation#template}.
   *
   * @since 1.0
   * @protected
   * @method
   * @see {@link BaseRelation#template}
   */
  template: function(key) {
    return this._super(key)
      .replace('<foreignKey>', _.str.camelize(this.foreignKey));
  }

});

/**
 * This wrapper method is used to immediately invalidate the `objectsQuery`
 * cache when performing any sort of update to related objects.
 *
 * The resulting wrapper function expects to be called with the first argument
 * being the model instance on which it is operating. It will use this to
 * invalidate the `objectsQuery` cache. It also expects that it will be used as
 * an instance method on a `BelongsTo` relation.
 *
 * @since 1.0
 * @private
 * @method BelongsTo~invalidatesQuery
 * @param {Function} fn The function to wrap.
 * @return {Function} The wrapper function.
 * @see {@link BelongsTo#_cacheObjectsQuery}
 */
var invalidatesQuery = function(fn) {
  return function(instance) {
    this._cacheObjectsQuery(instance, undefined);
    return fn.apply(this, arguments);
  };
};

BelongsTo.reopen(/** @lends BelongsTo# */ {

  /**
   * The identity property for this relation.
   *
   * This property allows access to the underlying relation object, allowing
   * you to access details about how the relation was created/configured. This
   * method is currently only intended for internal use, but if you have a
   * reason for it to be documented publicly, please create an issue on GitHub
   * and let us know.
   *
   * It is accessible on an individual model via `<singular>Relation`. For
   * instance, an article that belongs to an author would cause this method to
   * get triggered via `article.authorRelation`.
   *
   * The naming conventions are set forth in {@link BelongsTo.methods}.
   *
   * @since 1.0
   * @protected
   * @method
   * @param {Model} instance The model instance on which to operate.
   * @see {@link BaseRelation#methods}
   */
  identity: function(/*instance*/) {
    return this;
  },

  /**
   * The item property for this relation.
   *
   * This property allows access to the cached object that has been fetched
   * for a specific model in a given relation. Before the cache has been
   * filled, accessing this property will throw an exception.
   *
   * It is accessible on an individual model via `<singular>` and via
   * `get<Singular>`. For instance, an article that belongs to an author would
   * cause this method to get triggered via `article.author` or
   * `article.getAuthor`.
   *
   * The naming conventions are set forth in {@link BelongsTo.methods}.
   *
   * @since 1.0
   * @protected
   * @method
   * @param {Model} instance The model instance on which to operate.
   * @see {@link BaseRelation#methods}
   */
  item: function(instance) {
    var result = instance[this._itemCacheKey];
    if (!result) {
      throw new Error(util.format('The relation "%s" has not yet been ' +
        'loaded.', this._name));
    }
    return result;
  },

  /**
   * Update the {@link BelongsTo#item} cache to contain a new value.
   *
   * @since 1.0
   * @protected
   * @method
   * @param {Model} instance The model instance on which to operate.
   * @param {Array} value The new value for the item cache.
   */
  _cacheItem: function(instance, value) {
    instance[this._itemCacheKey] = value[0];
  },

  /**
   * The item property setter for this relation.
   *
   * This property allows altering the associated object of a specific model in
   * a given relation.
   *
   * It is accessible on an individual model via assignment with `<singular>`
   * and via `set<Singular>`. For instance, an article that belongs to an
   * author would cause this method to get triggered via
   * `article.author = '...'` or `article.setAuthor`.
   *
   * The naming conventions are set forth in {@link BelongsTo.methods}.
   *
   * @since 1.0
   * @protected
   * @method
   * @param {Model} instance The model instance on which to operate.
   * @see {@link BaseRelation#methods}
   */
  set: function(instance, value) {
    instance[this._itemCacheKey] = value;
  },

  objectsQuery: function(instance) {
    var cacheName = this._itemObjectsQueryCacheKey;
    if (!instance[cacheName]) {
      var where = _.object([[this.primaryKey, instance.attrs[this.foreignKey]]]);
      var cacheResult = _.bind(this._cacheItem, this, instance);
      instance[cacheName] = this._relatedModel
        .objects.where(where)
        .on('result', cacheResult);
    }
    return instance[cacheName];
  },

  /**
   * Update the {@link BelongsTo#objectsQuery} cache to contain a new value.
   *
   * @since 1.0
   * @protected
   * @method
   * @param {Model} instance The model instance on which to operate.
   * @param {Object} value The new value.
   */
  _cacheObjectsQuery: function(instance, value) {
    instance[this._itemObjectsQueryCacheKey] = value;
  },

  /**
   * The create object method for this relation.
   *
   * This simply creates a new object that has the appropriate relation data
   * already set.
   *
   * This method invalidates the {@link BelongsTo#objectsQuery} cache and sets
   * the created object as the {@link BelongsTo#item}.
   *
   * It is accessible on an individual model via `create<Singular>`. For
   * instance, an article that belongs to an author would cause this method to
   * get triggered via `article.createAuthor`.
   *
   * The naming conventions are set forth in {@link BelongsTo.methods}.
   *
   * @since 1.0
   * @protected
   * @method
   * @param {Model} instance The model instance on which to operate.
   * @param {Array} args The arguments to the method.
   * @see {@link BaseRelation#methods}
   */
  create: invalidatesQuery(function(instance, args) {
    var relatedModel = this._relatedModel;
    var result = relatedModel.create.apply(relatedModel, args);
    // TODO: add to inverse here or in `set`?
    this.set(instance, result);
    return result;
  }),

  fetch: function(instance) {
    return this.objectsQuery(instance).execute()
    .then(function(result) {

      return result[0];
    });
  },

  store: function() {

  }

});

BelongsTo.reopenClass(/** @lends BelongsTo */ {
  methods: {
    '<singular>': BaseRelation.property('item', 'set'),
    '<singular>Relation': BaseRelation.property('identity'),
    'get<Singular>': BaseRelation.method('item'),
    'set<Singular>': BaseRelation.method('set'),
    'create<Singular>': BaseRelation.method('create'),
    'fetch<Singular>': BaseRelation.method('fetch'),
    'store<Singular>': BaseRelation.method('store'),
    '<foreignKey>': function(relation) {
      return attr(relation.foreignKey);
    },
  }
});

module.exports = BelongsTo.reopenClass({ __name__: 'BelongsTo' });
<|endoftext|>