<commit_before>'use strict';

var P2PTV = P2PTV || {

  VERSION: 'v0.4.1',

  CHANNEL: 'p2ptvchannel',
  ICE_SERVERS: [{url: 'stun:stun.l.google.com:19302'}],
  PC_CONSTRAINTS: {optional: [{DtlsSrtpKeyAgreement: true}]},
  DC_CONSTRAINTS: {reliable: false, id: this.CHANNEL},

  MAX_MESSAGE_SIZE: 16*1024,
  MAX_CHUNKS_PER_MEDIA_SEGMENT: 256, // FIXME

  INIT_SEGMENT: 0,
  MIN_INIT_SEGMENT_HEADER: 9,
  MAX_INIT_SEGMENT_PAYLOAD: this.MAX_MESSAGE_SIZE
    - this.MIN_INIT_SEGMENT_HEADER,

  MEDIA_SEGMENT_CHUNK: 1,
  MIN_MEDIA_SEGMENT_CHUNK_HEADER: 16,
  MAX_MEDIA_SEGMENT_CHUNK_PAYLOAD: this.MAX_MESSAGE_SIZE
    -this.MIN_MEDIA_SEGMENT_CHUNK_HEADER,

  MEDIA_SEGMENT: 2,
  MIN_MEDIA_SEGMENT_HEADER: 9,

  STREAMS: {},

  /**
   * This function is used for logging.
   * text - The text to log.
   */
  log: function(text) {
    if (text[text.length - 1] === '\n') {
      text = text.substring(0, text.length - 1);
    }
    if (window.performance) {
      var now = (window.performance.now() / 1000).toFixed(3);
      console.log(now + ': ' + text);
    } else {
      console.log(text);
    }
  },

  /**
   * Checks support for all APIs used by P2PTV. Returns true if the browser
   * supports all APIs, otherwise false is returned and results can be
   * parsed for more information.
   *
   * supports - An empty object to be populated with API support results.
   */
  isSupported: function(supports) {

    if (!this._initialized) {
      throw new Error('Must initialize P2PTV before checking for support');
    }

    supports.WebSocket= !!window.WebSocket;

    supports.Blob= !!window.Blob;
    supports.FileReader= !!window.FileReader;
    supports.ArrayBuffer= !!window.ArrayBuffer;
    supports.Float64Array= !!window.Float64Array;
    supports.Uint8Array= !!window.Uint8Array;
    supports.Int32Array= !!window.Int32Array;

    supports.RTCPeerConnection= !!this.RTCPeerConnection;
    supports.RTCSessionDescription= !!this.RTCSessionDescription;
    supports.RTCIceCandidate= !!this.RTCIceCandidate;

    supports.MediaSource= !!window.MediaSource;
    supports.WebM_VP8 = (!supports.MediaSource) ? false :
      MediaSource.isTypeSupported('video/webm;codecs="vp8,vorbis"');

    var supported = true;
    Object.keys(supports).forEach(function(key) {
      supported = supported && supports[key];
    });

    return supported;
  },

  /**
  * Generates a unique stream id.
  */
  generateStreamId: function() {
    var self = this;
    var charSet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
      +'abcdefghijklmnopqrstuvwxyz0123456789';
    var id = '';
    for (var i = 0; i < 12; i++) {
      var randomPoz = Math.floor(Math.random()*charSet.length);
      id += charSet.substring(randomPoz, randomPoz+1);
    }
    return (id in self.STREAMS) ? self.generateStreamId() : id;
  },

  /**
   * P2PTV uses a custom WebRTC adapter. We store the WebRTC references
   * internally to avoid breaking other WebRTC applications.
   */
  RTCPeerConnection: null,
  RTCSessionDescription: null,
  RTCIceCandidate: null,
  detectedBrowser: 'unknown',
  _initialized: false,

  /**
   * FIXME
   * A custom WebRTC adapter that works with spoofed user agents.
   * Supports bare minimum features for maximum browser coverage.
   *
   * See: https://github.com/webrtc/adapter
   */
  adapter: function() {
    this.log('P2PTV ' + this.VERSION);
    if (!this._initialized) {
      this._initialized = true;

      // check user agent string for browser
      if (navigator.userAgent.match(/Edge/)) {
        this.log('This appears to be Edge');
        this.detectedBrowser = 'edge';
      } else if (navigator.userAgent.match(/Firefox/)) {
        this.log('This appears to be Firefox');
        this.detectedBrowser = 'firefox';
      } else if (navigator.userAgent.match(/(OPR|Opera)/)) {
        this.log('This appears to be Opera');
        this.detectedBrowser = 'opera';
      } else if (navigator.userAgent.match(/Chrom(e|ium)/)) {
        this.log('This appears to be Chrome');
        this.detectedBrowser = 'chrome';
      } else {
        this.log('This browser may not be supported');
        this.detectedBrowser = 'unknown';
      }

      if (typeof window === 'undefined' || !window.navigator) {
        // definitely not supported
        this.log('This does not appear to be a browser');
        this.detectedBrowser = 'not supported';
      } else if (!!window.mozRTCPeerConnection) {
        // has moz prefix -- firefox
        this.log('has prefix: moz');
        this.detectedBrowser = 'firefox';
        this.RTCPeerConnection = function(pcConfig, pcConstraints) {
          if (pcConfig && pcConfig.iceServers) {
            var newIceServers = [];
            for (var i = 0; i < pcConfig.iceServers.length; i++) {
              var server = pcConfig.iceServers[i];
              if (server.hasOwnProperty('urls')) {
                for (var j = 0; j < server.urls.length; j++) {
                  var newServer = {url: server.urls[j]};
                  if (server.urls[j].indexOf('turn') === 0) {
                    newServer.username = server.username;
                    newServer.credential = server.credential;
                  }
                  newIceServers.push(newServer);
                }
              } else {
                newIceServers.push(pcConfig.iceServers[i]);
              }
            }
            pcConfig.iceServers = newIceServers;
          }
          return new mozRTCPeerConnection(pcConfig, pcConstraints);
        };
        this.RTCSessionDescription = window.RTCSessionDescription
          || window.mozRTCSessionDescription;
        this.RTCIceCandidate = window.RTCIceCandidate
          || window.mozRTCIceCandidate;

      } else if (!!window.webkitRTCPeerConnection) {
        // has webkit prefix -- chrome or opera
        this.log('has prefix: webkit');
        if (this.detectedBrowser !== 'opera') {
          this.detectedBrowser = 'chrome';
        }
        this.RTCPeerConnection = function(pcConfig, pcConstraints) {
          return new webkitRTCPeerConnection(pcConfig, pcConstraints);
        };
        ['createOffer', 'createAnswer'].forEach(function(method) {
          var nativeMethod = webkitRTCPeerConnection.prototype[method];
          webkitRTCPeerConnection.prototype[method] = function() {
            var self = this;
            if (arguments.length < 1 || (arguments.length === 1 &&
                typeof(arguments[0]) === 'object')) {
              var opts = arguments.length === 1 ? arguments[0] : undefined;
              return new Promise(function(resolve, reject) {
                nativeMethod.apply(self, [resolve, reject, opts]);
              });
            } else {
              return nativeMethod.apply(this, arguments);
            }
          };
        });
        ['setLocalDescription', 'setRemoteDescription',
            'addIceCandidate'].forEach(function(method) {
          var nativeMethod = webkitRTCPeerConnection.prototype[method];
          webkitRTCPeerConnection.prototype[method] = function() {
            var args = arguments;
            var self = this;
            return new Promise(function(resolve, reject) {
              nativeMethod.apply(self, [args[0],
                  function() {
                    resolve();
                    if (args.length >= 2) {
                      args[1].apply(null, []);
                    }
                  },
                  function(err) {
                    reject(err);
                    if (args.length >= 3) {
                      args[2].apply(null, [err]);
                    }
                  }]
                );
            });
          };
        });
        this.RTCSessionDescription = window.RTCSessionDescription
          || window.webkitRTCSessionDescription;
        this.RTCIceCandidate = window.RTCIceCandidate
          || window.webkitRTCIceCandidate;
      } else {
        this.log('Your browser doesn\'t appear to support WebRTC');
        this.detectedBrowser('not supported');
      }
    }

  },

  /* No operation. */
  NOP: function() {}

};
<commit_msg>version 0.4.2<commit_after>'use strict';

var P2PTV = P2PTV || {

  VERSION: 'v0.4.2',

  CHANNEL: 'p2ptvchannel',
  ICE_SERVERS: [{url: 'stun:stun.l.google.com:19302'}],
  PC_CONSTRAINTS: {optional: [{DtlsSrtpKeyAgreement: true}]},
  DC_CONSTRAINTS: {reliable: false, id: this.CHANNEL},

  MAX_MESSAGE_SIZE: 16*1024,
  MAX_CHUNKS_PER_MEDIA_SEGMENT: 256, // FIXME

  INIT_SEGMENT: 0,
  MIN_INIT_SEGMENT_HEADER: 9,
  MAX_INIT_SEGMENT_PAYLOAD: this.MAX_MESSAGE_SIZE
    - this.MIN_INIT_SEGMENT_HEADER,

  MEDIA_SEGMENT_CHUNK: 1,
  MIN_MEDIA_SEGMENT_CHUNK_HEADER: 16,
  MAX_MEDIA_SEGMENT_CHUNK_PAYLOAD: this.MAX_MESSAGE_SIZE
    -this.MIN_MEDIA_SEGMENT_CHUNK_HEADER,

  MEDIA_SEGMENT: 2,
  MIN_MEDIA_SEGMENT_HEADER: 9,

  STREAMS: {},

  /**
   * This function is used for logging.
   * text - The text to log.
   */
  log: function(text) {
    if (text[text.length - 1] === '\n') {
      text = text.substring(0, text.length - 1);
    }
    if (window.performance) {
      var now = (window.performance.now() / 1000).toFixed(3);
      console.log(now + ': ' + text);
    } else {
      console.log(text);
    }
  },

  /**
   * Checks support for all APIs used by P2PTV. Returns true if the browser
   * supports all APIs, otherwise false is returned and results can be
   * parsed for more information.
   *
   * supports - An empty object to be populated with API support results.
   */
  isSupported: function(supports) {

    if (!this._initialized) {
      throw new Error('Must initialize P2PTV before checking for support');
    }

    supports.WebSocket= !!window.WebSocket;

    supports.Blob= !!window.Blob;
    supports.FileReader= !!window.FileReader;
    supports.ArrayBuffer= !!window.ArrayBuffer;
    supports.Float64Array= !!window.Float64Array;
    supports.Uint8Array= !!window.Uint8Array;
    supports.Int32Array= !!window.Int32Array;

    supports.RTCPeerConnection= !!this.RTCPeerConnection;
    supports.RTCSessionDescription= !!this.RTCSessionDescription;
    supports.RTCIceCandidate= !!this.RTCIceCandidate;

    supports.MediaSource= !!window.MediaSource;
    supports.WebM_VP8 = (!supports.MediaSource) ? false :
      MediaSource.isTypeSupported('video/webm;codecs="vp8,vorbis"');

    var supported = true;
    Object.keys(supports).forEach(function(key) {
      supported = supported && supports[key];
    });

    return supported;
  },

  /**
  * Generates a unique stream id.
  */
  generateStreamId: function() {
    var self = this;
    var charSet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
      +'abcdefghijklmnopqrstuvwxyz0123456789';
    var id = '';
    for (var i = 0; i < 12; i++) {
      var randomPoz = Math.floor(Math.random()*charSet.length);
      id += charSet.substring(randomPoz, randomPoz+1);
    }
    return (id in self.STREAMS) ? self.generateStreamId() : id;
  },

  /**
   * P2PTV uses a custom WebRTC adapter. We store the WebRTC references
   * internally to avoid breaking other WebRTC applications.
   */
  RTCPeerConnection: null,
  RTCSessionDescription: null,
  RTCIceCandidate: null,
  detectedBrowser: 'unknown',
  _initialized: false,

  /**
   * FIXME
   * A custom WebRTC adapter that works with spoofed user agents.
   * Supports bare minimum features for maximum browser coverage.
   *
   * See: https://github.com/webrtc/adapter
   */
  adapter: function() {
    this.log('P2PTV ' + this.VERSION);
    if (!this._initialized) {
      this._initialized = true;

      // check user agent string for browser
      if (navigator.userAgent.match(/Edge/)) {
        this.log('This appears to be Edge');
        this.detectedBrowser = 'edge';
      } else if (navigator.userAgent.match(/Firefox/)) {
        this.log('This appears to be Firefox');
        this.detectedBrowser = 'firefox';
      } else if (navigator.userAgent.match(/(OPR|Opera)/)) {
        this.log('This appears to be Opera');
        this.detectedBrowser = 'opera';
      } else if (navigator.userAgent.match(/Chrom(e|ium)/)) {
        this.log('This appears to be Chrome');
        this.detectedBrowser = 'chrome';
      } else {
        this.log('This browser may not be supported');
        this.detectedBrowser = 'unknown';
      }

      if (typeof window === 'undefined' || !window.navigator) {
        // definitely not supported
        this.log('This does not appear to be a browser');
        this.detectedBrowser = 'not supported';
      } else if (!!window.mozRTCPeerConnection) {
        // has moz prefix -- firefox
        this.log('has prefix: moz');
        this.detectedBrowser = 'firefox';
        this.RTCPeerConnection = function(pcConfig, pcConstraints) {
          if (pcConfig && pcConfig.iceServers) {
            var newIceServers = [];
            for (var i = 0; i < pcConfig.iceServers.length; i++) {
              var server = pcConfig.iceServers[i];
              if (server.hasOwnProperty('urls')) {
                for (var j = 0; j < server.urls.length; j++) {
                  var newServer = {url: server.urls[j]};
                  if (server.urls[j].indexOf('turn') === 0) {
                    newServer.username = server.username;
                    newServer.credential = server.credential;
                  }
                  newIceServers.push(newServer);
                }
              } else {
                newIceServers.push(pcConfig.iceServers[i]);
              }
            }
            pcConfig.iceServers = newIceServers;
          }
          return new mozRTCPeerConnection(pcConfig, pcConstraints);
        };
        this.RTCSessionDescription = window.RTCSessionDescription
          || window.mozRTCSessionDescription;
        this.RTCIceCandidate = window.RTCIceCandidate
          || window.mozRTCIceCandidate;

      } else if (!!window.webkitRTCPeerConnection) {
        // has webkit prefix -- chrome or opera
        this.log('has prefix: webkit');
        if (this.detectedBrowser !== 'opera') {
          this.detectedBrowser = 'chrome';
        }
        this.RTCPeerConnection = function(pcConfig, pcConstraints) {
          return new webkitRTCPeerConnection(pcConfig, pcConstraints);
        };
        ['createOffer', 'createAnswer'].forEach(function(method) {
          var nativeMethod = webkitRTCPeerConnection.prototype[method];
          webkitRTCPeerConnection.prototype[method] = function() {
            var self = this;
            if (arguments.length < 1 || (arguments.length === 1 &&
                typeof(arguments[0]) === 'object')) {
              var opts = arguments.length === 1 ? arguments[0] : undefined;
              return new Promise(function(resolve, reject) {
                nativeMethod.apply(self, [resolve, reject, opts]);
              });
            } else {
              return nativeMethod.apply(this, arguments);
            }
          };
        });
        ['setLocalDescription', 'setRemoteDescription',
            'addIceCandidate'].forEach(function(method) {
          var nativeMethod = webkitRTCPeerConnection.prototype[method];
          webkitRTCPeerConnection.prototype[method] = function() {
            var args = arguments;
            var self = this;
            return new Promise(function(resolve, reject) {
              nativeMethod.apply(self, [args[0],
                  function() {
                    resolve();
                    if (args.length >= 2) {
                      args[1].apply(null, []);
                    }
                  },
                  function(err) {
                    reject(err);
                    if (args.length >= 3) {
                      args[2].apply(null, [err]);
                    }
                  }]
                );
            });
          };
        });
        this.RTCSessionDescription = window.RTCSessionDescription
          || window.webkitRTCSessionDescription;
        this.RTCIceCandidate = window.RTCIceCandidate
          || window.webkitRTCIceCandidate;
      } else {
        this.log('Your browser doesn\'t appear to support WebRTC');
        this.detectedBrowser('not supported');
      }
    }

  },

  /* No operation. */
  NOP: function() {}

};
<|endoftext|>


<commit_before>const url = require('url')
const {shell} = require('electron')
const _ = require('underscore-plus')

let selector = null

module.exports = {
  activate () {
    atom.commands.add('atom-workspace', 'link:open', () => this.openLink())
  },

  openLink () {
    const editor = atom.workspace.getActiveTextEditor()
    if (editor == null) return

    let link = this.linkUnderCursor(editor)
    if (link == null) return

    if (editor.getGrammar().scopeName === 'source.gfm') {
      link = this.linkForName(editor.getBuffer(), link)
    }

    const {protocol} = url.parse(link)
    if (protocol === 'http:' || protocol === 'https:') shell.openExternal(link)
  },

  // Get the link under the cursor in the editor
  //
  // Returns a {String} link or undefined if no link found.
  linkUnderCursor (editor) {
    const cursorPosition = editor.getCursorBufferPosition()
    const link = this.linkAtPosition(editor, cursorPosition)
    if (link != null) return link

    // Look for a link to the left of the cursor
    if (cursorPosition.column > 0) {
      return this.linkAtPosition(editor, cursorPosition.translate([0, -1]))
    }
  },

  // Get the link at the buffer position in the editor.
  //
  // Returns a {String} link or undefined if no link found.
  linkAtPosition (editor, bufferPosition) {
    if (selector == null) {
      const {ScopeSelector} = require('first-mate')
      selector = new ScopeSelector('markup.underline.link')
    }

    const token = editor.tokenForBufferPosition(bufferPosition)
    if (token && token.value && selector.matches(token.scopes)) return token.value
  },

  // Get the link for the given name.
  //
  // This is for Markdown links of the style:
  //
  // ```
  // [label][name]
  //
  // [name]: https://github.com
  // ```
  //
  // Returns a {String} link
  linkForName (buffer, linkName) {
    let link = linkName
    const regex = new RegExp(`^\\s*\\[${_.escapeRegExp(linkName)}\\]\\s*:\\s*(.+)$`, 'g')
    buffer.backwardsScanInRange(regex, buffer.getRange(), ({match, stop}) => {
      link = match[1]
      stop()
    })
    return link
  }
}
<commit_msg>Don't forget to dispose commands<commit_after>const url = require('url')
const {shell} = require('electron')
const _ = require('underscore-plus')

let selector = null

module.exports = {
  activate () {
    this.commandDisposable = atom.commands.add('atom-workspace', 'link:open', () => this.openLink())
  },

  deactivate () {
    this.commandDisposable.dispose()
  },

  openLink () {
    const editor = atom.workspace.getActiveTextEditor()
    if (editor == null) return

    let link = this.linkUnderCursor(editor)
    if (link == null) return

    if (editor.getGrammar().scopeName === 'source.gfm') {
      link = this.linkForName(editor.getBuffer(), link)
    }

    const {protocol} = url.parse(link)
    if (protocol === 'http:' || protocol === 'https:') shell.openExternal(link)
  },

  // Get the link under the cursor in the editor
  //
  // Returns a {String} link or undefined if no link found.
  linkUnderCursor (editor) {
    const cursorPosition = editor.getCursorBufferPosition()
    const link = this.linkAtPosition(editor, cursorPosition)
    if (link != null) return link

    // Look for a link to the left of the cursor
    if (cursorPosition.column > 0) {
      return this.linkAtPosition(editor, cursorPosition.translate([0, -1]))
    }
  },

  // Get the link at the buffer position in the editor.
  //
  // Returns a {String} link or undefined if no link found.
  linkAtPosition (editor, bufferPosition) {
    if (selector == null) {
      const {ScopeSelector} = require('first-mate')
      selector = new ScopeSelector('markup.underline.link')
    }

    const token = editor.tokenForBufferPosition(bufferPosition)
    if (token && token.value && selector.matches(token.scopes)) return token.value
  },

  // Get the link for the given name.
  //
  // This is for Markdown links of the style:
  //
  // ```
  // [label][name]
  //
  // [name]: https://github.com
  // ```
  //
  // Returns a {String} link
  linkForName (buffer, linkName) {
    let link = linkName
    const regex = new RegExp(`^\\s*\\[${_.escapeRegExp(linkName)}\\]\\s*:\\s*(.+)$`, 'g')
    buffer.backwardsScanInRange(regex, buffer.getRange(), ({match, stop}) => {
      link = match[1]
      stop()
    })
    return link
  }
}
<|endoftext|>


<commit_before>import 'babel-core/polyfill'
import React from 'react'
import { render } from 'react-dom'
import { configureStore } from '../store'
import createHistory from 'history/lib/createBrowserHistory'
import { Provider } from 'react-redux'
import { match, Router } from 'react-router'
import makeRoutes from '../routes'
import { syncReduxAndRouter } from 'redux-simple-router'
import { getPrefetchedData, getDeferredData } from 'react-fetcher'

const store = configureStore(window.INITIAL_STATE)
const routes = makeRoutes(store)
const history = createHistory()
syncReduxAndRouter(history, store)

var prevLocation = {}

history.listen(location => {
  match({routes, location}, (error, redirectLocation, renderProps) => {
    if (error) {
      console.error(error)
      return
    }

    // WEIRD: when the logout action is dispatched, it triggers
    // a history event even though the location didn't change.
    // i don't know why that happens, but we work around it here
    // by comparing the new location to the previous one.
    if (location.pathname === prevLocation.pathname) {
      console.log('suppressed a redundant history event')
      return
    }

    const components = renderProps.routes.map(r => r.component)
    const locals = {
      path: renderProps.location.pathname,
      query: renderProps.location.query,
      params: renderProps.params,
      dispatch: store.dispatch
    }
    getPrefetchedData(components, locals)
    .then(() => getDeferredData(components, locals))
    .then(() => prevLocation = location)
  })
})

const component = (
  <Provider store={store}>
    <Router routes={routes} history={history}/>
  </Provider>
)

render(component, document.getElementById('app'))
<commit_msg>don't prefetch on client for the first route<commit_after>import 'babel-core/polyfill'
import React from 'react'
import { render } from 'react-dom'
import { configureStore } from '../store'
import createHistory from 'history/lib/createBrowserHistory'
import { Provider } from 'react-redux'
import { match, Router } from 'react-router'
import makeRoutes from '../routes'
import { syncReduxAndRouter } from 'redux-simple-router'
import { getPrefetchedData, getDeferredData } from 'react-fetcher'

const store = configureStore(window.INITIAL_STATE)
const routes = makeRoutes(store)
const history = createHistory()
syncReduxAndRouter(history, store)

var prevLocation = null

history.listen(location => {
  match({routes, location}, (error, redirectLocation, renderProps) => {
    if (error) {
      console.error(error)
      return
    }

    // don't prefetch for the first route after page load,
    // because it's all been loaded on the server already
    if (!prevLocation) {
      prevLocation = location
      return
    }

    // WEIRD: when the logout action is dispatched, it triggers
    // a history event even though the location didn't change.
    // i don't know why that happens, but we work around it here
    // by comparing the new location to the previous one.
    if (location.pathname === prevLocation.pathname) {
      console.log('suppressed a redundant history event')
      return
    }

    const components = renderProps.routes.map(r => r.component)
    const locals = {
      path: renderProps.location.pathname,
      query: renderProps.location.query,
      params: renderProps.params,
      dispatch: store.dispatch
    }
    getPrefetchedData(components, locals)
    .then(() => getDeferredData(components, locals))
    .then(() => prevLocation = location)
  })
})

const component = (
  <Provider store={store}>
    <Router routes={routes} history={history}/>
  </Provider>
)

render(component, document.getElementById('app'))
<|endoftext|>


<commit_before>/*===========================
Features Support Detection
===========================*/
Framework7.prototype.support = (function () {
    var support = {
        touch: !!(('ontouchstart' in window && window.ontouchstart != null) || window.DocumentTouch && document instanceof DocumentTouch),
        passiveListener: (function () {
            var supportsPassive = false;
            try {
                var opts = Object.defineProperty({}, 'passive', {
                    get: function() {
                        supportsPassive = true;
                    }
                });
                window.addEventListener('testPassiveListener', null, opts);
            } catch (e) {}
            return supportsPassive;
        })()
    };

    // Export object
    return support;
})();
<commit_msg>Fix touch detection brought by #1421<commit_after>/*===========================
Features Support Detection
===========================*/
Framework7.prototype.support = (function () {
    var support = {
        touch: !!(('ontouchstart' in window) || window.DocumentTouch && document instanceof DocumentTouch),
        passiveListener: (function () {
            var supportsPassive = false;
            try {
                var opts = Object.defineProperty({}, 'passive', {
                    get: function() {
                        supportsPassive = true;
                    }
                });
                window.addEventListener('testPassiveListener', null, opts);
            } catch (e) {}
            return supportsPassive;
        })()
    };

    // Export object
    return support;
})();
<|endoftext|>


<commit_before>// link with no args: symlink the folder to the global location
// link with package arg: symlink the global to the local

var npm = require("./npm.js")
  , symlink = require("./utils/link.js")
  , fs = require("graceful-fs")
  , log = require("npmlog")
  , asyncMap = require("slide").asyncMap
  , chain = require("slide").chain
  , path = require("path")
  , rm = require("./utils/gently-rm.js")
  , build = require("./build.js")
  , npa = require("npm-package-arg")

module.exports = link

link.usage = "npm link (in package dir)"
           + "\nnpm link <pkg> (link global into local)"

link.completion = function (opts, cb) {
  var dir = npm.globalDir
  fs.readdir(dir, function (er, files) {
    cb(er, files.filter(function (f) {
      return !f.match(/^[\._-]/)
    }))
  })
}

function link (args, cb) {
  if (process.platform === "win32") {
    var semver = require("semver")
    if (!semver.satisfies(process.version, ">=0.7.9")) {
      var msg = "npm link not supported on windows prior to node 0.7.9"
        , e = new Error(msg)
      e.code = "ENOTSUP"
      e.errno = require("constants").ENOTSUP
      return cb(e)
    }
  }

  if (npm.config.get("global")) {
    return cb(new Error("link should never be --global.\n"
                       +"Please re-run this command with --local"))
  }

  if (args.length === 1 && args[0] === ".") args = []
  if (args.length) return linkInstall(args, cb)
  linkPkg(npm.prefix, cb)
}

function linkInstall (pkgs, cb) {
  asyncMap(pkgs, function (pkg, cb) {
    var t = path.resolve(npm.globalDir, "..")
      , pp = path.resolve(npm.globalDir, pkg)
      , rp = null
      , target = path.resolve(npm.dir, pkg)

    function n (er, data) {
      if (er) return cb(er, data)
      // install returns [ [folder, pkgId], ... ]
      // but we definitely installed just one thing.
      var d = data.filter(function (d) { return !d[3] })
      var what = npa(d[0][0])
      pp = d[0][1]
      pkg = what.name
      target = path.resolve(npm.dir, pkg)
      next()
    }

    // if it's a folder, a random not-installed thing, or not a scoped package,
    // then link or install it first
    if (pkg[0] !== "@" && (pkg.indexOf("/") !== -1 || pkg.indexOf("\\") !== -1)) {
      return fs.lstat(path.resolve(pkg), function (er, st) {
        if (er || !st.isDirectory()) {
          npm.commands.install(t, pkg, n)
        } else {
          rp = path.resolve(pkg)
          linkPkg(rp, n)
        }
      })
    }

    fs.lstat(pp, function (er, st) {
      if (er) {
        rp = pp
        return npm.commands.install(t, pkg, n)
      } else if (!st.isSymbolicLink()) {
        rp = pp
        next()
      } else {
        return fs.realpath(pp, function (er, real) {
          if (er) log.warn("invalid symbolic link", pkg)
          else rp = real
          next()
        })
      }
    })

    function next () {
      chain
        ( [ [npm.commands, "unbuild", [target]]
          , [function (cb) {
              log.verbose("link", "symlinking %s to %s",  pp, target)
              cb()
            }]
          , [symlink, pp, target]
          // do run lifecycle scripts - full build here.
          , rp && [build, [target]]
          , [ resultPrinter, pkg, pp, target, rp ] ]
        , cb )
    }
  }, cb)
}

function linkPkg (folder, cb_) {
  var me = folder || npm.prefix
    , readJson = require("read-package-json")

  log.verbose("linkPkg", folder)

  readJson(path.resolve(me, "package.json"), function (er, d) {
    function cb (er) {
      return cb_(er, [[d && d._id, target, null, null]])
    }
    if (er) return cb(er)
    if (!d.name) {
      er = new Error("Package must have a name field to be linked")
      return cb(er)
    }
    var target = path.resolve(npm.globalDir, d.name)
    rm(target, function (er) {
      if (er) return cb(er)
      symlink(me, target, function (er) {
        if (er) return cb(er)
        log.verbose("link", "build target", target)
        // also install missing dependencies.
        npm.commands.install(me, [], function (er) {
          if (er) return cb(er)
          // build the global stuff.  Don't run *any* scripts, because
          // install command already will have done that.
          build([target], true, build._noLC, true, function (er) {
            if (er) return cb(er)
            resultPrinter(path.basename(me), me, target, cb)
          })
        })
      })
    })
  })
}

function resultPrinter (pkg, src, dest, rp, cb) {
  if (typeof cb !== "function") cb = rp, rp = null
  var where = dest
  rp = (rp || "").trim()
  src = (src || "").trim()
  // XXX If --json is set, then look up the data from the package.json
  if (npm.config.get("parseable")) {
    return parseableOutput(dest, rp || src, cb)
  }
  if (rp === src) rp = null
  console.log(where + " -> " + src + (rp ? " -> " + rp: ""))
  cb()
}

function parseableOutput (dest, rp, cb) {
  // XXX this should match ls --parseable and install --parseable
  // look up the data from package.json, format it the same way.
  //
  // link is always effectively "long", since it doesn't help much to
  // *just* print the target folder.
  // However, we don't actually ever read the version number, so
  // the second field is always blank.
  console.log(dest + "::" + rp)
  cb()
}
<commit_msg>link: rm rm<commit_after>// link with no args: symlink the folder to the global location
// link with package arg: symlink the global to the local

var npm = require("./npm.js")
  , symlink = require("./utils/link.js")
  , fs = require("graceful-fs")
  , log = require("npmlog")
  , asyncMap = require("slide").asyncMap
  , chain = require("slide").chain
  , path = require("path")
  , build = require("./build.js")
  , npa = require("npm-package-arg")

module.exports = link

link.usage = "npm link (in package dir)"
           + "\nnpm link <pkg> (link global into local)"

link.completion = function (opts, cb) {
  var dir = npm.globalDir
  fs.readdir(dir, function (er, files) {
    cb(er, files.filter(function (f) {
      return !f.match(/^[\._-]/)
    }))
  })
}

function link (args, cb) {
  if (process.platform === "win32") {
    var semver = require("semver")
    if (!semver.satisfies(process.version, ">=0.7.9")) {
      var msg = "npm link not supported on windows prior to node 0.7.9"
        , e = new Error(msg)
      e.code = "ENOTSUP"
      e.errno = require("constants").ENOTSUP
      return cb(e)
    }
  }

  if (npm.config.get("global")) {
    return cb(new Error("link should never be --global.\n"
                       +"Please re-run this command with --local"))
  }

  if (args.length === 1 && args[0] === ".") args = []
  if (args.length) return linkInstall(args, cb)
  linkPkg(npm.prefix, cb)
}

function linkInstall (pkgs, cb) {
  asyncMap(pkgs, function (pkg, cb) {
    var t = path.resolve(npm.globalDir, "..")
      , pp = path.resolve(npm.globalDir, pkg)
      , rp = null
      , target = path.resolve(npm.dir, pkg)

    function n (er, data) {
      if (er) return cb(er, data)
      // install returns [ [folder, pkgId], ... ]
      // but we definitely installed just one thing.
      var d = data.filter(function (d) { return !d[3] })
      var what = npa(d[0][0])
      pp = d[0][1]
      pkg = what.name
      target = path.resolve(npm.dir, pkg)
      next()
    }

    // if it's a folder, a random not-installed thing, or not a scoped package,
    // then link or install it first
    if (pkg[0] !== "@" && (pkg.indexOf("/") !== -1 || pkg.indexOf("\\") !== -1)) {
      return fs.lstat(path.resolve(pkg), function (er, st) {
        if (er || !st.isDirectory()) {
          npm.commands.install(t, pkg, n)
        } else {
          rp = path.resolve(pkg)
          linkPkg(rp, n)
        }
      })
    }

    fs.lstat(pp, function (er, st) {
      if (er) {
        rp = pp
        return npm.commands.install(t, pkg, n)
      } else if (!st.isSymbolicLink()) {
        rp = pp
        next()
      } else {
        return fs.realpath(pp, function (er, real) {
          if (er) log.warn("invalid symbolic link", pkg)
          else rp = real
          next()
        })
      }
    })

    function next () {
      chain
        ( [ [npm.commands, "unbuild", [target]]
          , [function (cb) {
              log.verbose("link", "symlinking %s to %s",  pp, target)
              cb()
            }]
          , [symlink, pp, target]
          // do run lifecycle scripts - full build here.
          , rp && [build, [target]]
          , [ resultPrinter, pkg, pp, target, rp ] ]
        , cb )
    }
  }, cb)
}

function linkPkg (folder, cb_) {
  var me = folder || npm.prefix
    , readJson = require("read-package-json")

  log.verbose("linkPkg", folder)

  readJson(path.resolve(me, "package.json"), function (er, d) {
    function cb (er) {
      return cb_(er, [[d && d._id, target, null, null]])
    }
    if (er) return cb(er)
    if (!d.name) {
      er = new Error("Package must have a name field to be linked")
      return cb(er)
    }
    var target = path.resolve(npm.globalDir, d.name)
    symlink(me, target, function (er) {
      if (er) return cb(er)
      log.verbose("link", "build target", target)
      // also install missing dependencies.
      npm.commands.install(me, [], function (er) {
        if (er) return cb(er)
        // build the global stuff.  Don't run *any* scripts, because
        // install command already will have done that.
        build([target], true, build._noLC, true, function (er) {
          if (er) return cb(er)
          resultPrinter(path.basename(me), me, target, cb)
        })
      })
    })
  })
}

function resultPrinter (pkg, src, dest, rp, cb) {
  if (typeof cb !== "function") cb = rp, rp = null
  var where = dest
  rp = (rp || "").trim()
  src = (src || "").trim()
  // XXX If --json is set, then look up the data from the package.json
  if (npm.config.get("parseable")) {
    return parseableOutput(dest, rp || src, cb)
  }
  if (rp === src) rp = null
  console.log(where + " -> " + src + (rp ? " -> " + rp: ""))
  cb()
}

function parseableOutput (dest, rp, cb) {
  // XXX this should match ls --parseable and install --parseable
  // look up the data from package.json, format it the same way.
  //
  // link is always effectively "long", since it doesn't help much to
  // *just* print the target folder.
  // However, we don't actually ever read the version number, so
  // the second field is always blank.
  console.log(dest + "::" + rp)
  cb()
}
<|endoftext|>


<commit_before>angular.module('blocktrail.wallet')
    .factory('trackingService', function(CONFIG, $q, $analytics) {

        var EVENTS = {
            ACTIVATED: 'activated',
            SIGN_UP: "sign_up",
            LOGIN: "login",
            RECEIVE_CUSTOM_AMOUNT: "receive_custom_amount",
            BUYBTC: {
                REGION_OK: "region_ok",
                REGION_NOTOK: "region_notok",
                GLIDERA_SETUP_INIT: "glidera_setup_init",
                GLIDERA_SETUP_UPDATE: "glidera_setup_update",
                GLIDERA_SETUP_DONE: "glidera_setup_done",
                GLIDERA_OPEN: "glidera_open",
                GLIDERA_BUY: "glidera_buy",
                GLIDERA_BUY_CONFIRM: "glidera_buy_confirm",
                GLIDERA_BUY_ERR: "glidera_buy_error",
                GLIDERA_BUY_DONE: "glidera_buy_done"
            },
            SWEEP: {
                SWEEP_START: "sweep_start",
                SWEEP_NO_BALANCE: "sweep_no_balance",
                SWEEP_BALANCE: "sweep_balance",
                SWEEP_SUCCESS: "sweep_success",
                SWEEP_FAIL: "sweep_fail"
            }
        };

        var ANALYTICS_META = {};
        Object.keys(EVENTS.BUYBTC).forEach(function(eventKey) {
            var eventVal = EVENTS.BUYBTC[eventKey];
            ANALYTICS_META[eventVal] = { category: "BuyBTC" };
        });

        var getBrowserFingerprint = function() {
            var def = $q.defer();

            new Fingerprint2({excludeFlashFonts: true}).get(function (result, components) {
                if(!result) def.reject();
                def.resolve({
                    "hash": result,
                    "components": components
                });
            });

            return def.promise;
        };

        var trackEvent = function(event) {
            $analytics.eventTrack(event, ANALYTICS_META[event] || { category: 'Events' });

            if (event === EVENTS.LOGIN) {
                if (window.fbq) {
                    // (ab)use "Other" event for logins
                    fbq('track', 'Other');
                }
                if (window.qp) {
                    qp('track', 'Login');
                }
            } else if (event === EVENTS.ACTIVATED) {
                if (window.fbq) {
                    // (ab)use "Lead" event for activated
                    fbq('track', 'Lead');
                }
                if (window.qp) {
                    qp('track', 'Activated');
                }
            } else if (event === EVENTS.SIGN_UP) {
                if (CONFIG.FBTRACKING_ID) {
                    if (window.fbq) {
                        fbq('track', 'CompleteRegistration');
                    }
                }
                if (window.qp) {
                    qp('track', 'Registration');
                }

                if (CONFIG.GOOGLEADWORDS_ID && CONFIG.GOOGLEADWORDS_LABEL) {
                    var image = new Image(1,1);
                    image.src = 'https://www.googleadservices.com/pagead/conversion/'+ CONFIG.GOOGLEADWORDS_ID +'/?label=' + CONFIG.GOOGLEADWORDS_LABEL + '&guid=ON&script=0';
                    document.body.appendChild(image);
                }
            }

        };

        return {
            EVENTS: EVENTS,
            trackEvent: trackEvent,
            getBrowserFingerprint: getBrowserFingerprint
        }
    })
;
<commit_msg>remove quora detailed events<commit_after>angular.module('blocktrail.wallet')
    .factory('trackingService', function(CONFIG, $q, $analytics) {

        var EVENTS = {
            ACTIVATED: 'activated',
            SIGN_UP: "sign_up",
            LOGIN: "login",
            RECEIVE_CUSTOM_AMOUNT: "receive_custom_amount",
            BUYBTC: {
                REGION_OK: "region_ok",
                REGION_NOTOK: "region_notok",
                GLIDERA_SETUP_INIT: "glidera_setup_init",
                GLIDERA_SETUP_UPDATE: "glidera_setup_update",
                GLIDERA_SETUP_DONE: "glidera_setup_done",
                GLIDERA_OPEN: "glidera_open",
                GLIDERA_BUY: "glidera_buy",
                GLIDERA_BUY_CONFIRM: "glidera_buy_confirm",
                GLIDERA_BUY_ERR: "glidera_buy_error",
                GLIDERA_BUY_DONE: "glidera_buy_done"
            },
            SWEEP: {
                SWEEP_START: "sweep_start",
                SWEEP_NO_BALANCE: "sweep_no_balance",
                SWEEP_BALANCE: "sweep_balance",
                SWEEP_SUCCESS: "sweep_success",
                SWEEP_FAIL: "sweep_fail"
            }
        };

        var ANALYTICS_META = {};
        Object.keys(EVENTS.BUYBTC).forEach(function(eventKey) {
            var eventVal = EVENTS.BUYBTC[eventKey];
            ANALYTICS_META[eventVal] = { category: "BuyBTC" };
        });

        var getBrowserFingerprint = function() {
            var def = $q.defer();

            new Fingerprint2({excludeFlashFonts: true}).get(function (result, components) {
                if(!result) def.reject();
                def.resolve({
                    "hash": result,
                    "components": components
                });
            });

            return def.promise;
        };

        var trackEvent = function(event) {
            $analytics.eventTrack(event, ANALYTICS_META[event] || { category: 'Events' });

            if (event === EVENTS.LOGIN) {
                if (window.fbq) {
                    // (ab)use "Other" event for logins
                    fbq('track', 'Other');
                }
            } else if (event === EVENTS.ACTIVATED) {
                if (window.fbq) {
                    // (ab)use "Lead" event for activated
                    fbq('track', 'Lead');
                }
            } else if (event === EVENTS.SIGN_UP) {
                if (CONFIG.FBTRACKING_ID) {
                    if (window.fbq) {
                        fbq('track', 'CompleteRegistration');
                    }
                }

                if (CONFIG.GOOGLEADWORDS_ID && CONFIG.GOOGLEADWORDS_LABEL) {
                    var image = new Image(1,1);
                    image.src = 'https://www.googleadservices.com/pagead/conversion/'+ CONFIG.GOOGLEADWORDS_ID +'/?label=' + CONFIG.GOOGLEADWORDS_LABEL + '&guid=ON&script=0';
                    document.body.appendChild(image);
                }
            }

        };

        return {
            EVENTS: EVENTS,
            trackEvent: trackEvent,
            getBrowserFingerprint: getBrowserFingerprint
        }
    })
;
<|endoftext|>


<commit_before>'use strict';

angular.module('Teem')
  .factory('ProjectsSvc', [
  'swellRT', '$q', '$timeout', 'base64', 'SessionSvc', 'SwellRTCommon', 'User', '$rootScope',
  function(swellRT, $q, $timeout, base64, SessionSvc, SwellRTCommon, User, $rootScope){

    // class that expose only read methods of the project object
    class ProjectReadOnly {
      constructor (val) {
        if (val) {
          for (var k in val.root){
            if (val.root.hasOwnProperty(k)){
              this[k] = val.root[k];
            }
          }
          this._participants = val.participants;
        }
      }

      get urlId () {
        if (! this._urlId) {
          this._urlId = base64.urlencode(this.id);
        }

        return this._urlId;
      }

      getTimestampAccess () {
        var access;

        if (this.lastAccesses === undefined) {
          this.lastAccesses = [];
        }

        angular.forEach(this.lastAccesses, function(a) {
          if (a.user === User.currentId()) {
            access = a;
          }
        });

        return access;
      }

      // section in {'chat', 'pad', 'needs'}
      lastChange (section) {
        if (section === undefined) {
          //cast to Date
          return new Date(Math.max(
            this.lastChange('chat').getTime(),
            this.lastChange('pad').getTime(),
            this.lastChange('needs').getTime()
          ));
        } else {
          switch (section) {

          case 'chat':
            if (this.chat && this.chat.length > 0) {
              return new Date(this.chat[this.chat.length -1].time);
            } else {
              return new Date(0);
            }
            break;

          case 'pad':
            return new Date(this.pad.lastmodtime) || new Date(0);

          case 'needs':
            var lastChange = new Date(0);
            angular.forEach(this.needs, function(n){
              lastChange = Math.max(
                lastChange,
                new Date(n.time||0),
                (function() {
                  var lastComment = new Date(0);
                  angular.forEach(n.comments, function(c){
                    lastComment = Math.max(lastComment, new Date(c.time || 0));
                  });
                  return lastComment;
                }())
              );
            });
            // cast to Date
            return new Date(lastChange);

          default:
            return new Date(0);
          }
        }
      }

      // section in {'chat', 'pad', 'needs'}
      // pos in {'last','prev'}
      lastAccess (section, pos) {
        var access;

        angular.forEach(this.lastAccesses || [], function(a) {
          if (a.user === User.currentId()) {
            access = a;
          }
        });

        if (!pos) {
          pos = 'last';
        }

        return (access && access[section] && access[section][pos] ? new Date(access[section][pos]) : new Date(0));
      }

      newMessagesCount () {
        var access = this.lastAccess('chat');

        if (this.chat.length > 0){
          var i = this.chat.length - 1;

          while (i > -1 && (new Date(this.chat[i].time) > access)) {
            i --;
          }
          return this.chat.length - 1 - i;
        } else {
          return 0;
        }
      }

      padEditionCount () {
        if (this.lastAccess('pad').getTime() < this.pad.lastmodtime) {
          return 1;
        } else {
          return 0;
        }
      }

      isSupporter (userId = User.currentId()) {
        if (!userId) {
          return false;
        }

        return this.supporters.indexOf(userId) > -1;
      }

      isContributor (userId = User.currentId()) {
        if (! userId) {
          return false;
        }
        return this._participants.indexOf(userId) > -1;
      }

      needCompletedCount () {
        var completed = 0;

        angular.forEach(this.needs, function(need) {
          if (need.completed === 'true') {
            completed++;
          }
        });

        return completed;
      }

      needIncompletedCount () {
        return this.needCount() - this.needCompletedCount();
      }

      needCount () {
        if (this.needs === undefined) {
          return 0;
        }

        return this.needs.length;
      }

      progressPercentage () {
        var size = this.needCount();

        if (size === 0) {
          return 0;
        }

        return Math.round(this.needCompletedCount() * 100 / size);
      }

      // Show at least 1%
      progressPercentageNotZero () {
        var value = this.progressPercentage();

        if (value === 0 && this.needCount() > 0) {
          return 1;
        }

        return value;
      }

      progressType () {
        var percentage = this.progressPercentage();

        if (percentage < 33) {
          return 'danger';
        } else if (percentage > 66) {
          return 'success';
        } else {
          return 'warning';
        }
      }

      isShareMode (mode) {
        return mode === this.shareMode;
      }

      participantCount () {
        return this._participants.reduce(function(a,b){
          // do not count participants of the form @domain that represents that it is a public wave.
          return a + (/.+@.+/.test(b)? 1 : 0);
        }, 0);
      }
    }

    class Project extends ProjectReadOnly {

      addContributor (userId = User.currentId()) {

        if (userId && this._participants.indexOf(userId) < 0){
          this._participants.push(userId);

          if (userId === User.currentId()){
            $rootScope.$broadcast('teem.project.join');
          }
        }

        if (userId === User.currentId()){
          $rootScope.$broadcast('teem.project.join');
        }
      }

      setShareMode (shareMode) {
        this.shareMode = shareMode;
      }

      /*
       * Record when the user had her last and previous access to one project section
       */
      setTimestampAccess (section, overridePrev) {
        if (! this.isContributor()) {
          return;
        }

        if (this.getTimestampAccess() === undefined){
          this.lastAccesses.push({user: User.currentId()});
        }
        var timestamp = this.getTimestampAccess()[section];

        if (timestamp === undefined) {
          timestamp = {};
        }

        if (timestamp instanceof Date) {
          timestamp = {
            last: timestamp
          };
        }

        if(! overridePrev){
          timestamp.prev = timestamp.last || (new Date()).toJSON();
        } else {
          timestamp.prev = (new Date()).toJSON();
        }

        timestamp.last = (new Date()).toJSON();

        this.getTimestampAccess()[section] = timestamp;
      }

      toggleSupport () {

        var userId = User.currentId();

        if (userId === undefined) {
          return;
        }
        var index = this.supporters.indexOf(userId);

        if (index > -1) {
          this.supporters.splice(index, 1);
        } else {
          this.supporters.push(userId);
        }
      }

      removeContributor (userId = User.currentId()) {

        this._participants.splice(
          this._participants.indexOf(userId),
          1);

        if (userId === User.currentId()){
          $rootScope.$broadcast('teem.project.leave');
        }
      }

      toggleContributor () {
        if (! SessionSvc.users.loggedIn()) {
          return;
        }

        var userId = User.currentId();

        if (this.isContributor(userId)) {
          this.removeContributor(userId);
        } else {
          this.addContributor(userId);
        }
      }

      addChatMessage (text, file) {
        if (file) {
          file = new swellRT.FileObject(file);
        }
        this.chat.push({
          text,
          file,
          who: User.currentId(),
          time: (new Date()).toJSON()
        });
        this.addContributor();
        this.setTimestampAccess('chat', true);
      }

      addNeed(need) {
        if (! need.text) {
          return;
        }

        need.author = SessionSvc.users.current();
        need.time = (new Date()).toJSON();
        need.completed = 'false';

        this.needs.push(need);
        this.setTimestampAccess('needs', true);

        return need;
      }

      removeNeed (need) {
        var i = this.needs.indexOf(need);

        this.needs.splice(i,1);
      }

      addNeedComment (need, comment) {
        if (!need.comments){
          need.comments = [];
        }
        need.comments.push({
          text: comment,
          time: (new Date()).toJSON(),
          author: User.currentId()
        });
        this.setTimestampAccess('needs', true);
      }

      delete () {
        this.type = 'deleted';
        this.communities = [];
      }
    }

    // Service functions //

    var openedProjects = {};

    /*
     * Build options for all query
     */
    function buildAllQuery(options) {
      var query = {
        _aggregate: [
          {
            $match: {
              'root.type': 'project'
            }
          }
        ]
      };

      if (options.contributor) {
        query._aggregate[0].$match.participants = options.contributor;
      }

      if (options.community) {
        query._aggregate[0].$match['root.communities'] = options.community;
      }

      if (options.localId) {
        query._aggregate[0].$match['root.localId'] = options.localId;
      }

      if (options.shareMode) {
        query._aggregate[0].$match['root.shareMode'] = 'public';
      }

      if (options.publicAndContributor) {
        query._aggregate[0].$match.$or = [
          { 'participants': options.publicAndContributor },
          { 'root.shareMode': 'public' }
        ];
      }

      return query;
    }

    /*
     * Find all the projects that meet some condition
     */
    function all(options) {
      var query = buildAllQuery(options);

      return $q(function(resolve, reject) {
        var res = [];

        SwellRT.query(query, function(result) {
          angular.forEach(result.result, function(val){

            var v = new ProjectReadOnly(val);

            res.push(v);
          });

          resolve(res);
        },
        function(error){
          console.log(error);

          reject([]);
        });
      });
    }

    function contributing () {

      if (!SessionSvc.users.loggedIn()) {
        return $q(function(resolve) {
          resolve([]);
        });
      }

      return all({ contributor: User.currentId()});
    }

    function find(id) {
      var def = $q.defer();

      if (!openedProjects[id]) {
        openedProjects[id] = def.promise;
        SwellRT.openModel(id, function(model){
          $timeout(function(){
            var pr = swellRT.proxy(model, Project);
            def.resolve(pr);
          });
        }, function(error){
          console.log(error);
          def.reject(error);
        });
      }
      return openedProjects[id];
    }

    function findByUrlId(urlId) {
      return find(base64.urldecode(urlId));
    }

    function create(options, callback) {
      var d = $q.defer();
      var id = SwellRT.createModel(function(model){
        openedProjects[id] = d.promise;

        SwellRTCommon.makeModelPublic(model);

        var proxyProj;

        $timeout(function(){
          proxyProj = swellRT.proxy(model, Project);
        });

        $timeout(function(){
          proxyProj.type = 'project';
          proxyProj.communities =
           (options.communityId) ? [ options.communityId ] : [];
          proxyProj.id = id;
          proxyProj.title = '';
          proxyProj.chat = [];
          proxyProj.pad = new swellRT.TextObject();
          proxyProj.needs = [];
          proxyProj.lastAccesses = [];
          proxyProj.promoter = User.currentId();
          proxyProj.supporters = [];
          proxyProj.shareMode = 'public';
          d.resolve(proxyProj);
        });
      });

      d.promise.then(callback);

      return d.promise;
    }

    return {
      all,
      contributing,
      findByUrlId,
      find,
      create
    };
  }]);
<commit_msg>Do not call addContributor when sending chat message<commit_after>'use strict';

angular.module('Teem')
  .factory('ProjectsSvc', [
  'swellRT', '$q', '$timeout', 'base64', 'SessionSvc', 'SwellRTCommon', 'User', '$rootScope',
  function(swellRT, $q, $timeout, base64, SessionSvc, SwellRTCommon, User, $rootScope){

    // class that expose only read methods of the project object
    class ProjectReadOnly {
      constructor (val) {
        if (val) {
          for (var k in val.root){
            if (val.root.hasOwnProperty(k)){
              this[k] = val.root[k];
            }
          }
          this._participants = val.participants;
        }
      }

      get urlId () {
        if (! this._urlId) {
          this._urlId = base64.urlencode(this.id);
        }

        return this._urlId;
      }

      getTimestampAccess () {
        var access;

        if (this.lastAccesses === undefined) {
          this.lastAccesses = [];
        }

        angular.forEach(this.lastAccesses, function(a) {
          if (a.user === User.currentId()) {
            access = a;
          }
        });

        return access;
      }

      // section in {'chat', 'pad', 'needs'}
      lastChange (section) {
        if (section === undefined) {
          //cast to Date
          return new Date(Math.max(
            this.lastChange('chat').getTime(),
            this.lastChange('pad').getTime(),
            this.lastChange('needs').getTime()
          ));
        } else {
          switch (section) {

          case 'chat':
            if (this.chat && this.chat.length > 0) {
              return new Date(this.chat[this.chat.length -1].time);
            } else {
              return new Date(0);
            }
            break;

          case 'pad':
            return new Date(this.pad.lastmodtime) || new Date(0);

          case 'needs':
            var lastChange = new Date(0);
            angular.forEach(this.needs, function(n){
              lastChange = Math.max(
                lastChange,
                new Date(n.time||0),
                (function() {
                  var lastComment = new Date(0);
                  angular.forEach(n.comments, function(c){
                    lastComment = Math.max(lastComment, new Date(c.time || 0));
                  });
                  return lastComment;
                }())
              );
            });
            // cast to Date
            return new Date(lastChange);

          default:
            return new Date(0);
          }
        }
      }

      // section in {'chat', 'pad', 'needs'}
      // pos in {'last','prev'}
      lastAccess (section, pos) {
        var access;

        angular.forEach(this.lastAccesses || [], function(a) {
          if (a.user === User.currentId()) {
            access = a;
          }
        });

        if (!pos) {
          pos = 'last';
        }

        return (access && access[section] && access[section][pos] ? new Date(access[section][pos]) : new Date(0));
      }

      newMessagesCount () {
        var access = this.lastAccess('chat');

        if (this.chat.length > 0){
          var i = this.chat.length - 1;

          while (i > -1 && (new Date(this.chat[i].time) > access)) {
            i --;
          }
          return this.chat.length - 1 - i;
        } else {
          return 0;
        }
      }

      padEditionCount () {
        if (this.lastAccess('pad').getTime() < this.pad.lastmodtime) {
          return 1;
        } else {
          return 0;
        }
      }

      isSupporter (userId = User.currentId()) {
        if (!userId) {
          return false;
        }

        return this.supporters.indexOf(userId) > -1;
      }

      isContributor (userId = User.currentId()) {
        if (! userId) {
          return false;
        }
        return this._participants.indexOf(userId) > -1;
      }

      needCompletedCount () {
        var completed = 0;

        angular.forEach(this.needs, function(need) {
          if (need.completed === 'true') {
            completed++;
          }
        });

        return completed;
      }

      needIncompletedCount () {
        return this.needCount() - this.needCompletedCount();
      }

      needCount () {
        if (this.needs === undefined) {
          return 0;
        }

        return this.needs.length;
      }

      progressPercentage () {
        var size = this.needCount();

        if (size === 0) {
          return 0;
        }

        return Math.round(this.needCompletedCount() * 100 / size);
      }

      // Show at least 1%
      progressPercentageNotZero () {
        var value = this.progressPercentage();

        if (value === 0 && this.needCount() > 0) {
          return 1;
        }

        return value;
      }

      progressType () {
        var percentage = this.progressPercentage();

        if (percentage < 33) {
          return 'danger';
        } else if (percentage > 66) {
          return 'success';
        } else {
          return 'warning';
        }
      }

      isShareMode (mode) {
        return mode === this.shareMode;
      }

      participantCount () {
        return this._participants.reduce(function(a,b){
          // do not count participants of the form @domain that represents that it is a public wave.
          return a + (/.+@.+/.test(b)? 1 : 0);
        }, 0);
      }
    }

    class Project extends ProjectReadOnly {

      addContributor (userId = User.currentId()) {

        if (userId && this._participants.indexOf(userId) < 0){
          this._participants.push(userId);

          if (userId === User.currentId()){
            $rootScope.$broadcast('teem.project.join');
          }
        }

        if (userId === User.currentId()){
          $rootScope.$broadcast('teem.project.join');
        }
      }

      setShareMode (shareMode) {
        this.shareMode = shareMode;
      }

      /*
       * Record when the user had her last and previous access to one project section
       */
      setTimestampAccess (section, overridePrev) {
        if (! this.isContributor()) {
          return;
        }

        if (this.getTimestampAccess() === undefined){
          this.lastAccesses.push({user: User.currentId()});
        }
        var timestamp = this.getTimestampAccess()[section];

        if (timestamp === undefined) {
          timestamp = {};
        }

        if (timestamp instanceof Date) {
          timestamp = {
            last: timestamp
          };
        }

        if(! overridePrev){
          timestamp.prev = timestamp.last || (new Date()).toJSON();
        } else {
          timestamp.prev = (new Date()).toJSON();
        }

        timestamp.last = (new Date()).toJSON();

        this.getTimestampAccess()[section] = timestamp;
      }

      toggleSupport () {

        var userId = User.currentId();

        if (userId === undefined) {
          return;
        }
        var index = this.supporters.indexOf(userId);

        if (index > -1) {
          this.supporters.splice(index, 1);
        } else {
          this.supporters.push(userId);
        }
      }

      removeContributor (userId = User.currentId()) {

        this._participants.splice(
          this._participants.indexOf(userId),
          1);

        if (userId === User.currentId()){
          $rootScope.$broadcast('teem.project.leave');
        }
      }

      toggleContributor () {
        if (! SessionSvc.users.loggedIn()) {
          return;
        }

        var userId = User.currentId();

        if (this.isContributor(userId)) {
          this.removeContributor(userId);
        } else {
          this.addContributor(userId);
        }
      }

      addChatMessage (text, file) {
        if (file) {
          file = new swellRT.FileObject(file);
        }
        this.chat.push({
          text,
          file,
          who: User.currentId(),
          time: (new Date()).toJSON()
        });

        this.setTimestampAccess('chat', true);
      }

      addNeed(need) {
        if (! need.text) {
          return;
        }

        need.author = SessionSvc.users.current();
        need.time = (new Date()).toJSON();
        need.completed = 'false';

        this.needs.push(need);
        this.setTimestampAccess('needs', true);

        return need;
      }

      removeNeed (need) {
        var i = this.needs.indexOf(need);

        this.needs.splice(i,1);
      }

      addNeedComment (need, comment) {
        if (!need.comments){
          need.comments = [];
        }
        need.comments.push({
          text: comment,
          time: (new Date()).toJSON(),
          author: User.currentId()
        });
        this.setTimestampAccess('needs', true);
      }

      delete () {
        this.type = 'deleted';
        this.communities = [];
      }
    }

    // Service functions //

    var openedProjects = {};

    /*
     * Build options for all query
     */
    function buildAllQuery(options) {
      var query = {
        _aggregate: [
          {
            $match: {
              'root.type': 'project'
            }
          }
        ]
      };

      if (options.contributor) {
        query._aggregate[0].$match.participants = options.contributor;
      }

      if (options.community) {
        query._aggregate[0].$match['root.communities'] = options.community;
      }

      if (options.localId) {
        query._aggregate[0].$match['root.localId'] = options.localId;
      }

      if (options.shareMode) {
        query._aggregate[0].$match['root.shareMode'] = 'public';
      }

      if (options.publicAndContributor) {
        query._aggregate[0].$match.$or = [
          { 'participants': options.publicAndContributor },
          { 'root.shareMode': 'public' }
        ];
      }

      return query;
    }

    /*
     * Find all the projects that meet some condition
     */
    function all(options) {
      var query = buildAllQuery(options);

      return $q(function(resolve, reject) {
        var res = [];

        SwellRT.query(query, function(result) {
          angular.forEach(result.result, function(val){

            var v = new ProjectReadOnly(val);

            res.push(v);
          });

          resolve(res);
        },
        function(error){
          console.log(error);

          reject([]);
        });
      });
    }

    function contributing () {

      if (!SessionSvc.users.loggedIn()) {
        return $q(function(resolve) {
          resolve([]);
        });
      }

      return all({ contributor: User.currentId()});
    }

    function find(id) {
      var def = $q.defer();

      if (!openedProjects[id]) {
        openedProjects[id] = def.promise;
        SwellRT.openModel(id, function(model){
          $timeout(function(){
            var pr = swellRT.proxy(model, Project);
            def.resolve(pr);
          });
        }, function(error){
          console.log(error);
          def.reject(error);
        });
      }
      return openedProjects[id];
    }

    function findByUrlId(urlId) {
      return find(base64.urldecode(urlId));
    }

    function create(options, callback) {
      var d = $q.defer();
      var id = SwellRT.createModel(function(model){
        openedProjects[id] = d.promise;

        SwellRTCommon.makeModelPublic(model);

        var proxyProj;

        $timeout(function(){
          proxyProj = swellRT.proxy(model, Project);
        });

        $timeout(function(){
          proxyProj.type = 'project';
          proxyProj.communities =
           (options.communityId) ? [ options.communityId ] : [];
          proxyProj.id = id;
          proxyProj.title = '';
          proxyProj.chat = [];
          proxyProj.pad = new swellRT.TextObject();
          proxyProj.needs = [];
          proxyProj.lastAccesses = [];
          proxyProj.promoter = User.currentId();
          proxyProj.supporters = [];
          proxyProj.shareMode = 'public';
          d.resolve(proxyProj);
        });
      });

      d.promise.then(callback);

      return d.promise;
    }

    return {
      all,
      contributing,
      findByUrlId,
      find,
      create
    };
  }]);
<|endoftext|>


<commit_before>'use strict'

const Writable = require('stream').Writable;
const parse = require('./parse');

/*
scope.get = (key, emitter, cb) => {

    let item = scope.cache.get(key);
    let newItem;
    if (!item) {
        newItem = true;
        item = new EventEmitter();
        scope.cache.set(key, item);
        item.on('error', console.error.bind(console));
    }

    if (item.ready) {
        process.nextTick(cb.bind(emitter, item));
    } else {
        item.once('ready', cb.bind(emitter));
    }

    return !!newItem;
};
*/

module.exports = (scope, node) => {

    let loader = new Writable({
        objectMode: true,
        write: (triple, enc, done) => {
            done(
                parse(
                    loader,
                    triple.subject || triple[0],
                    triple.predicate || triple[1],
                    triple.object || triple[2]
                )
            );
        }
    });

    loader.scope = scope;
    loader.check = {};
    loader.on('finish', () => {
        console.log('LOADER FINISH');
        //process.exit(1);
        //node.link();
    });

    loader.on('done', (what) => {
        switch (what) {
            case 'read':
                loader.check.read = true;
            break;
        }
    });

    return loader;
};
<commit_msg>log loader errors<commit_after>'use strict'

const Writable = require('stream').Writable;
const parse = require('./parse');

/*
scope.get = (key, emitter, cb) => {

    let item = scope.cache.get(key);
    let newItem;
    if (!item) {
        newItem = true;
        item = new EventEmitter();
        scope.cache.set(key, item);
        item.on('error', console.error.bind(console));
    }

    if (item.ready) {
        process.nextTick(cb.bind(emitter, item));
    } else {
        item.once('ready', cb.bind(emitter));
    }

    return !!newItem;
};
*/

module.exports = (scope, node, args) => {

    let loader = new Writable({
        objectMode: true,
        write: (triple, enc, done) => {
            done(
                parse(
                    loader,
                    triple.subject || triple[0],
                    triple.predicate || triple[1],
                    triple.object || triple[2]
                )
            );
        }
    });

    loader.args = args;
    loader.scope = scope;
    loader.check = {};
    loader.on('finish', () => {
        console.log('LOADER FINISH');
        //process.exit(1);
        //node.link();
    });

    loader.on('error', (err) => {
        console.log(err);
    });

    loader.on('done', (what) => {
        switch (what) {
            case 'read':
                loader.check.read = true;
            break;
        }
    });

    return loader;
};
<|endoftext|>


<commit_before>'use babel';

import {CompositeDisposable} from 'atom';
import {run} from './worker';

const commands = {'speck-runner:run': runInContext };

function runInContext() {
  const editor = atom.workspace.getActivePaneItem();
  const path = editor.getPath();
  const text = editor.getText();
  run(text, path);
}

function subscriptions() {
  const subscriptions = new CompositeDisposable;
  subscriptions.add(atom.commands.add('atom-workspace', commands));
  return subscriptions;
}

class SpeckRunner {
  constructor() {
    this.subscriptions = null;
  }
  activate(state) {
    this.subscriptions = subscriptions();
  }
  deactivate() {
    this.subscriptions.dispose();
  }
}

export default new SpeckRunner();
<commit_msg>run speck as lint service<commit_after>'use babel';

import {CompositeDisposable} from 'atom';
import {run} from './worker';

const commands = {'speck-runner:run': runInContext };

function runInContext() {
  const editor = atom.workspace.getActivePaneItem();
  const path = editor.getPath();
  const text = editor.getText();
  run(text, path);
}

function lint(editor) {
  const path = editor.getPath();
  const text = editor.getText();
  return new Promise(resolve => {
    try {
      run(text, path);
    } catch(e) {
      console.log(e);
    }
    resolve([]);
  });
}

function subscriptions() {
  const subscriptions = new CompositeDisposable;
  subscriptions.add(atom.commands.add('atom-workspace', commands));
  return subscriptions;
}

class SpeckRunner {
  constructor() {
    this.subscriptions = null;
  }
  activate(state) {
    this.subscriptions = subscriptions();
  }
  deactivate() {
    this.subscriptions.dispose();
  }
  provideLinter() {
    return {
      name: 'Speck Runner',
      grammarScopes: ['source.js'],
      scope: 'file',
      lintOnFly: false,
      lint
    };
  }
}

export default new SpeckRunner();
<|endoftext|>


<commit_before>// Load in dependencies
var EventEmitter = require('events').EventEmitter;
var inherits = require('inherits');
var Keyboard = require('./keyboard');
var Mouse = require('./mouse');

// Define selector constants
var SELECTORS = {
  info: {
    containerId: 'playerSongInfo',
    titleId: 'player-song-title'
  },
  forward: {
    buttonSelector: '#player sj-icon-button[data-id="forward"]'
  },
  playPause: {
    buttonSelector: '#player sj-icon-button[data-id="play-pause"]',
    dataId: 'play-pause',
    playingClass: 'playing'
  },
  rating: {
    // DEV: `.player-rating-container` doesn't exist until a song is playing
    containerSelector: '#playerSongInfo',
    thumbsSelector: '#player .player-rating-container [icon^="sj:thumb-"][data-rating]',
    thumbsUpSelector: '#player .player-rating-container [icon^="sj:thumb-"][data-rating="5"]',
    thumbsDownSelector: '#player .player-rating-container [icon^="sj:thumb-"][data-rating="1"]',
    thumbSelectorFormat: '#player .player-rating-container [icon^="sj:thumb-"][data-rating="{rating}"]'
  },
  repeat: {
    dataId: 'repeat',
    buttonSelector: '#player sj-icon-button[data-id="repeat"]'
  },
  rewind: {
    buttonSelector: '#player sj-icon-button[data-id="rewind"]'
  },
  shuffle: {
    dataId: 'shuffle',
    buttonSelector: '#player sj-icon-button[data-id="shuffle"]'
  },
  slider: {
    id: 'material-player-progress'
  }
};

// Define bind method
function bind(context, fn) {
  return function bindFn () {
    return fn.apply(context, arguments);
  };
}

// Define our constructor
function GoogleMusic(win) {
  // If win was not provided, complain
  if (!win) {
    throw new Error('`win` was not provided to the `GoogleMusic` constructor');
  }

  // Inherit from EventEmitter
  EventEmitter.call(this);

  // Localize reference to window and document
  this.win = win;
  this.doc = win.document;

  // Initialize a keyboard and mouse
  this.keyboard = new Keyboard(win);
  this.mouse = new Mouse(win);

  // For each of the prototype sections
  var proto = GoogleMusic._protoObj;
  for (var protoKey in proto) {
    if (proto.hasOwnProperty(protoKey)) {
      // Define a key on our object
      this[protoKey] = {};

      // For each of the keys on the section, define a function that invokes on this original context
      var section = proto[protoKey];
      for (var sectionKey in section) {
        if (section.hasOwnProperty(sectionKey)) {
          this[protoKey][sectionKey] = bind(this, section[sectionKey]);
        }
      }

      // If there was an `init` method, run it
      if (this[protoKey].init) {
        this[protoKey].init();
      }
    }
  }
}
// Inherit from EventEmitter normally
inherits(GoogleMusic, EventEmitter);

// Define a "prototype" that will have magical invocation
var proto = GoogleMusic._protoObj = {};

// Create a volume API
proto.volume = {
  // Query required elements
  init: function () {
    this.volume._sliderEl = this.doc.querySelector('#vslider');
  },

  // Get the current volume level.
  getVolume: function () {
    return parseInt(this.volume._sliderEl.getAttribute('aria-valuenow'), 10);
  },

  // Set the volume level (0 - 100).
  setVolume: function (vol) {
    var current = this.volume.getVolume();

    if (vol > current) {
      this.volume.increaseVolume(vol - current);
    } else if (vol < current) {
      this.volume.decreaseVolume(current - vol);
    }
  },

  // Increase the volume by an amount (default of 1)
  increaseVolume: function (amount) {
    if (amount === undefined) {
      amount = 1;
    }

    for (var i = 0; i < amount; i++) {
      this.keyboard.sendKey(this.volume._sliderEl, Keyboard.KEY_UP);
    }
  },

  // Decrease the volume by an amount (default of 1)
  decreaseVolume: function (amount) {
    if (amount === undefined) {
      amount = 1;
    }

    for (var i = 0; i < amount; i++) {
      this.keyboard.sendKey(this.volume._sliderEl, Keyboard.KEY_DOWN);
    }
  }
};

// Create a playback API and constants
GoogleMusic.Playback = {
  // Playback states
  STOPPED: 0,
  PAUSED: 1,
  PLAYING: 2,

  // Repeat modes
  LIST_REPEAT: 'LIST_REPEAT',
  SINGLE_REPEAT: 'SINGLE_REPEAT',
  NO_REPEAT: 'NO_REPEAT',

  // Shuffle modes
  ALL_SHUFFLE: 'ALL_SHUFFLE',
  NO_SHUFFLE: 'NO_SHUFFLE'
};
proto.playback = {
  // Query references to the media playback elements
  init: function () {
    this.playback._sliderEl = this.doc.getElementById(SELECTORS.slider.id);
    this.playback._playPauseEl = this.doc.querySelector(SELECTORS.playPause.buttonSelector);
    this.playback._forwardEl = this.doc.querySelector(SELECTORS.forward.buttonSelector);
    this.playback._rewindEl = this.doc.querySelector(SELECTORS.rewind.buttonSelector);
    this.playback._shuffleEl = this.doc.querySelector(SELECTORS.shuffle.buttonSelector);
    this.playback._repeatEl = this.doc.querySelector(SELECTORS.repeat.buttonSelector);
  },

  // Time functions
  getPlaybackTime: function () {
    return parseInt(this.playback._sliderEl.getAttribute('aria-valuenow'), 10);
  },

  setPlaybackTime: function (milliseconds) {
    var percent = milliseconds / parseFloat(this.playback._sliderEl.getAttribute('aria-valuemax'), 10);
    var lower = this.playback._sliderEl.offsetLeft + 6;
    var upper = this.playback._sliderEl.offsetLeft + this.playback._sliderEl.clientWidth - 6;
    var x = lower + percent * (upper - lower);

    this.mouse.clickAtLocation(this.playback._sliderEl, x, 0);
  },

  // Playback functions
  playPause: function () { this.playback._playPauseEl.click(); },
  forward: function () { this.playback._forwardEl.click(); },
  rewind: function () { this.playback._rewindEl.click(); },

  getShuffle: function () { return this.playback._shuffleEl.getAttribute('value'); },
  toggleShuffle: function () { this.playback._shuffleEl.click(); },

  getRepeat: function () {
    return this.playback._repeatEl.getAttribute('value');
  },

  toggleRepeat: function (mode) {
    if (!mode) {
      // Toggle between repeat modes once
      this.playback._repeatEl.click();
    } else {
      // Toggle between repeat modes until the desired mode is activated
      while (this.playback.getRepeat() !== mode) {
        this.playback._repeatEl.click();
      }
    }
  },

  // Taken from the Google Play Music page
  toggleVisualization: function () {
    this.win.SJBpost('toggleVisualization');
  }
};

// Create a rating API
proto.rating = {
  // Determine if a thumb is selected or not
  _isElSelected: function (el) {
    // If the target is not outlined in its shadow DOM, then it's selected
    // jscs:disable maximumLineLength
    // DEV: Access shadow DOM via `$`
    //   Selected thumbs up:
    //   <core-icon relative="" id="icon" src="{{src}}" icon="{{icon}}" aria-label="thumb-up" role="img"></core-icon>
    //   Unselected thumbs down:
    //   <core-icon relative="" id="icon" src="{{src}}" icon="{{icon}}" aria-label="thumb-down-outline" role="img"></core-icon>
    // jscs:enable maximumLineLength
    return el.$.icon.getAttribute('aria-label').indexOf('-outline') === -1;
  },
  // Get current rating
  getRating: function () {
    var thumbEls = this.doc.querySelectorAll(SELECTORS.rating.thumbsSelector);
    var i = 0;
    var len = thumbEls.length;
    for (; i < len; i++) {
      var el = thumbEls[i];
      if (this.rating._isElSelected(el)) {
        return el.getAttribute('data-rating');
      }
    }
    return 0;
  },

  // Thumbs up
  toggleThumbsUp: function () {
    var el = this.doc.querySelector(SELECTORS.rating.thumbsUpSelector);

    if (el) {
      el.click();
    }
  },

  // Thumbs down
  toggleThumbsDown: function () {
    var el = this.doc.querySelector(SELECTORS.rating.thumbsDownSelector);

    if (el) {
      el.click();
    }
  },

  // Set a rating
  setRating: function (rating) {
    var selector = SELECTORS.rating.thumbSelectorFormat.replace('{rating}', rating);
    var el = this.doc.querySelector(selector);

    if (el && !this.rating._isElSelected(el)) {
      el.click();
    }
  }
};

// Miscellaneous functions
proto.extras = {
  // Get a shareable URL of the song on Google Play Music
  getSongURL: function () {
    var albumEl = this.doc.querySelector('.player-album');
    var artistEl = this.doc.querySelector('.player-artist');

    var urlTemplate = 'https://play.google.com/music/m/';
    var url = null;

    var parseID = function (id) {
      return id.substring(0, id.indexOf('/'));
    };

    if (albumEl === null && artistEl === null) {
      return null;
    }

    var albumId = parseID(albumEl.dataset.id);
    var artistId = parseID(artistEl.dataset.id);

    if (albumId) {
      url = urlTemplate + albumId;
    } else if (artistId) {
      url = urlTemplate + artistId;
    }

    return url;
  }
};

proto.hooks = {
  init: function () {
    // Save context for bindings
    var that = this;

    // Define mutation observer for reuse
    var MutationObserver = this.win.MutationObserver || this.win.WebKitMutationObserver;

    var lastTitle = '';
    var lastArtist = '';
    var lastAlbum = '';

    var addObserver = new MutationObserver(function (mutations) {
      mutations.forEach(function (m) {
        for (var i = 0; i < m.addedNodes.length; i++) {
          var target = m.addedNodes[i];
          var name = target.id || target.className;

          if (name === 'text-wrapper')  {
            var title = that.doc.querySelector('#playerSongTitle');
            var artist = that.doc.querySelector('#player-artist');
            var album = that.doc.querySelector('.player-album');
            var art = that.doc.querySelector('#playingAlbumArt');
            var duration = parseInt(that.doc.getElementById(SELECTORS.slider.id).getAttribute('aria-valuemax'), 10);

            title = (title) ? title.innerText : 'Unknown';
            artist = (artist) ? artist.innerText : 'Unknown';
            album = (album) ? album.innerText : 'Unknown';
            art = (art) ? art.src : null;

            // The art may be a protocol-relative URL, so normalize it to HTTPS
            if (art && art.slice(0, 2) === '//') {
              art = 'https:' + art;
            }

            // Make sure that this is the first of the notifications for the
            // insertion of the song information elements.
            if (lastTitle !== title || lastArtist !== artist || lastAlbum !== album) {
              that.emit('change:song', {
                title: title,
                artist: artist,
                album: album,
                art: art,
                duration: duration
              });

              lastTitle = title;
              lastArtist = artist;
              lastAlbum = album;
            }
          }
        }
      });
    });

    var shuffleObserver = new MutationObserver(function (mutations) {
      mutations.forEach(function (m) {
        var target = m.target;
        var id = target.dataset.id;

        if (id === SELECTORS.shuffle.dataId) {
          that.emit('change:shuffle', target.value);
        }
      });
    });

    var repeatObserver = new MutationObserver(function (mutations) {
      mutations.forEach(function (m) {
        var target = m.target;
        var id = target.dataset.id;

        if (id === SELECTORS.repeat.dataId) {
          that.emit('change:repeat', target.value);
        }
      });
    });

    var playbackObserver = new MutationObserver(function (mutations) {
      mutations.forEach(function (m) {
        var target = m.target;
        var id = target.dataset.id;

        if (id === SELECTORS.playPause.dataId) {
          var mode;
          var playing = target.classList.contains(SELECTORS.playPause.playingClass);

          if (playing) {
            mode = GoogleMusic.Playback.PLAYING;
          } else {
            // If there is a current song, then the player is paused
            if (that.doc.getElementById(SELECTORS.info.containerId).childNodes.length) {
              mode = GoogleMusic.Playback.PAUSED;
            } else {
              mode = GoogleMusic.Playback.STOPPED;
            }
          }

          that.emit('change:playback', mode);
        }
      });
    });

    var playbackTimeObserver = new MutationObserver(function (mutations) {
      mutations.forEach(function (m) {
        var target = m.target;
        var id = target.id;

        if (id === SELECTORS.shuffle.buttonSelector) {
          var currentTime = parseInt(target.getAttribute('aria-valuenow'), 10);
          var totalTime = parseInt(target.getAttribute('aria-valuemax'), 10);
          that.emit('change:playback-time', {current: currentTime, total: totalTime});
        }
      });
    });

    var ratingObserver = new MutationObserver(function (mutations) {
      mutations.forEach(function (m) {
        // var target = m.target;
        // var notSelected = target.$.icon.ariaLabel.indexOf('-outline') === -1;
        // var selected = !notSelected;
        // if (selected) {
        //   that.emit('change:rating', target.dataset.rating);
        // }
      });
    });

    addObserver.observe(this.doc.getElementById(SELECTORS.info.containerId), {
      childList: true,
      subtree: true
    });
    shuffleObserver.observe(this.doc.querySelector(SELECTORS.shuffle.buttonSelector), {
      attributes: true
    });
    repeatObserver.observe(this.doc.querySelector(SELECTORS.repeat.buttonSelector), {
      attributes: true
    });
    playbackObserver.observe(this.doc.querySelector(SELECTORS.playPause.buttonSelector), {
      attributes: true
    });
    playbackTimeObserver.observe(this.doc.getElementById(SELECTORS.slider.id), {
      attributes: true
    });
    ratingObserver.observe(this.doc.querySelector(SELECTORS.rating.containerSelector), {
      attributes: true,
      subtree: true
    });
  }
};

// Expose selectors as a class property
GoogleMusic.SELECTORS = SELECTORS;

// Export our constructor
module.exports = GoogleMusic;
<commit_msg>Added back rating hook<commit_after>// Load in dependencies
var EventEmitter = require('events').EventEmitter;
var inherits = require('inherits');
var Keyboard = require('./keyboard');
var Mouse = require('./mouse');

// Define selector constants
var SELECTORS = {
  info: {
    containerId: 'playerSongInfo',
    titleId: 'player-song-title'
  },
  forward: {
    buttonSelector: '#player sj-icon-button[data-id="forward"]'
  },
  playPause: {
    buttonSelector: '#player sj-icon-button[data-id="play-pause"]',
    dataId: 'play-pause',
    playingClass: 'playing'
  },
  rating: {
    // DEV: `.player-rating-container` doesn't exist until a song is playing
    containerSelector: '#playerSongInfo',
    thumbsSelector: '#player .player-rating-container [icon^="sj:thumb-"][data-rating]',
    thumbsUpSelector: '#player .player-rating-container [icon^="sj:thumb-"][data-rating="5"]',
    thumbsDownSelector: '#player .player-rating-container [icon^="sj:thumb-"][data-rating="1"]',
    thumbSelectorFormat: '#player .player-rating-container [icon^="sj:thumb-"][data-rating="{rating}"]'
  },
  repeat: {
    dataId: 'repeat',
    buttonSelector: '#player sj-icon-button[data-id="repeat"]'
  },
  rewind: {
    buttonSelector: '#player sj-icon-button[data-id="rewind"]'
  },
  shuffle: {
    dataId: 'shuffle',
    buttonSelector: '#player sj-icon-button[data-id="shuffle"]'
  },
  slider: {
    id: 'material-player-progress'
  }
};

// Define bind method
function bind(context, fn) {
  return function bindFn () {
    return fn.apply(context, arguments);
  };
}

// Define our constructor
function GoogleMusic(win) {
  // If win was not provided, complain
  if (!win) {
    throw new Error('`win` was not provided to the `GoogleMusic` constructor');
  }

  // Inherit from EventEmitter
  EventEmitter.call(this);

  // Localize reference to window and document
  this.win = win;
  this.doc = win.document;

  // Initialize a keyboard and mouse
  this.keyboard = new Keyboard(win);
  this.mouse = new Mouse(win);

  // For each of the prototype sections
  var proto = GoogleMusic._protoObj;
  for (var protoKey in proto) {
    if (proto.hasOwnProperty(protoKey)) {
      // Define a key on our object
      this[protoKey] = {};

      // For each of the keys on the section, define a function that invokes on this original context
      var section = proto[protoKey];
      for (var sectionKey in section) {
        if (section.hasOwnProperty(sectionKey)) {
          this[protoKey][sectionKey] = bind(this, section[sectionKey]);
        }
      }

      // If there was an `init` method, run it
      if (this[protoKey].init) {
        this[protoKey].init();
      }
    }
  }
}
// Inherit from EventEmitter normally
inherits(GoogleMusic, EventEmitter);

// Define a "prototype" that will have magical invocation
var proto = GoogleMusic._protoObj = {};

// Create a volume API
proto.volume = {
  // Query required elements
  init: function () {
    this.volume._sliderEl = this.doc.querySelector('#vslider');
  },

  // Get the current volume level.
  getVolume: function () {
    return parseInt(this.volume._sliderEl.getAttribute('aria-valuenow'), 10);
  },

  // Set the volume level (0 - 100).
  setVolume: function (vol) {
    var current = this.volume.getVolume();

    if (vol > current) {
      this.volume.increaseVolume(vol - current);
    } else if (vol < current) {
      this.volume.decreaseVolume(current - vol);
    }
  },

  // Increase the volume by an amount (default of 1)
  increaseVolume: function (amount) {
    if (amount === undefined) {
      amount = 1;
    }

    for (var i = 0; i < amount; i++) {
      this.keyboard.sendKey(this.volume._sliderEl, Keyboard.KEY_UP);
    }
  },

  // Decrease the volume by an amount (default of 1)
  decreaseVolume: function (amount) {
    if (amount === undefined) {
      amount = 1;
    }

    for (var i = 0; i < amount; i++) {
      this.keyboard.sendKey(this.volume._sliderEl, Keyboard.KEY_DOWN);
    }
  }
};

// Create a playback API and constants
GoogleMusic.Playback = {
  // Playback states
  STOPPED: 0,
  PAUSED: 1,
  PLAYING: 2,

  // Repeat modes
  LIST_REPEAT: 'LIST_REPEAT',
  SINGLE_REPEAT: 'SINGLE_REPEAT',
  NO_REPEAT: 'NO_REPEAT',

  // Shuffle modes
  ALL_SHUFFLE: 'ALL_SHUFFLE',
  NO_SHUFFLE: 'NO_SHUFFLE'
};
proto.playback = {
  // Query references to the media playback elements
  init: function () {
    this.playback._sliderEl = this.doc.getElementById(SELECTORS.slider.id);
    this.playback._playPauseEl = this.doc.querySelector(SELECTORS.playPause.buttonSelector);
    this.playback._forwardEl = this.doc.querySelector(SELECTORS.forward.buttonSelector);
    this.playback._rewindEl = this.doc.querySelector(SELECTORS.rewind.buttonSelector);
    this.playback._shuffleEl = this.doc.querySelector(SELECTORS.shuffle.buttonSelector);
    this.playback._repeatEl = this.doc.querySelector(SELECTORS.repeat.buttonSelector);
  },

  // Time functions
  getPlaybackTime: function () {
    return parseInt(this.playback._sliderEl.getAttribute('aria-valuenow'), 10);
  },

  setPlaybackTime: function (milliseconds) {
    var percent = milliseconds / parseFloat(this.playback._sliderEl.getAttribute('aria-valuemax'), 10);
    var lower = this.playback._sliderEl.offsetLeft + 6;
    var upper = this.playback._sliderEl.offsetLeft + this.playback._sliderEl.clientWidth - 6;
    var x = lower + percent * (upper - lower);

    this.mouse.clickAtLocation(this.playback._sliderEl, x, 0);
  },

  // Playback functions
  playPause: function () { this.playback._playPauseEl.click(); },
  forward: function () { this.playback._forwardEl.click(); },
  rewind: function () { this.playback._rewindEl.click(); },

  getShuffle: function () { return this.playback._shuffleEl.getAttribute('value'); },
  toggleShuffle: function () { this.playback._shuffleEl.click(); },

  getRepeat: function () {
    return this.playback._repeatEl.getAttribute('value');
  },

  toggleRepeat: function (mode) {
    if (!mode) {
      // Toggle between repeat modes once
      this.playback._repeatEl.click();
    } else {
      // Toggle between repeat modes until the desired mode is activated
      while (this.playback.getRepeat() !== mode) {
        this.playback._repeatEl.click();
      }
    }
  },

  // Taken from the Google Play Music page
  toggleVisualization: function () {
    this.win.SJBpost('toggleVisualization');
  }
};

// Create a rating API
proto.rating = {
  // Determine if a thumb is selected or not
  _isElSelected: function (el) {
    // If the target is not outlined in its shadow DOM, then it's selected
    // jscs:disable maximumLineLength
    // DEV: Access shadow DOM via `$`
    //   Selected thumbs up:
    //   <core-icon relative="" id="icon" src="{{src}}" icon="{{icon}}" aria-label="thumb-up" role="img"></core-icon>
    //   Unselected thumbs down:
    //   <core-icon relative="" id="icon" src="{{src}}" icon="{{icon}}" aria-label="thumb-down-outline" role="img"></core-icon>
    // jscs:enable maximumLineLength
    return el.$.icon.getAttribute('aria-label').indexOf('-outline') === -1;
  },
  // Get current rating
  getRating: function () {
    var thumbEls = this.doc.querySelectorAll(SELECTORS.rating.thumbsSelector);
    var i = 0;
    var len = thumbEls.length;
    for (; i < len; i++) {
      var el = thumbEls[i];
      if (this.rating._isElSelected(el)) {
        return el.dataset.rating;
      }
    }
    return 0;
  },

  // Thumbs up
  toggleThumbsUp: function () {
    var el = this.doc.querySelector(SELECTORS.rating.thumbsUpSelector);

    if (el) {
      el.click();
    }
  },

  // Thumbs down
  toggleThumbsDown: function () {
    var el = this.doc.querySelector(SELECTORS.rating.thumbsDownSelector);

    if (el) {
      el.click();
    }
  },

  // Set a rating
  setRating: function (rating) {
    var selector = SELECTORS.rating.thumbSelectorFormat.replace('{rating}', rating);
    var el = this.doc.querySelector(selector);

    if (el && !this.rating._isElSelected(el)) {
      el.click();
    }
  }
};

// Miscellaneous functions
proto.extras = {
  // Get a shareable URL of the song on Google Play Music
  getSongURL: function () {
    var albumEl = this.doc.querySelector('.player-album');
    var artistEl = this.doc.querySelector('.player-artist');

    var urlTemplate = 'https://play.google.com/music/m/';
    var url = null;

    var parseID = function (id) {
      return id.substring(0, id.indexOf('/'));
    };

    if (albumEl === null && artistEl === null) {
      return null;
    }

    var albumId = parseID(albumEl.dataset.id);
    var artistId = parseID(artistEl.dataset.id);

    if (albumId) {
      url = urlTemplate + albumId;
    } else if (artistId) {
      url = urlTemplate + artistId;
    }

    return url;
  }
};

proto.hooks = {
  init: function () {
    // Save context for bindings
    var that = this;

    // Define mutation observer for reuse
    var MutationObserver = this.win.MutationObserver || this.win.WebKitMutationObserver;

    var lastTitle = '';
    var lastArtist = '';
    var lastAlbum = '';

    var addObserver = new MutationObserver(function (mutations) {
      mutations.forEach(function (m) {
        for (var i = 0; i < m.addedNodes.length; i++) {
          var target = m.addedNodes[i];
          var name = target.id || target.className;

          if (name === 'text-wrapper')  {
            var title = that.doc.querySelector('#playerSongTitle');
            var artist = that.doc.querySelector('#player-artist');
            var album = that.doc.querySelector('.player-album');
            var art = that.doc.querySelector('#playingAlbumArt');
            var duration = parseInt(that.doc.getElementById(SELECTORS.slider.id).getAttribute('aria-valuemax'), 10);

            title = (title) ? title.innerText : 'Unknown';
            artist = (artist) ? artist.innerText : 'Unknown';
            album = (album) ? album.innerText : 'Unknown';
            art = (art) ? art.src : null;

            // The art may be a protocol-relative URL, so normalize it to HTTPS
            if (art && art.slice(0, 2) === '//') {
              art = 'https:' + art;
            }

            // Make sure that this is the first of the notifications for the
            // insertion of the song information elements.
            if (lastTitle !== title || lastArtist !== artist || lastAlbum !== album) {
              that.emit('change:song', {
                title: title,
                artist: artist,
                album: album,
                art: art,
                duration: duration
              });

              lastTitle = title;
              lastArtist = artist;
              lastAlbum = album;
            }
          }
        }
      });
    });

    var shuffleObserver = new MutationObserver(function (mutations) {
      mutations.forEach(function (m) {
        var target = m.target;
        var id = target.dataset.id;

        if (id === SELECTORS.shuffle.dataId) {
          that.emit('change:shuffle', target.value);
        }
      });
    });

    var repeatObserver = new MutationObserver(function (mutations) {
      mutations.forEach(function (m) {
        var target = m.target;
        var id = target.dataset.id;

        if (id === SELECTORS.repeat.dataId) {
          that.emit('change:repeat', target.value);
        }
      });
    });

    var playbackObserver = new MutationObserver(function (mutations) {
      mutations.forEach(function (m) {
        var target = m.target;
        var id = target.dataset.id;

        if (id === SELECTORS.playPause.dataId) {
          var mode;
          var playing = target.classList.contains(SELECTORS.playPause.playingClass);

          if (playing) {
            mode = GoogleMusic.Playback.PLAYING;
          } else {
            // If there is a current song, then the player is paused
            if (that.doc.getElementById(SELECTORS.info.containerId).childNodes.length) {
              mode = GoogleMusic.Playback.PAUSED;
            } else {
              mode = GoogleMusic.Playback.STOPPED;
            }
          }

          that.emit('change:playback', mode);
        }
      });
    });

    var playbackTimeObserver = new MutationObserver(function (mutations) {
      mutations.forEach(function (m) {
        var target = m.target;
        var id = target.id;

        if (id === SELECTORS.shuffle.buttonSelector) {
          var currentTime = parseInt(target.getAttribute('aria-valuenow'), 10);
          var totalTime = parseInt(target.getAttribute('aria-valuemax'), 10);
          that.emit('change:playback-time', {current: currentTime, total: totalTime});
        }
      });
    });

    var ratingObserver = new MutationObserver(function (mutations) {
      mutations.forEach(function (m) {
        var target = m.target;
        if (that.rating._isElSelected(target)) {
          that.emit('change:rating', target.dataset.rating);
        }
      });
    });

    addObserver.observe(this.doc.getElementById(SELECTORS.info.containerId), {
      childList: true,
      subtree: true
    });
    shuffleObserver.observe(this.doc.querySelector(SELECTORS.shuffle.buttonSelector), {
      attributes: true
    });
    repeatObserver.observe(this.doc.querySelector(SELECTORS.repeat.buttonSelector), {
      attributes: true
    });
    playbackObserver.observe(this.doc.querySelector(SELECTORS.playPause.buttonSelector), {
      attributes: true
    });
    playbackTimeObserver.observe(this.doc.getElementById(SELECTORS.slider.id), {
      attributes: true
    });
    ratingObserver.observe(this.doc.querySelector(SELECTORS.rating.containerSelector), {
      attributes: true,
      subtree: true
    });
  }
};

// Expose selectors as a class property
GoogleMusic.SELECTORS = SELECTORS;

// Export our constructor
module.exports = GoogleMusic;
<|endoftext|>