<commit_before>/* global TrelloPowerUp */

var WHITE_ICON = './images/icon-white.svg';
var GRAY_ICON = './images/icon-gray.svg';
var APP_ID = '380ed9c5-4e06-4b73-b2e0-115ab3810cb8';
var DEFAULT = "XXX";

var KEY_ACCESS_TOKEN = "accessToken";
var KEY_ENDPOINT_HINT= "endpointHint";
var KEY_REFRESH_TOKEN = "refreshToken";
var KEY_EXPIRES_IN = "expiresIn";

var parkMap = {
    acad: 'Acadia National Park',
    arch: 'Arches National Park',
    badl: 'Badlands National Park',
    brca: 'Bryce Canyon National Park',
    crla: 'Crater Lake National Park',
    dena: 'Denali National Park',
    glac: 'Glacier National Park',
    grca: 'Grand Canyon National Park',
    grte: 'Grand Teton National Park',
    olym: 'Olympic National Park',
    yell: 'Yellowstone National Park',
    yose: 'Yosemite National Park',
    zion: 'Zion National Park'
};

var getBadges = function (t) {
    return t.card('name')
        .get('name')
        .then(function (cardName) {
            var badgeColor;
            var icon = GRAY_ICON;
            var lowercaseName = cardName.toLowerCase();
            if (lowercaseName.indexOf('green') > -1) {
                badgeColor = 'green';
                icon = WHITE_ICON;
            } else if (lowercaseName.indexOf('yellow') > -1) {
                badgeColor = 'yellow';
                icon = WHITE_ICON;
            } else if (lowercaseName.indexOf('red') > -1) {
                badgeColor = 'red';
                icon = WHITE_ICON;
            }

            if (lowercaseName.indexOf('dynamic') > -1) {
                // dynamic badges can have their function rerun after a set number
                // of seconds defined by refresh. Minimum of 10 seconds.
                return [{
                    dynamic: function () {
                        return {
                            title: 'Detail Badge', // for detail badges only
                            text: 'Dynamic ' + (Math.random() * 100).toFixed(0).toString(),
                            icon: icon, // for card front badges only
                            color: badgeColor,
                            refresh: 10
                        }
                    }
                }]
            }

            if (lowercaseName.indexOf('static') > -1) {
                // return an array of badge objects
                return [{
                    title: 'Detail Badge', // for detail badges only
                    text: 'Static',
                    icon: icon, // for card front badges only
                    color: badgeColor
                }];
            } else {
                return [];
            }
        })
};

var formatNPSUrl = function (t, url) {
    if (!/^https?:\/\/www\.nps\.gov\/[a-z]{4}\//.test(url)) {
        return null;
    }
    var parkShort = /^https?:\/\/www\.nps\.gov\/([a-z]{4})\//.exec(url)[1];
    if (parkShort && parkMap[parkShort]) {
        return parkMap[parkShort];
    } else {
        return null;
    }
};

var boardButtonCallback = function (t) {
    /*return t.popup({
        title: 'Popup List Example',
        items: [
            {
                text: 'Open Overlay',
                callback: function (t) {
                    return t.overlay({
                        url: './overlay.html',
                        args: {rand: (Math.random() * 100).toFixed(0)}
                    })
                        .then(function () {
                            return t.closePopup();
                        });
                }
            },
            {
                text: 'Open Board Bar',
                callback: function (t) {
                    return t.boardBar({
                        url: './board-bar.html',
                        height: 200
                    })
                        .then(function () {
                            return t.closePopup();
                        });
                }
            }
        ]
    });*/

    return t.overlay({
        url: './javascript/editor/grapheditor/www/index.html',
        args: {rand: (Math.random() * 100).toFixed(0)}
    })
        .then(function () {
            return t.closePopup();
        });

};

var cardButtonCallback = function (t) {
    /*var items = Object.keys(parkMap).map(function (parkCode) {
        var urlForCode = 'http://www.nps.gov/' + parkCode + '/';
        return {
            text: parkMap[parkCode],
            url: urlForCode,
            callback: function (t) {
                return t.attach({url: urlForCode, name: parkMap[parkCode]})
                    .then(function () {
                        return t.closePopup();
                    })
            }
        };
    });

    return t.popup({
        title: 'Popup Search Example',
        items: items,
        search: {
            count: 5,
            placeholder: 'Search National Parks',
            empty: 'No parks found'
        }
    });*/

    return t.overlay({
        url: './javascript/editor/grapheditor/www/index.html',
        args: {rand: (Math.random() * 100).toFixed(0)}
    })
        .then(function () {
            return t.closePopup();
        });

};

TrelloPowerUp.initialize({

    'authorization-status': function(t) {

        return t.get('board', 'private', KEY_ACCESS_TOKEN, DEFAULT)
            .then(function (token) {
                console.log(token);

                if(token===DEFAULT) {

                    // if false
                    // `show-authorization` is displayed
                    return new TrelloPowerUp.Promise((resolve) =>
                        resolve({ authorized: false })
                    )
                }
                else {

                    // if true
                    // t.set has been used
                    // `remove settings` is displayed

                    return new TrelloPowerUp.Promise((resolve) =>
                        resolve({ authorized: true })
                    )

                }

            });

    },

    'show-authorization': function(t) {
        return t.popup({
            title: 'Authorize',
            items: [
                {
                    text: "Connect Microsoft or Office 365 Account",
                    callback: function () {
                        console.log("TODO settings auth");
                    }
                }
            ]
        });
    },

    'show-settings': function (t, options) {
        return t.popup({
            title: 'Settings',
            url: './settings.html',
            height: 184
        });
    },

    // Allows you to add one or more buttons on the right side of the back of cards
    // Each button should have { icon, text, callback -> method }
    'card-buttons': function (t, options) {
        return [{
            icon: GRAY_ICON,
            text: 'New Drawing',
            callback: cardButtonCallback
        }];
    },

    'attachment-sections': function (t, options) {

        //console.log(JSON.stringify(options));

        // options.entries is a list of the attachments for this card
        // you can look through them and 'claim' any that you want to
        // include in your section.

        // we will just claim urls for Yellowstone
        var claimed = options.entries.filter(function (attachment) {
            return attachment.url.indexOf(BASE_URL) == 0;
        });

        // you can have more than one attachment section on a card
        // you can group items together into one section, have a section
        // per attachment, or anything in between.
        if (claimed && claimed.length > 0) {
            // if the title for your section requires a network call or other
            // potentially length operation you can provide a function for the title
            // that returns the section title. If you do so, provide a unique id for
            // your section

            //console.log(JSON.stringify(claimed));

            return [{
                id: BASE_URL, // optional if you aren't using a function for the title
                claimed: claimed,
                icon: GRAY_ICON,
                title: 'Attached Drawings',
                content: {
                    type: 'iframe',
                    url: t.signUrl('./section.html', {arg: claimed}),
                    height: 230
                }
            }];
        } else {
            return [];
        }
    },
    'attachment-thumbnail': function (t, options) {
        var parkName = formatNPSUrl(t, options.url);
        if (parkName) {
            // return an object with some or all of these properties:
            // url, title, image, openText, modified (Date), created (Date), createdBy, modifiedBy
            return {
                url: options.url,
                title: parkName,
                image: {
                    url: './images/nps.svg',
                    logo: true // false if you are using a thumbnail of the content
                },
                openText: 'Open in NPS'
            };
        } else {
            throw t.NotHandled();
        }
    },

    // Allows you to add one or more buttons to a Board at the top right of the screen
    'board-buttons': function (t, options) {
        return [{
            icon: WHITE_ICON,
            text: 'Office 365',
            callback: boardButtonCallback
        }];
    },

    'card-badges': function (t, options) {
        return getBadges(t);
    },

    'card-detail-badges': function (t, options) {
        return getBadges(t);
    },
    'card-from-url': function (t, options) {
        var parkName = formatNPSUrl(t, options.url);
        if (parkName) {
            return {
                name: parkName,
                desc: 'An awesome park: ' + options.url
            };
        } else {
            throw t.NotHandled();
        }
    },
    'format-url': function (t, options) {
        var parkName = formatDrawUrl(t, options.url);
        if (parkName) {
            return {
                icon: GRAY_ICON,
                text: parkName
            };
        } else {
            throw t.NotHandled();
        }
    }
});
<commit_msg>url match<commit_after>/* global TrelloPowerUp */

var WHITE_ICON = './images/icon-white.svg';
var GRAY_ICON = './images/icon-gray.svg';
var APP_ID = '380ed9c5-4e06-4b73-b2e0-115ab3810cb8';
var DEFAULT = "XXX";

var KEY_ACCESS_TOKEN = "accessToken";
var KEY_ENDPOINT_HINT= "endpointHint";
var KEY_REFRESH_TOKEN = "refreshToken";
var KEY_EXPIRES_IN = "expiresIn";

var parkMap = {
    acad: 'Acadia National Park',
    arch: 'Arches National Park',
    badl: 'Badlands National Park',
    brca: 'Bryce Canyon National Park',
    crla: 'Crater Lake National Park',
    dena: 'Denali National Park',
    glac: 'Glacier National Park',
    grca: 'Grand Canyon National Park',
    grte: 'Grand Teton National Park',
    olym: 'Olympic National Park',
    yell: 'Yellowstone National Park',
    yose: 'Yosemite National Park',
    zion: 'Zion National Park'
};

var getBadges = function (t) {
    return t.card('name')
        .get('name')
        .then(function (cardName) {
            var badgeColor;
            var icon = GRAY_ICON;
            var lowercaseName = cardName.toLowerCase();
            if (lowercaseName.indexOf('green') > -1) {
                badgeColor = 'green';
                icon = WHITE_ICON;
            } else if (lowercaseName.indexOf('yellow') > -1) {
                badgeColor = 'yellow';
                icon = WHITE_ICON;
            } else if (lowercaseName.indexOf('red') > -1) {
                badgeColor = 'red';
                icon = WHITE_ICON;
            }

            if (lowercaseName.indexOf('dynamic') > -1) {
                // dynamic badges can have their function rerun after a set number
                // of seconds defined by refresh. Minimum of 10 seconds.
                return [{
                    dynamic: function () {
                        return {
                            title: 'Detail Badge', // for detail badges only
                            text: 'Dynamic ' + (Math.random() * 100).toFixed(0).toString(),
                            icon: icon, // for card front badges only
                            color: badgeColor,
                            refresh: 10
                        }
                    }
                }]
            }

            if (lowercaseName.indexOf('static') > -1) {
                // return an array of badge objects
                return [{
                    title: 'Detail Badge', // for detail badges only
                    text: 'Static',
                    icon: icon, // for card front badges only
                    color: badgeColor
                }];
            } else {
                return [];
            }
        })
};

var formatNPSUrl = function (t, url) {
    if (!/^https?:\/\/www\.nps\.gov\/[a-z]{4}\//.test(url)) {
        return null;
    }
    var parkShort = /^https?:\/\/www\.nps\.gov\/([a-z]{4})\//.exec(url)[1];
    if (parkShort && parkMap[parkShort]) {
        return parkMap[parkShort];
    } else {
        return null;
    }
};

var boardButtonCallback = function (t) {
    /*return t.popup({
        title: 'Popup List Example',
        items: [
            {
                text: 'Open Overlay',
                callback: function (t) {
                    return t.overlay({
                        url: './overlay.html',
                        args: {rand: (Math.random() * 100).toFixed(0)}
                    })
                        .then(function () {
                            return t.closePopup();
                        });
                }
            },
            {
                text: 'Open Board Bar',
                callback: function (t) {
                    return t.boardBar({
                        url: './board-bar.html',
                        height: 200
                    })
                        .then(function () {
                            return t.closePopup();
                        });
                }
            }
        ]
    });*/

    return t.overlay({
        url: './javascript/editor/grapheditor/www/index.html',
        args: {rand: (Math.random() * 100).toFixed(0)}
    })
        .then(function () {
            return t.closePopup();
        });

};

var cardButtonCallback = function (t) {
    /*var items = Object.keys(parkMap).map(function (parkCode) {
        var urlForCode = 'http://www.nps.gov/' + parkCode + '/';
        return {
            text: parkMap[parkCode],
            url: urlForCode,
            callback: function (t) {
                return t.attach({url: urlForCode, name: parkMap[parkCode]})
                    .then(function () {
                        return t.closePopup();
                    })
            }
        };
    });

    return t.popup({
        title: 'Popup Search Example',
        items: items,
        search: {
            count: 5,
            placeholder: 'Search National Parks',
            empty: 'No parks found'
        }
    });*/

    return t.overlay({
        url: './javascript/editor/grapheditor/www/index.html',
        args: {rand: (Math.random() * 100).toFixed(0)}
    })
        .then(function () {
            return t.closePopup();
        });

};

TrelloPowerUp.initialize({

    'authorization-status': function(t) {

        return t.get('board', 'private', KEY_ACCESS_TOKEN, DEFAULT)
            .then(function (token) {
                console.log(token);

                if(token===DEFAULT) {

                    // if false
                    // `show-authorization` is displayed
                    return new TrelloPowerUp.Promise((resolve) =>
                        resolve({ authorized: false })
                    )
                }
                else {

                    // if true
                    // t.set has been used
                    // `remove settings` is displayed

                    return new TrelloPowerUp.Promise((resolve) =>
                        resolve({ authorized: true })
                    )

                }

            });

    },

    'show-authorization': function(t) {
        return t.popup({
            title: 'Authorize',
            items: [
                {
                    text: "Connect Microsoft or Office 365 Account",
                    callback: function () {
                        console.log("TODO settings auth");
                    }
                }
            ]
        });
    },

    'show-settings': function (t, options) {
        return t.popup({
            title: 'Settings',
            url: './settings.html',
            height: 184
        });
    },

    // Allows you to add one or more buttons on the right side of the back of cards
    // Each button should have { icon, text, callback -> method }
    'card-buttons': function (t, options) {
        return [{
            icon: GRAY_ICON,
            text: 'New Drawing',
            callback: cardButtonCallback
        }];
    },

    'attachment-sections': function (t, options) {

        //console.log(JSON.stringify(options));

        // options.entries is a list of the attachments for this card
        // you can look through them and 'claim' any that you want to
        // include in your section.

        // we will just claim urls for Yellowstone
        var claimed = options.entries.filter(function (attachment) {
            return attachment.url.indexOf(BASE_URL) == 0;
        });

        // you can have more than one attachment section on a card
        // you can group items together into one section, have a section
        // per attachment, or anything in between.
        if (claimed && claimed.length > 0) {
            // if the title for your section requires a network call or other
            // potentially length operation you can provide a function for the title
            // that returns the section title. If you do so, provide a unique id for
            // your section

            //console.log(JSON.stringify(claimed));

            return [{
                id: BASE_URL, // optional if you aren't using a function for the title
                claimed: claimed,
                icon: GRAY_ICON,
                title: 'Attached Drawings',
                content: {
                    type: 'iframe',
                    url: t.signUrl('./section.html', {arg: claimed}),
                    height: 230
                }
            }];
        } else {
            return [];
        }
    },
    'attachment-thumbnail': function (t, options) {
        var parkName = formatNPSUrl(t, options.url);
        if (parkName) {
            // return an object with some or all of these properties:
            // url, title, image, openText, modified (Date), created (Date), createdBy, modifiedBy
            return {
                url: options.url,
                title: parkName,
                image: {
                    url: './images/nps.svg',
                    logo: true // false if you are using a thumbnail of the content
                },
                openText: 'Open in NPS'
            };
        } else {
            throw t.NotHandled();
        }
    },

    // Allows you to add one or more buttons to a Board at the top right of the screen
    'board-buttons': function (t, options) {
        return [{
            icon: WHITE_ICON,
            text: 'Office 365',
            callback: boardButtonCallback
        }];
    },

    'card-badges': function (t, options) {
        return getBadges(t);
    },

    'card-detail-badges': function (t, options) {
        return getBadges(t);
    },
    'card-from-url': function (t, options) {
        var parkName = formatNPSUrl(t, options.url);
        if (parkName) {
            return {
                name: parkName,
                desc: 'An awesome park: ' + options.url
            };
        } else {
            throw t.NotHandled();
        }
    },
    'format-url': function (t, options) {
        var name = formatDrawUrl(t, options.url);

        console.log("format-url> "+JSON.stringify(name));

        if (name) {
            return {
                icon: GRAY_ICON,
                text: name
            };
        } else {
            throw t.NotHandled();
        }
    }
});
<|endoftext|>


<commit_before>'use strict'

const MultihashingAsync = require('multihashing-async')
const stream = require('pull-stream')
const Logger = require('logplease')
const deferred = require('deferred')
Logger.setLogLevel(Logger.LogLevels.DEBUG) // change to ERROR

const logger = Logger.create('UP2P', { color: Logger.Colors.Blue })

const ConnectionHandler = require('./connectionHandler')

module.exports = class UniversalPeerToPeer {

  constructor (aPeerId, aFileMetadataHandler) {
    this._connectionHandler

    let self = this
    return new ConnectionHandler(aPeerId, aFileMetadataHandler) // returns a promise that resolves to the connectionHandler after all async tasks are completed
      .then((connectionHandler) => {
        self._connectionHandler = connectionHandler
        return deferred(self)
      })
  }

  start () {
    return deferred.promisify(this._connectionHandler._node.start.bind(this._connectionHandler._node))()
  }

  stop () {
    return deferred.promisify(this._connectionHandler._node.stop.bind(this._connectionHandler._node))()
  }

  connect (aUserHashStr) {
    var ma = '/libp2p-webrtc-star/ip4/127.0.0.1/tcp/15555/ws/ipfs/' + aUserHashStr
    logger.debug('Attempting to connect to ' + aUserHashStr)
    return this._connectionHandler.connect(ma)
  }
  disconnect (aUserHashStr) {
    return this._connectionHandler.disconnect(aUserHashStr)
  }
  publish (aData, aMetadata) {
    var self = this
    return deferred.promisify(MultihashingAsync)(Buffer(aData), 'sha2-256')
      .then((mh) => {
        var hash = MultihashingAsync.multihash.toB58String(mh)
        var def = deferred()
        stream(
          stream.once(aData),
          self._connectionHandler._db.getFileWriter(hash, function () {
            def.resolve(hash)
          })
        )
        return def.promise.then(() => self._connectionHandler._db.storeMetadata(hash, aMetadata)).then(() => hash)
      })
  }

  view (aDataHashStr) {
    return this._connectionHandler._db.getFile(aDataHashStr)
  }

  delete (aDataHashStr) {
    return this._connectionHandler._db.deleteFile(aDataHashStr)
  }

  copy (aDataHashStr, aUserHashStr) {
    return this._connectionHandler._requestHandler.buildAndSendFileRequest(aDataHashStr, aUserHashStr)
  }

  query (aQueryStr) {
    return this._connectionHandler._requestHandler.buildAndSendQuery(aQueryStr)
  }

  queryLocal (aQueryStr) {
    return this._connectionHandler._db.queryMetadata(aQueryStr)
  }
}
module.exports.Buffer = Buffer
<commit_msg>Update index.js<commit_after>'use strict'

const MultihashingAsync = require('multihashing-async')
const stream = require('pull-stream')
const Logger = require('logplease')
const deferred = require('deferred')
Logger.setLogLevel(Logger.LogLevels.DEBUG) // change to ERROR

const logger = Logger.create('UP2P', { color: Logger.Colors.Blue })

const ConnectionHandler = require('./connectionHandler')

module.exports = class UniversalPeerToPeer {

  constructor (aFileMetadataHandler, aPeerId) {
    this._connectionHandler

    let self = this
    return new ConnectionHandler(aFileMetadataHandler, aPeerId) // returns a promise that resolves to the connectionHandler after all async tasks are completed
      .then((connectionHandler) => {
        self._connectionHandler = connectionHandler
        return deferred(self)
      })
  }

  start () {
    return deferred.promisify(this._connectionHandler._node.start.bind(this._connectionHandler._node))()
  }

  stop () {
    return deferred.promisify(this._connectionHandler._node.stop.bind(this._connectionHandler._node))()
  }

  connect (aUserHashStr) {
    var ma = '/libp2p-webrtc-star/ip4/127.0.0.1/tcp/15555/ws/ipfs/' + aUserHashStr
    logger.debug('Attempting to connect to ' + aUserHashStr)
    return this._connectionHandler.connect(ma)
  }
  disconnect (aUserHashStr) {
    return this._connectionHandler.disconnect(aUserHashStr)
  }
  publish (aData, aMetadata) {
    var self = this
    return deferred.promisify(MultihashingAsync)(Buffer(aData), 'sha2-256')
      .then((mh) => {
        var hash = MultihashingAsync.multihash.toB58String(mh)
        var def = deferred()
        stream(
          stream.once(aData),
          self._connectionHandler._db.getFileWriter(hash, function () {
            def.resolve(hash)
          })
        )
        return def.promise.then(() => self._connectionHandler._db.storeMetadata(hash, aMetadata)).then(() => hash)
      })
  }

  view (aDataHashStr) {
    return this._connectionHandler._db.getFile(aDataHashStr)
  }

  delete (aDataHashStr) {
    return this._connectionHandler._db.deleteFile(aDataHashStr)
  }

  copy (aDataHashStr, aUserHashStr) {
    return this._connectionHandler._requestHandler.buildAndSendFileRequest(aDataHashStr, aUserHashStr)
  }

  query (aQueryStr) {
    return this._connectionHandler._requestHandler.buildAndSendQuery(aQueryStr)
  }

  queryLocal (aQueryStr) {
    return this._connectionHandler._db.queryMetadata(aQueryStr)
  }
}
module.exports.Buffer = Buffer
<|endoftext|>


<commit_before>/**
 * Main AngularJS Web Application
 */
var app = angular.module('myWebsite', ['ngRoute']);

/**
 * Configure the Routes
 */
app.config(['$routeProvider', function ($routeProvider) {
  $routeProvider
    .when("/", {
      templateUrl: "app/home/home.html", controller: "HomeCtrl"
    })
    .otherwise("/404", {templateUrl: "app/404.html"});
}]);<commit_msg>otherwise fix<commit_after>/**
 * Main AngularJS Web Application
 */
var app = angular.module('myWebsite', ['ngRoute']);

/**
 * Configure the Routes
 */
app.config(['$routeProvider', function ($routeProvider) {
  $routeProvider
    .when("/", {
      templateUrl: "app/home/home.html", controller: "HomeCtrl"
    })

    .when("/404", {templateUrl: "app/404.html"})
    .otherwise({redirectTo: '/404'})
}]);<|endoftext|>


<commit_before>'use strict'

import React, { Component, PropTypes } from 'react'
import ReactDOM from 'react-dom'
import classname from 'classnames'

/* Decoraters */
import staticMethods from './decorators/staticMethods'
import windowListener from './decorators/windowListener'
import customEvent from './decorators/customEvent'
import isCapture from './decorators/isCapture'

/* Utils */
import getPosition from './utils/getPosition'
import getTipContent from './utils/getTipContent'
import { parseAria } from './utils/aria'

/* CSS */
import cssStyle from './style'

@staticMethods @windowListener @customEvent @isCapture
class ReactTooltip extends Component {

  static propTypes = {
    children: PropTypes.any,
    place: PropTypes.string,
    type: PropTypes.string,
    effect: PropTypes.string,
    offset: PropTypes.object,
    multiline: PropTypes.bool,
    border: PropTypes.bool,
    insecure: PropTypes.bool,
    class: PropTypes.string,
    id: PropTypes.string,
    html: PropTypes.bool,
    delayHide: PropTypes.number,
    delayShow: PropTypes.number,
    event: PropTypes.string,
    eventOff: PropTypes.string,
    watchWindow: PropTypes.bool,
    isCapture: PropTypes.bool,
    globalEventOff: PropTypes.string,
    getContent: PropTypes.any,
    afterShow: PropTypes.func,
    afterHide: PropTypes.func,
    disable: PropTypes.bool,
    scrollHide: PropTypes.bool,
    resizeHide: PropTypes.bool
  };

  static defaultProps = {
    insecure: true,
    resizeHide: true
  };

  constructor (props) {
    super(props)
    this.state = {
      place: 'top', // Direction of tooltip
      type: 'dark', // Color theme of tooltip
      effect: 'float', // float or fixed
      show: false,
      border: false,
      placeholder: '',
      offset: {},
      extraClass: '',
      html: false,
      delayHide: 0,
      delayShow: 0,
      event: props.event || null,
      eventOff: props.eventOff || null,
      currentEvent: null, // Current mouse event
      currentTarget: null, // Current target of mouse event
      ariaProps: parseAria(props), // aria- and role attributes
      isEmptyTip: false,
      disable: false
    }

    this.bind([
      'showTooltip',
      'updateTooltip',
      'hideTooltip',
      'globalRebuild',
      'globalShow',
      'globalHide',
      'onWindowResize'
    ])

    this.mount = true
    this.delayShowLoop = null
    this.delayHideLoop = null
    this.intervalUpdateContent = null
  }

  /**
   * For unify the bind and unbind listener
   */
  bind (methodArray) {
    methodArray.forEach(method => {
      this[method] = this[method].bind(this)
    })
  }

  componentDidMount () {
    const { insecure, resizeHide } = this.props
    if (insecure) {
      this.setStyleHeader() // Set the style to the <link>
    }
    this.bindListener() // Bind listener for tooltip
    this.bindWindowEvents(resizeHide) // Bind global event for static method
  }

  componentWillReceiveProps (props) {
    const { ariaProps } = this.state
    const newAriaProps = parseAria(props)

    const isChanged = Object.keys(newAriaProps).some(props => {
      return newAriaProps[props] !== ariaProps[props]
    })
    if (isChanged) {
      this.setState({ ariaProps: newAriaProps })
    }
  }

  componentWillUnmount () {
    this.mount = false

    this.clearTimer()

    this.unbindListener()
    this.removeScrollListener()
    this.unbindWindowEvents()
  }

  /**
   * Pick out corresponded target elements
   */
  getTargetArray (id) {
    let targetArray

    if (!id) {
      targetArray = document.querySelectorAll('[data-tip]:not([data-for])')
    } else {
      targetArray = document.querySelectorAll(`[data-tip][data-for="${id}"]`)
    }

    // targetArray is a NodeList, convert it to a real array
    // I hope I can use Object.values...
    return Object.getOwnPropertyNames(targetArray).map(key => {
      return targetArray[key]
    })
  }

  /**
   * Bind listener to the target elements
   * These listeners used to trigger showing or hiding the tooltip
   */
  bindListener () {
    const {id, globalEventOff} = this.props
    let targetArray = this.getTargetArray(id)

    targetArray.forEach(target => {
      const isCaptureMode = this.isCapture(target)
      if (target.getAttribute('currentItem') === null) {
        target.setAttribute('currentItem', 'false')
      }
      this.unbindBasicListener(target)

      if (this.isCustomEvent(target)) {
        this.customBindListener(target)
        return
      }

      target.addEventListener('mouseenter', this.showTooltip, isCaptureMode)
      if (this.state.effect === 'float') {
        target.addEventListener('mousemove', this.updateTooltip, isCaptureMode)
      }
      target.addEventListener('mouseleave', this.hideTooltip, isCaptureMode)
    })

    // Global event to hide tooltip
    if (globalEventOff) {
      window.removeEventListener(globalEventOff, this.hideTooltip)
      window.addEventListener(globalEventOff, this.hideTooltip, false)
    }
  }

  /**
   * Unbind listeners on target elements
   */
  unbindListener () {
    const {id, globalEventOff} = this.props
    const targetArray = this.getTargetArray(id)
    targetArray.forEach(target => {
      this.unbindBasicListener(target)
      if (this.isCustomEvent(target)) this.customUnbindListener(target)
    })

    if (globalEventOff) window.removeEventListener(globalEventOff, this.hideTooltip)
  }

  /**
   * Invoke this before bind listener and ummount the compont
   * it is necessary to invloke this even when binding custom event
   * so that the tooltip can switch between custom and default listener
   */
  unbindBasicListener (target) {
    const isCaptureMode = this.isCapture(target)
    target.removeEventListener('mouseenter', this.showTooltip, isCaptureMode)
    target.removeEventListener('mousemove', this.updateTooltip, isCaptureMode)
    target.removeEventListener('mouseleave', this.hideTooltip, isCaptureMode)
  }

  /**
   * When mouse enter, show the tooltip
   */
  showTooltip (e, isGlobalCall) {
    if (isGlobalCall) {
      // Don't trigger other elements belongs to other ReactTooltip
      const targetArray = this.getTargetArray(this.props.id)
      const isMyElement = targetArray.some(ele => ele === e.currentTarget)
      if (!isMyElement || this.state.show) return
    }
    // Get the tooltip content
    // calculate in this phrase so that tip width height can be detected
    const {children, multiline, getContent} = this.props
    const originTooltip = e.currentTarget.getAttribute('data-tip')
    const isMultiline = e.currentTarget.getAttribute('data-multiline') || multiline || false

    // Generate tootlip content
    let content
    if (getContent) {
      if (Array.isArray(getContent)) {
        content = getContent[0] && getContent[0]()
      } else {
        content = getContent()
      }
    }
    const placeholder = getTipContent(originTooltip, children, content, isMultiline)
    const isEmptyTip = typeof placeholder === 'string' && placeholder === '' || placeholder === null

    // If it is focus event or called by ReactTooltip.show, switch to `solid` effect
    const switchToSolid = e instanceof window.FocusEvent || isGlobalCall

    // if it need to skip adding hide listener to scroll
    let scrollHide = true
    if (e.currentTarget.getAttribute('data-scroll-hide')) {
      scrollHide = e.currentTarget.getAttribute('data-scroll-hide') === 'true'
    } else if (this.props.scrollHide != null) {
      scrollHide = this.props.scrollHide
    }

    this.setState({
      placeholder,
      isEmptyTip,
      place: e.currentTarget.getAttribute('data-place') || this.props.place || 'top',
      type: e.currentTarget.getAttribute('data-type') || this.props.type || 'dark',
      effect: switchToSolid && 'solid' || e.currentTarget.getAttribute('data-effect') || this.props.effect || 'float',
      offset: e.currentTarget.getAttribute('data-offset') || this.props.offset || {},
      html: e.currentTarget.getAttribute('data-html')
        ? e.currentTarget.getAttribute('data-html') === 'true'
        : (this.props.html || false),
      delayShow: e.currentTarget.getAttribute('data-delay-show') || this.props.delayShow || 0,
      delayHide: e.currentTarget.getAttribute('data-delay-hide') || this.props.delayHide || 0,
      border: e.currentTarget.getAttribute('data-border')
        ? e.currentTarget.getAttribute('data-border') === 'true'
        : (this.props.border || false),
      extraClass: e.currentTarget.getAttribute('data-class') || this.props.class || '',
      disable: e.currentTarget.getAttribute('data-tip-disable')
        ? e.currentTarget.getAttribute('data-tip-disable') === 'true'
        : (this.props.disable || false)
    }, () => {
      if (scrollHide) this.addScrollListener(e)
      this.updateTooltip(e)

      if (getContent && Array.isArray(getContent)) {
        this.intervalUpdateContent = setInterval(() => {
          if (this.mount) {
            const {getContent} = this.props
            const placeholder = getTipContent(originTooltip, getContent[0](), isMultiline)
            const isEmptyTip = typeof placeholder === 'string' && placeholder === ''
            this.setState({
              placeholder,
              isEmptyTip
            })
          }
        }, getContent[1])
      }
    })
  }

  /**
   * When mouse hover, updatetooltip
   */
  updateTooltip (e) {
    const {delayShow, show, isEmptyTip, disable} = this.state
    const {afterShow} = this.props
    let {placeholder} = this.state
    const delayTime = show ? 0 : parseInt(delayShow, 10)
    const eventTarget = e.currentTarget

    if (isEmptyTip || disable) return // if the tooltip is empty, disable the tooltip
    const updateState = () => {
      if (Array.isArray(placeholder) && placeholder.length > 0 || placeholder) {
        const isInvisible = !this.state.show
        this.setState({
          currentEvent: e,
          currentTarget: eventTarget,
          show: true
        }, () => {
          this.updatePosition()
          if (isInvisible && afterShow) afterShow()
        })
      }
    }

    clearTimeout(this.delayShowLoop)
    if (delayShow) {
      this.delayShowLoop = setTimeout(updateState, delayTime)
    } else {
      updateState()
    }
  }

  /**
   * When mouse leave, hide tooltip
   */
  hideTooltip (e, hasTarget) {
    const {delayHide, isEmptyTip, disable} = this.state
    const {afterHide} = this.props
    if (!this.mount) return
    if (isEmptyTip || disable) return // if the tooltip is empty, disable the tooltip
    if (hasTarget) {
      // Don't trigger other elements belongs to other ReactTooltip
      const targetArray = this.getTargetArray(this.props.id)
      const isMyElement = targetArray.some(ele => ele === e.currentTarget)
      if (!isMyElement || !this.state.show) return
    }
    const resetState = () => {
      const isVisible = this.state.show
      this.setState({
        show: false
      }, () => {
        this.removeScrollListener()
        if (isVisible && afterHide) afterHide()
      })
    }

    this.clearTimer()
    if (delayHide) {
      this.delayHideLoop = setTimeout(resetState, parseInt(delayHide, 10))
    } else {
      resetState()
    }
  }

  /**
   * Add scroll eventlistener when tooltip show
   * automatically hide the tooltip when scrolling
   */
  addScrollListener (e) {
    const isCaptureMode = this.isCapture(e.currentTarget)
    window.addEventListener('scroll', this.hideTooltip, isCaptureMode)
  }

  removeScrollListener () {
    window.removeEventListener('scroll', this.hideTooltip)
  }

  // Calculation the position
  updatePosition () {
    const {currentEvent, currentTarget, place, effect, offset} = this.state
    const node = ReactDOM.findDOMNode(this)
    const result = getPosition(currentEvent, currentTarget, node, place, effect, offset)

    if (result.isNewState) {
      // Switch to reverse placement
      return this.setState(result.newState, () => {
        this.updatePosition()
      })
    }
    // Set tooltip position
    node.style.left = result.position.left + 'px'
    node.style.top = result.position.top + 'px'
  }

  /**
   * Set style tag in header
   * in this way we can insert default css
   */
  setStyleHeader () {
    if (!document.getElementsByTagName('head')[0].querySelector('style[id="react-tooltip"]')) {
      let tag = document.createElement('style')
      tag.id = 'react-tooltip'
      tag.innerHTML = cssStyle
      document.getElementsByTagName('head')[0].appendChild(tag)
    }
  }

  /**
   * CLear all kinds of timeout of interval
   */
  clearTimer () {
    clearTimeout(this.delayShowLoop)
    clearTimeout(this.delayHideLoop)
    clearInterval(this.intervalUpdateContent)
  }

  render () {
    const {placeholder, extraClass, html, ariaProps, disable, isEmptyTip} = this.state
    let tooltipClass = classname(
      '__react_component_tooltip',
      {'show': this.state.show && !disable && !isEmptyTip},
      {'border': this.state.border},
      {'place-top': this.state.place === 'top'},
      {'place-bottom': this.state.place === 'bottom'},
      {'place-left': this.state.place === 'left'},
      {'place-right': this.state.place === 'right'},
      {'type-dark': this.state.type === 'dark'},
      {'type-success': this.state.type === 'success'},
      {'type-warning': this.state.type === 'warning'},
      {'type-error': this.state.type === 'error'},
      {'type-info': this.state.type === 'info'},
      {'type-light': this.state.type === 'light'}
    )
    if (html) {
      return (
        <div className={`${tooltipClass} ${extraClass}`}
          {...ariaProps}
          data-id='tooltip'
          dangerouslySetInnerHTML={{__html: placeholder}}>

      )
    } else {
      return (
        <div className={`${tooltipClass} ${extraClass}`}
          {...ariaProps}
          data-id='tooltip'>{placeholder}

      )
    }
  }
}

/* export default not fit for standalone, it will exports {default:...} */
module.exports = ReactTooltip
<commit_msg>fix #246 delayHide triggers when tip already hidden<commit_after>'use strict'

import React, { Component, PropTypes } from 'react'
import ReactDOM from 'react-dom'
import classname from 'classnames'

/* Decoraters */
import staticMethods from './decorators/staticMethods'
import windowListener from './decorators/windowListener'
import customEvent from './decorators/customEvent'
import isCapture from './decorators/isCapture'

/* Utils */
import getPosition from './utils/getPosition'
import getTipContent from './utils/getTipContent'
import { parseAria } from './utils/aria'

/* CSS */
import cssStyle from './style'

@staticMethods @windowListener @customEvent @isCapture
class ReactTooltip extends Component {

  static propTypes = {
    children: PropTypes.any,
    place: PropTypes.string,
    type: PropTypes.string,
    effect: PropTypes.string,
    offset: PropTypes.object,
    multiline: PropTypes.bool,
    border: PropTypes.bool,
    insecure: PropTypes.bool,
    class: PropTypes.string,
    id: PropTypes.string,
    html: PropTypes.bool,
    delayHide: PropTypes.number,
    delayShow: PropTypes.number,
    event: PropTypes.string,
    eventOff: PropTypes.string,
    watchWindow: PropTypes.bool,
    isCapture: PropTypes.bool,
    globalEventOff: PropTypes.string,
    getContent: PropTypes.any,
    afterShow: PropTypes.func,
    afterHide: PropTypes.func,
    disable: PropTypes.bool,
    scrollHide: PropTypes.bool,
    resizeHide: PropTypes.bool
  };

  static defaultProps = {
    insecure: true,
    resizeHide: true
  };

  constructor (props) {
    super(props)
    this.state = {
      place: 'top', // Direction of tooltip
      type: 'dark', // Color theme of tooltip
      effect: 'float', // float or fixed
      show: false,
      border: false,
      placeholder: '',
      offset: {},
      extraClass: '',
      html: false,
      delayHide: 0,
      delayShow: 0,
      event: props.event || null,
      eventOff: props.eventOff || null,
      currentEvent: null, // Current mouse event
      currentTarget: null, // Current target of mouse event
      ariaProps: parseAria(props), // aria- and role attributes
      isEmptyTip: false,
      disable: false
    }

    this.bind([
      'showTooltip',
      'updateTooltip',
      'hideTooltip',
      'globalRebuild',
      'globalShow',
      'globalHide',
      'onWindowResize'
    ])

    this.mount = true
    this.delayShowLoop = null
    this.delayHideLoop = null
    this.intervalUpdateContent = null
  }

  /**
   * For unify the bind and unbind listener
   */
  bind (methodArray) {
    methodArray.forEach(method => {
      this[method] = this[method].bind(this)
    })
  }

  componentDidMount () {
    const { insecure, resizeHide } = this.props
    if (insecure) {
      this.setStyleHeader() // Set the style to the <link>
    }
    this.bindListener() // Bind listener for tooltip
    this.bindWindowEvents(resizeHide) // Bind global event for static method
  }

  componentWillReceiveProps (props) {
    const { ariaProps } = this.state
    const newAriaProps = parseAria(props)

    const isChanged = Object.keys(newAriaProps).some(props => {
      return newAriaProps[props] !== ariaProps[props]
    })
    if (isChanged) {
      this.setState({ ariaProps: newAriaProps })
    }
  }

  componentWillUnmount () {
    this.mount = false

    this.clearTimer()

    this.unbindListener()
    this.removeScrollListener()
    this.unbindWindowEvents()
  }

  /**
   * Pick out corresponded target elements
   */
  getTargetArray (id) {
    let targetArray

    if (!id) {
      targetArray = document.querySelectorAll('[data-tip]:not([data-for])')
    } else {
      targetArray = document.querySelectorAll(`[data-tip][data-for="${id}"]`)
    }

    // targetArray is a NodeList, convert it to a real array
    // I hope I can use Object.values...
    return Object.getOwnPropertyNames(targetArray).map(key => {
      return targetArray[key]
    })
  }

  /**
   * Bind listener to the target elements
   * These listeners used to trigger showing or hiding the tooltip
   */
  bindListener () {
    const {id, globalEventOff} = this.props
    let targetArray = this.getTargetArray(id)

    targetArray.forEach(target => {
      const isCaptureMode = this.isCapture(target)
      if (target.getAttribute('currentItem') === null) {
        target.setAttribute('currentItem', 'false')
      }
      this.unbindBasicListener(target)

      if (this.isCustomEvent(target)) {
        this.customBindListener(target)
        return
      }

      target.addEventListener('mouseenter', this.showTooltip, isCaptureMode)
      if (this.state.effect === 'float') {
        target.addEventListener('mousemove', this.updateTooltip, isCaptureMode)
      }
      target.addEventListener('mouseleave', this.hideTooltip, isCaptureMode)
    })

    // Global event to hide tooltip
    if (globalEventOff) {
      window.removeEventListener(globalEventOff, this.hideTooltip)
      window.addEventListener(globalEventOff, this.hideTooltip, false)
    }
  }

  /**
   * Unbind listeners on target elements
   */
  unbindListener () {
    const {id, globalEventOff} = this.props
    const targetArray = this.getTargetArray(id)
    targetArray.forEach(target => {
      this.unbindBasicListener(target)
      if (this.isCustomEvent(target)) this.customUnbindListener(target)
    })

    if (globalEventOff) window.removeEventListener(globalEventOff, this.hideTooltip)
  }

  /**
   * Invoke this before bind listener and ummount the compont
   * it is necessary to invloke this even when binding custom event
   * so that the tooltip can switch between custom and default listener
   */
  unbindBasicListener (target) {
    const isCaptureMode = this.isCapture(target)
    target.removeEventListener('mouseenter', this.showTooltip, isCaptureMode)
    target.removeEventListener('mousemove', this.updateTooltip, isCaptureMode)
    target.removeEventListener('mouseleave', this.hideTooltip, isCaptureMode)
  }

  /**
   * When mouse enter, show the tooltip
   */
  showTooltip (e, isGlobalCall) {
    if (isGlobalCall) {
      // Don't trigger other elements belongs to other ReactTooltip
      const targetArray = this.getTargetArray(this.props.id)
      const isMyElement = targetArray.some(ele => ele === e.currentTarget)
      if (!isMyElement || this.state.show) return
    }
    // Get the tooltip content
    // calculate in this phrase so that tip width height can be detected
    const {children, multiline, getContent} = this.props
    const originTooltip = e.currentTarget.getAttribute('data-tip')
    const isMultiline = e.currentTarget.getAttribute('data-multiline') || multiline || false

    // Generate tootlip content
    let content
    if (getContent) {
      if (Array.isArray(getContent)) {
        content = getContent[0] && getContent[0]()
      } else {
        content = getContent()
      }
    }
    const placeholder = getTipContent(originTooltip, children, content, isMultiline)
    const isEmptyTip = typeof placeholder === 'string' && placeholder === '' || placeholder === null

    // If it is focus event or called by ReactTooltip.show, switch to `solid` effect
    const switchToSolid = e instanceof window.FocusEvent || isGlobalCall

    // if it need to skip adding hide listener to scroll
    let scrollHide = true
    if (e.currentTarget.getAttribute('data-scroll-hide')) {
      scrollHide = e.currentTarget.getAttribute('data-scroll-hide') === 'true'
    } else if (this.props.scrollHide != null) {
      scrollHide = this.props.scrollHide
    }

    // To prevent previously created timers from triggering
    this.clearTimer()

    this.setState({
      placeholder,
      isEmptyTip,
      place: e.currentTarget.getAttribute('data-place') || this.props.place || 'top',
      type: e.currentTarget.getAttribute('data-type') || this.props.type || 'dark',
      effect: switchToSolid && 'solid' || e.currentTarget.getAttribute('data-effect') || this.props.effect || 'float',
      offset: e.currentTarget.getAttribute('data-offset') || this.props.offset || {},
      html: e.currentTarget.getAttribute('data-html')
        ? e.currentTarget.getAttribute('data-html') === 'true'
        : (this.props.html || false),
      delayShow: e.currentTarget.getAttribute('data-delay-show') || this.props.delayShow || 0,
      delayHide: e.currentTarget.getAttribute('data-delay-hide') || this.props.delayHide || 0,
      border: e.currentTarget.getAttribute('data-border')
        ? e.currentTarget.getAttribute('data-border') === 'true'
        : (this.props.border || false),
      extraClass: e.currentTarget.getAttribute('data-class') || this.props.class || '',
      disable: e.currentTarget.getAttribute('data-tip-disable')
        ? e.currentTarget.getAttribute('data-tip-disable') === 'true'
        : (this.props.disable || false)
    }, () => {
      if (scrollHide) this.addScrollListener(e)
      this.updateTooltip(e)

      if (getContent && Array.isArray(getContent)) {
        this.intervalUpdateContent = setInterval(() => {
          if (this.mount) {
            const {getContent} = this.props
            const placeholder = getTipContent(originTooltip, getContent[0](), isMultiline)
            const isEmptyTip = typeof placeholder === 'string' && placeholder === ''
            this.setState({
              placeholder,
              isEmptyTip
            })
          }
        }, getContent[1])
      }
    })
  }

  /**
   * When mouse hover, updatetooltip
   */
  updateTooltip (e) {
    const {delayShow, show, isEmptyTip, disable} = this.state
    const {afterShow} = this.props
    let {placeholder} = this.state
    const delayTime = show ? 0 : parseInt(delayShow, 10)
    const eventTarget = e.currentTarget

    if (isEmptyTip || disable) return // if the tooltip is empty, disable the tooltip
    const updateState = () => {
      if (Array.isArray(placeholder) && placeholder.length > 0 || placeholder) {
        const isInvisible = !this.state.show
        this.setState({
          currentEvent: e,
          currentTarget: eventTarget,
          show: true
        }, () => {
          this.updatePosition()
          if (isInvisible && afterShow) afterShow()
        })
      }
    }

    clearTimeout(this.delayShowLoop)
    if (delayShow) {
      this.delayShowLoop = setTimeout(updateState, delayTime)
    } else {
      updateState()
    }
  }

  /**
   * When mouse leave, hide tooltip
   */
  hideTooltip (e, hasTarget) {
    const {delayHide, isEmptyTip, disable} = this.state
    const {afterHide} = this.props
    if (!this.mount) return
    if (isEmptyTip || disable) return // if the tooltip is empty, disable the tooltip
    if (hasTarget) {
      // Don't trigger other elements belongs to other ReactTooltip
      const targetArray = this.getTargetArray(this.props.id)
      const isMyElement = targetArray.some(ele => ele === e.currentTarget)
      if (!isMyElement || !this.state.show) return
    }
    const resetState = () => {
      const isVisible = this.state.show
      this.setState({
        show: false
      }, () => {
        this.removeScrollListener()
        if (isVisible && afterHide) afterHide()
      })
    }

    this.clearTimer()
    if (delayHide) {
      this.delayHideLoop = setTimeout(resetState, parseInt(delayHide, 10))
    } else {
      resetState()
    }
  }

  /**
   * Add scroll eventlistener when tooltip show
   * automatically hide the tooltip when scrolling
   */
  addScrollListener (e) {
    const isCaptureMode = this.isCapture(e.currentTarget)
    window.addEventListener('scroll', this.hideTooltip, isCaptureMode)
  }

  removeScrollListener () {
    window.removeEventListener('scroll', this.hideTooltip)
  }

  // Calculation the position
  updatePosition () {
    const {currentEvent, currentTarget, place, effect, offset} = this.state
    const node = ReactDOM.findDOMNode(this)
    const result = getPosition(currentEvent, currentTarget, node, place, effect, offset)

    if (result.isNewState) {
      // Switch to reverse placement
      return this.setState(result.newState, () => {
        this.updatePosition()
      })
    }
    // Set tooltip position
    node.style.left = result.position.left + 'px'
    node.style.top = result.position.top + 'px'
  }

  /**
   * Set style tag in header
   * in this way we can insert default css
   */
  setStyleHeader () {
    if (!document.getElementsByTagName('head')[0].querySelector('style[id="react-tooltip"]')) {
      let tag = document.createElement('style')
      tag.id = 'react-tooltip'
      tag.innerHTML = cssStyle
      document.getElementsByTagName('head')[0].appendChild(tag)
    }
  }

  /**
   * CLear all kinds of timeout of interval
   */
  clearTimer () {
    clearTimeout(this.delayShowLoop)
    clearTimeout(this.delayHideLoop)
    clearInterval(this.intervalUpdateContent)
  }

  render () {
    const {placeholder, extraClass, html, ariaProps, disable, isEmptyTip} = this.state
    let tooltipClass = classname(
      '__react_component_tooltip',
      {'show': this.state.show && !disable && !isEmptyTip},
      {'border': this.state.border},
      {'place-top': this.state.place === 'top'},
      {'place-bottom': this.state.place === 'bottom'},
      {'place-left': this.state.place === 'left'},
      {'place-right': this.state.place === 'right'},
      {'type-dark': this.state.type === 'dark'},
      {'type-success': this.state.type === 'success'},
      {'type-warning': this.state.type === 'warning'},
      {'type-error': this.state.type === 'error'},
      {'type-info': this.state.type === 'info'},
      {'type-light': this.state.type === 'light'}
    )
    if (html) {
      return (
        <div className={`${tooltipClass} ${extraClass}`}
          {...ariaProps}
          data-id='tooltip'
          dangerouslySetInnerHTML={{__html: placeholder}}>

      )
    } else {
      return (
        <div className={`${tooltipClass} ${extraClass}`}
          {...ariaProps}
          data-id='tooltip'>{placeholder}

      )
    }
  }
}

/* export default not fit for standalone, it will exports {default:...} */
module.exports = ReactTooltip
<|endoftext|>


<commit_before>// Closes the sidebar menu
$("#menu-close").click(function (e) {
	e.preventDefault();
	$("#sidebar-wrapper").toggleClass("active");
});

// Opens the sidebar menu
$("#menu-toggle").click(function (e) {
	e.preventDefault();
	$("#sidebar-wrapper").toggleClass("active");
});

// Scrolls to the selected menu item on the page
/*
$(function () {
    $('a[href*=#]:not([href=#])').click(function () {
        if (location.pathname.replace(/^\//, '') == this.pathname.replace(/^\//, '') || location.hostname == this.hostname) {

            var target = $(this.hash);
            target = target.length ? target : $('[name=' + this.hash.slice(1) + ']');
            if (target.length) {
                $('html,body').animate({
                    scrollTop: target.offset().top
                }, 1000);
                return false;
            }
        }
    });
});
*/

//ajax get water reservoir data
$(function () {
	$.ajax({
		type: "GET",
		url: "http://128.199.223.114:10080",
		dataType: "jsonp",
		success: function (data) {

            //data.data[0] 石門水庫(新北、桃園、新竹)
            ////data.data[1] 新山水庫(基隆)
            //data.data[1] 翡翠水庫(台北、新北)
            ////data.data[3] 寶山水庫(新竹)
            //data.data[2] 寶山第二水庫(新竹)
            //data.data[3] 永和山水庫(新竹、苗栗)
            //data.data[4] 明德水庫(苗栗)
            //data.data[5] 鯉魚潭水庫(苗栗、台中)
            //data.data[6] 德基水庫(台中)
            //data.data[7] 石岡壩(台中)
            //data.data[8] 霧社水庫(南投)
            //data.data[9] 日月潭水庫(南投)
            //data.data[10] 集集攔河堰(南投)
            //data.data[11] 仁義潭水庫(嘉義)
            ////data.data[13] 蘭潭水庫(嘉義)
            //data.data[12] 白河水庫(台南)
            //data.data[13] 烏山頭水庫(台南)
            //data.data[14] 曾文水庫(嘉義、台南)
            //data.data[15] 南化水庫(台南、高雄)
            //data.data[16] 阿公店水庫(高雄)
            //data.data[17] 高屏溪攔河堰(高雄)
            //data.data[18] 牡丹水庫(屏東)

            var config = liquidFillGaugeDefaultSettings();
            for (var i = 0; i < 19; i++) {
                  // if(data.data[i].immediatePercentage.replace('%','') <= 30){
                  if(data.data[i].immediateStorage.replace('%','') <= 30){

            		config.circleColor = "#FF7777";
            		config.textColor = "#FF4444";
            		config.waveTextColor = "#FFAAAA";
            		config.waveColor = "#FFDDDD";
            	} // else if(data.data[i].immediatePercentage.replace('%','') <= 60){
                  else if(data.data[i].immediateStorage.replace('%','') <= 60){
                        config.circleColor = "#808015";
                        config.textColor = "#555500";
                        config.waveTextColor = "#FFFFAA";
                        config.waveColor = "#AAAA39";
                  }
                  else {
                        config = liquidFillGaugeDefaultSettings();
                  }
                    config.waveAnimateTime = 2000;
                    config.waveHeight = 0.2;
                    config.waveCount = 1;
                  // loadLiquidFillGauge('fillgauge'+i, Number(data.data[i].immediatePercentage.replace('%','')), config);
                  loadLiquidFillGauge('fillgauge'+i, Number(data.data[i].immediateStorage.replace('%','')), config);
                /*  document.getElementById("demo").innerHTML = data.data[i].reservoirName; */

                //if(data.data[i].immediateStorage.replace('%','') <= 30){
		  $('.normalclass').on('click', function(){
		  // 把下面內容塞進 DOM
		  var picture = 'fillgauge'+i;

		  $('
<svg id="fillgauge3" height="200"></svg>
' + data.data[3].reservoirName + '

').appendTo('#tab_d');
		  });
                //}
            };
        	}
});
});
<commit_msg>Update main.js<commit_after>// Closes the sidebar menu
$("#menu-close").click(function (e) {
	e.preventDefault();
	$("#sidebar-wrapper").toggleClass("active");
});

// Opens the sidebar menu
$("#menu-toggle").click(function (e) {
	e.preventDefault();
	$("#sidebar-wrapper").toggleClass("active");
});

// Scrolls to the selected menu item on the page
/*
$(function () {
    $('a[href*=#]:not([href=#])').click(function () {
        if (location.pathname.replace(/^\//, '') == this.pathname.replace(/^\//, '') || location.hostname == this.hostname) {

            var target = $(this.hash);
            target = target.length ? target : $('[name=' + this.hash.slice(1) + ']');
            if (target.length) {
                $('html,body').animate({
                    scrollTop: target.offset().top
                }, 1000);
                return false;
            }
        }
    });
});
*/

//ajax get water reservoir data
$(function () {
	$.ajax({
		type: "GET",
		url: "http://128.199.223.114:10080",
		dataType: "jsonp",
		success: function (data) {

            //data.data[0] 石門水庫(新北、桃園、新竹)
            ////data.data[1] 新山水庫(基隆)
            //data.data[1] 翡翠水庫(台北、新北)
            ////data.data[3] 寶山水庫(新竹)
            //data.data[2] 寶山第二水庫(新竹)
            //data.data[3] 永和山水庫(新竹、苗栗)
            //data.data[4] 明德水庫(苗栗)
            //data.data[5] 鯉魚潭水庫(苗栗、台中)
            //data.data[6] 德基水庫(台中)
            //data.data[7] 石岡壩(台中)
            //data.data[8] 霧社水庫(南投)
            //data.data[9] 日月潭水庫(南投)
            //data.data[10] 集集攔河堰(南投)
            //data.data[11] 仁義潭水庫(嘉義)
            ////data.data[13] 蘭潭水庫(嘉義)
            //data.data[12] 白河水庫(台南)
            //data.data[13] 烏山頭水庫(台南)
            //data.data[14] 曾文水庫(嘉義、台南)
            //data.data[15] 南化水庫(台南、高雄)
            //data.data[16] 阿公店水庫(高雄)
            //data.data[17] 高屏溪攔河堰(高雄)
            //data.data[18] 牡丹水庫(屏東)

            var config = liquidFillGaugeDefaultSettings();
            for (var i = 0; i < 19; i++) {
                  // if(data.data[i].immediatePercentage.replace('%','') <= 30){
                  if(data.data[i].immediateStorage.replace('%','') <= 30){

            		config.circleColor = "#FF7777";
            		config.textColor = "#FF4444";
            		config.waveTextColor = "#FFAAAA";
            		config.waveColor = "#FFDDDD";
            	} // else if(data.data[i].immediatePercentage.replace('%','') <= 60){
                  else if(data.data[i].immediateStorage.replace('%','') <= 60){
                        config.circleColor = "#808015";
                        config.textColor = "#555500";
                        config.waveTextColor = "#FFFFAA";
                        config.waveColor = "#AAAA39";
                  }
                  else {
                        config = liquidFillGaugeDefaultSettings();
                  }
                    config.waveAnimateTime = 2000;
                    config.waveHeight = 0.2;
                    config.waveCount = 1;
                  // loadLiquidFillGauge('fillgauge'+i, Number(data.data[i].immediatePercentage.replace('%','')), config);
                  loadLiquidFillGauge('fillgauge'+i, Number(data.data[i].immediateStorage.replace('%','')), config);
                /*  document.getElementById("demo").innerHTML = data.data[i].reservoirName; */

                if(data.data[i].immediateStorage.replace('%','') <= 30){
		  $('.normalclass').on('click', function(){
		  // 把下面內容塞進 DOM
		  var picture = 'fillgauge'+i;

		  $('
<svg id="fillgauge3" height="200"></svg>
' + data.data[i].reservoirName + '

').appendTo('#tab_d');
		  });
                }
            };
        	}
});
});
<|endoftext|>


<commit_before>import React from 'react';
import { render } from 'react-dom';
import { createStore, applyMiddleware, compose } from 'redux';
import { Provider } from 'react-redux';
import { browserHistory } from 'react-router';
import { syncHistoryWithStore, routerMiddleware } from 'react-router-redux';
import { persistStore, autoRehydrate } from 'redux-persist';
import thunk from 'redux-thunk';
import reducers from './reducers';
import Router from './components/Router';

const preloadedState = window.__PRELOADED_STATE__;

// Allow the passed state to be garbage-collected
delete window.__PRELOADED_STATE__;

const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
const store = createStore(reducers, preloadedState, composeEnhancers(
    applyMiddleware(thunk, routerMiddleware(browserHistory)),
    autoRehydrate()
  )
);

persistStore(store, {
  keyPrefix: 'v0.0.1:',
  whitelist: [
    'profile'
  ]
});

const history = syncHistoryWithStore(browserHistory, store);

render(
  <Provider store={store}>
    <Router history={history} />
  </Provider>,
  document.getElementById('root')
);
<commit_msg>Update storage prefix<commit_after>import React from 'react';
import { render } from 'react-dom';
import { createStore, applyMiddleware, compose } from 'redux';
import { Provider } from 'react-redux';
import { browserHistory } from 'react-router';
import { syncHistoryWithStore, routerMiddleware } from 'react-router-redux';
import { persistStore, autoRehydrate } from 'redux-persist';
import thunk from 'redux-thunk';
import reducers from './reducers';
import Router from './components/Router';

const preloadedState = window.__PRELOADED_STATE__;

// Allow the passed state to be garbage-collected
delete window.__PRELOADED_STATE__;

const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
const store = createStore(reducers, preloadedState, composeEnhancers(
    applyMiddleware(thunk, routerMiddleware(browserHistory)),
    autoRehydrate()
  )
);

persistStore(store, {
  keyPrefix: 'v0.0.2:',
  whitelist: [
    'profile'
  ]
});

const history = syncHistoryWithStore(browserHistory, store);

render(
  <Provider store={store}>
    <Router history={history} />
  </Provider>,
  document.getElementById('root')
);
<|endoftext|>


<commit_before>///////////Models//////////////

//This object can represent either a whitelist or blacklist for a given user. On an update send results to server to update stored data. On intialization set is synced with server. Should allow offline syncing in the future.
var FilterSet = Backbone.Model.extend({

    defaults : {
        'set' : {},
        'type' : ''
    },

    initialize : function() {
        _.bindAll(this); //allow access to 'this' in callbacks with 'this' meaning the object not the context of the callback
        this.syncSet();
    },

    getType : function() {
        return this.get('type')
    },

    getSet : function() {
        return this.get('set')
    },

    getItem : function(item) {
        return this.getSet()[item]
    },

    syncSet : function() {
        return
        var payload = {
            'type' : this.getType(),
        };
        var url = this.urlSync();
        /*
            we send the server the type of set we want to sync and reset our set to be what the server has. This allows access across computers/entensions
        */
        $.post(url, payload, function(res){
            if (res.success) {
                this.set({
                    'set' : res.set,
                });
            }
        });
    },

    addItem : function(item) {
        var set = this.getSet();
        set[item] = item;

        var payload = {
            'type' : this.getType(),
            'action' : 'add',
            'item': item,
        }
        this.updateSet(payload);
    },

    rmItem : function(item) {
        var set = this.getSet();
        delete set[item];
        var payload = {
            'type' : this.getType(),
            'action' : 'rm',
            'item': item,
        }
        this.updateSet(payload);
    },

    updateSet : function(payload) {
        return //tmp for dev
        var url = this.urlUpdateSet();
        $.post(url, payload, function(res) {
            return this.res.success //return true or false maybe we have a gui update here.
        });
    },

    urlSync : function() {
        return baseUrl //+ todo
    },

    urlUpdateSet : function() {
        return baseUrl // + todo
    },
});

//User object holds the status of the user, the cookie from the server, preferences for eyebrowse, whitelist, blacklist, etc
var User = Backbone.Model.extend({
    defaults: {
        'loggedIn' : false,
        'whitelist' : new FilterSet({
            'type' : 'whitelist',
        }),  // for dev lets use a blacklist since its easier to exclude only a few
        'blacklist' : new FilterSet({
            'type' : 'blacklist',
        }),
    },
    initialize : function() {
        _.bindAll(this); //allow access to 'this' in callbacks with 'this' meaning the object not the context of the callback
        /*
            debug is a dictionary of the following form:
            debug = {
                whitelist : [url1, url2, ...],
                blacklist : [url1, url2, ...]
            }
            This allows for debugging of whitelist/blacklist functionality
        */
        var debug = this.get('debug');
        if (debug != undefined) {
            this.initDefaults(debug);
        }
    },

    //set the default whitelist and blacklist values
    initDefaults : function(debug) {
        var whitelist = this.getWhitelist();
        var blacklist = this.getBlackList();
        $.each(debug.whitelist, function(index, item) {
            whitelist.addItem(item);
        });
        $.each(debug.blacklist, function(index, item) {
            blacklist.addItem(item);
        });
    },

    getWhitelist : function() {
        return this.get('whitelist')
    },

    getBlackList : function() {
        return this.get('blacklist')
    },

    isLoggedIn : function() {
        return this.get('loggedIn')
    },

    //when the user is logged in set the boolean to give logged in views.
    setLogin : function(status) {
        this.set({
            'loggedIn': status,
        });
    },

    //check if a url is in the blacklist
    inBlackList : function(url) {
        return this.inSet('blacklist', url)
    },

    //check if a url is in the whitelise
    inWhitelist : function(url) {
        return this.inSet('whitelist', url)
    },

    //check if url is in a set (either whitelist or blacklist)
    // documentation for URL.js : http://medialize.github.com/URI.js/docs.html
    inSet : function(setType, url) {
        var set = this.get(setType);
        var uri = new URI(url)
        var hostname = uri.hostname();
        var protocol = uri.protocol();
        return (set.getItem(hostname) != undefined || set.getItem(protocol) != undefined)
    },

    //type is whitelist or blacklist which calls update method on FilterSet object
    updateSet : function(listType, item, action) {
         if (action == 'add') {
            this.get(listType).addItem(item);
        } else if (action == 'rm') {
            this.get(listType).rmItem(item);
        }
    },
});

//
// SET UP EXTENSION VARIABLES
//

// default values
var serverURL = "http://vivid-meadow-7063.herokuapp.com/visits/myCreate";
var blacklist = JSON.stringify(["mail.google.com","www.sillypinkbunnies.com"]);
var graylist = ["www.google.com"]; // expose button to include/exclude

//  Check if these are already set to avoid overwriting.
function localSetIfNull(key,value) {
    if (localStorage.getItem(key)==null) {
        console.log(key+" not set. Setting now to "+value);
        localStorage.setItem(key,value);
    } else {
        console.log(key+" already set. Leaving it alone. Value is "+localStorage.getItem(key));
    }
}

localSetIfNull("serverURL",serverURL);
localSetIfNull("blacklist",blacklist);
localSetIfNull("graylist",graylist);

/*
    inputs:
    tabId - indentifer of tab (unique to session only)
    url - url of the tab making the request
    title - title of the webpage the tab is displaying
    event_type - whether a tab is opening or closing/navigating to a new page etc
*/
function open_item(tabId, url, faviconUrl, title, event_type) {

    var timeCheck = checkTimeDelta();
    user = new User({
        'debug' : {
            'whitelist' : [],
            'blacklist' : ['chrome', 'chrome-devtools'],
        }
    });

    //if event type is focus we need to close out the current tab
    if(!user.inBlackList(url) && timeCheck.allow) {
        if (event_type === "focus" && active_item != undefined) {
            close_item(active_item.tabId, 'blur', timeCheck.time);
        }

        //reassign the active item to be the current tab
        active_item = {
            'tabId' : tabId,
            'url' : url,
            'faviconUrl' : faviconUrl,
            'title' : title,
            'start_event' : event_type,
            'start_time' : new Date().getTime(), // milliseconds
        };

    open_items.push(active_item); // tmp for dev/testing
    update_badge();
    submit_to_server(active_item);
}

localString = localStorage['local_storage'];
parsed = JSON.parse(localString);
local_storage = parsed || []; //tmp tmp tmp //http://stackoverflow.com/questions/2153070/do-chrome-extensions-have-access-to-local-storage
/*
    There is only ever one active_item at a time so only close out the active one.
    This event will be fired when a tab is closed or unfocused but we would have already 'closed' the item so we don't want to do it again.
*/
function close_item(tabId, url, event_type, time) {
    var time = time || new Date().getTime(); // time is indefined for destroy event

    if (active_item.tabId === tabId && !user.inBlackList(url)) {
        //write to local storage
        var item = $.extend({}, active_item); //copy active_item

        item.end_event = event_type;
        item.end_time = time;
        item.tot_time = item.start_time - item.end_time;
        local_storage.push(item);
    }
}

/*
    checks if the time between the current event and the active item is greater than the delta. Default delta is 900ms
*/
function checkTimeDelta(delta) {
    var delta = delta || 900
    var now = new Date().getTime();
    var allow = true; // default to true allows active item to be set initially
    if (active_item != undefined) {
        allow = (now - active_item.start_time) > delta
    }
    console.log({
        'allow' : allow,
        'time' : now,
    });
    return {
        'allow' : allow,
        'time' : now,
    }
}

///////////Global vars/////////////
var baseUrl = "http://localhost:8000" // global website base, set to localhost for testing
//var baseUrl = "http://eyebrowse.herokuapp.com"

/////////init models///////
var user = new User({
    'debug' : {
        'whitelist' : [],
        'blacklist' : ['chrome', 'chrome-devtools'],
    }
});

// dictionary mapping all open items. Keyed on tabIds and containing all information to be written to the log.
open_items = [];
var active_item;
open_items_dict = {};

//tmp for dev
function update_badge() {

    chrome.browserAction.setBadgeText(
        {
            text: String(open_items.length + 1)
        });
}

function submit_to_server(item) {
    var xhr = new XMLHttpRequest();
    var url = localStorage.getItem("serverURL")+"?site="+encodeURI(item.title)+"&URL="+encodeURI(item.url);
    //console.log(url);
    xhr.open("GET",url,true);
    xhr.send();
    // the url is "http://vivid-meadow-7063.herokuapp.com/visits/myCreate?site="+encodeURI(pageInfo[2])+"&URL="+encodeURI(pageInfo[0]),
    // in this case, the args should be item.title and item.url
}

>>>>>>> fae24f0e83cb3a33c8443c46d47a1885ca3e9d7d
<commit_msg>Fixed two bugs in main.js. Not sure if the logic is right, but at least the syntax is.<commit_after>///////////Models//////////////

//This object can represent either a whitelist or blacklist for a given user. On an update send results to server to update stored data. On intialization set is synced with server. Should allow offline syncing in the future.

var FilterSet = Backbone.Model.extend({

    defaults : {
        'set' : {},
        'type' : ''
    },

    initialize : function() {
        _.bindAll(this); //allow access to 'this' in callbacks with 'this' meaning the object not the context of the callback
        this.syncSet();
    },

    getType : function() {
        return this.get('type')
    },

    getSet : function() {
        return this.get('set')
    },

    getItem : function(item) {
        return this.getSet()[item]
    },

    syncSet : function() {
        return
        var payload = {
            'type' : this.getType(),
        };
        var url = this.urlSync();
        /*
            we send the server the type of set we want to sync and reset our set to be what the server has. This allows access across computers/entensions
        */
        $.post(url, payload, function(res){
            if (res.success) {
                this.set({
                    'set' : res.set,
                });
            }
        });
    },

    addItem : function(item) {
        var set = this.getSet();
        set[item] = item;

        var payload = {
            'type' : this.getType(),
            'action' : 'add',
            'item': item,
        }
        this.updateSet(payload);
    },

    rmItem : function(item) {
        var set = this.getSet();
        delete set[item];
        var payload = {
            'type' : this.getType(),
            'action' : 'rm',
            'item': item,
        }
        this.updateSet(payload);
    },

    updateSet : function(payload) {
        return //tmp for dev
        var url = this.urlUpdateSet();
        $.post(url, payload, function(res) {
            return this.res.success //return true or false maybe we have a gui update here.
        });
    },

    urlSync : function() {
        return baseUrl //+ todo
    },

    urlUpdateSet : function() {
        return baseUrl // + todo
    }
});

//User object holds the status of the user, the cookie from the server, preferences for eyebrowse, whitelist, blacklist, etc
var User = Backbone.Model.extend({
    defaults: {
        'loggedIn' : false,
        'whitelist' : new FilterSet({
            'type' : 'whitelist',
        }),  // for dev lets use a blacklist since its easier to exclude only a few
        'blacklist' : new FilterSet({
            'type' : 'blacklist',
        }),
    },
    initialize : function() {
        _.bindAll(this); //allow access to 'this' in callbacks with 'this' meaning the object not the context of the callback
        /*
            debug is a dictionary of the following form:
            debug = {
                whitelist : [url1, url2, ...],
                blacklist : [url1, url2, ...]
            }
            This allows for debugging of whitelist/blacklist functionality
        */
        var debug = this.get('debug');
        if (debug != undefined) {
            this.initDefaults(debug);
        }
    },

    //set the default whitelist and blacklist values
    initDefaults : function(debug) {
        var whitelist = this.getWhitelist();
        var blacklist = this.getBlackList();
        $.each(debug.whitelist, function(index, item) {
            whitelist.addItem(item);
        });
        $.each(debug.blacklist, function(index, item) {
            blacklist.addItem(item);
        });
    },

    getWhitelist : function() {
        return this.get('whitelist')
    },

    getBlackList : function() {
        return this.get('blacklist')
    },

    isLoggedIn : function() {
        return this.get('loggedIn')
    },

    //when the user is logged in set the boolean to give logged in views.
    setLogin : function(status) {
        this.set({
            'loggedIn': status,
        });
    },

    //check if a url is in the blacklist
    inBlackList : function(url) {
        return this.inSet('blacklist', url)
    },

    //check if a url is in the whitelise
    inWhitelist : function(url) {
        return this.inSet('whitelist', url)
    },

    //check if url is in a set (either whitelist or blacklist)
    // documentation for URL.js : http://medialize.github.com/URI.js/docs.html
    inSet : function(setType, url) {
        var set = this.get(setType);
        var uri = new URI(url)
        var hostname = uri.hostname();
        var protocol = uri.protocol();
        return (set.getItem(hostname) != undefined || set.getItem(protocol) != undefined)
    },

    //type is whitelist or blacklist which calls update method on FilterSet object
    updateSet : function(listType, item, action) {
         if (action == 'add') {
            this.get(listType).addItem(item);
        } else if (action == 'rm') {
            this.get(listType).rmItem(item);
        }
    },
});

// SET UP EXTENSION VARIABLES

// default values
var serverURL = "http://vivid-meadow-7063.herokuapp.com/visits/myCreate";
var blacklist = JSON.stringify(["mail.google.com","www.sillypinkbunnies.com"]);
var graylist = ["www.google.com"]; // expose button to include/exclude

//  Check if these are already set to avoid overwriting.
function localSetIfNull(key,value) {
    if (localStorage.getItem(key)==null) {
        console.log(key+" not set. Setting now to "+value);
        localStorage.setItem(key,value);
    } else {
        console.log(key+" already set. Leaving it alone. Value is "+localStorage.getItem(key));
    }
}

localSetIfNull("serverURL",serverURL);
localSetIfNull("blacklist",blacklist);
localSetIfNull("graylist",graylist);

/*
    inputs:
    tabId - indentifer of tab (unique to session only)
    url - url of the tab making the request
    title - title of the webpage the tab is displaying
    event_type - whether a tab is opening or closing/navigating to a new page etc
*/
function open_item(tabId, url, faviconUrl, title, event_type) {

    var timeCheck = checkTimeDelta();
    user = new User({
        'debug' : {
            'whitelist' : [],
            'blacklist' : ['chrome', 'chrome-devtools'],
        }
    });

    //if event type is focus we need to close out the current tab
    if(!user.inBlackList(url) && timeCheck.allow) {
        if (event_type === "focus" && active_item != undefined) {
            close_item(active_item.tabId, 'blur', timeCheck.time);
        };
    };

    //reassign the active item to be the current tab
    active_item = {
        'tabId' : tabId,
        'url' : url,
        'faviconUrl' : faviconUrl,
        'title' : title,
        'start_event' : event_type,
        'start_time' : new Date().getTime(), // milliseconds
    };

    open_items.push(active_item); // tmp for dev/testing
    update_badge();
    submit_to_server(active_item);
}

localString = localStorage['local_storage'];
localString = (localString) ? localString : "[]"; // catch undefined case
parsed = JSON.parse(localString);
local_storage = parsed;
//local_storage = (parsed || []);

/*
    There is only ever one active_item at a time so only close out the active one.
    This event will be fired when a tab is closed or unfocused but we would have already 'closed' the item so we don't want to do it again.
*/
function close_item(tabId, url, event_type, time) {
    var time = time || new Date().getTime(); // time is indefined for destroy event

    if (active_item.tabId === tabId && !user.inBlackList(url)) {
        //write to local storage
        var item = $.extend({}, active_item); //copy active_item

        item.end_event = event_type;
        item.end_time = time;
        item.tot_time = item.start_time - item.end_time;
        local_storage.push(item);
    }
}

/*
    checks if the time between the current event and the active item is greater than the delta. Default delta is 900ms
*/
function checkTimeDelta(delta) {
    var delta = delta || 900
    var now = new Date().getTime();
    var allow = true; // default to true allows active item to be set initially
    if (active_item != undefined) {
        allow = (now - active_item.start_time) > delta
    }
    console.log({
        'allow' : allow,
        'time' : now,
    });
    return {
        'allow' : allow,
        'time' : now,
    }
}

///////////Global vars/////////////
var baseUrl = "http://localhost:8000";
// global website base, set to localhost for testing
//var baseUrl = "http://eyebrowse.herokuapp.com"

// /////////init models///////
// var user = new User({
//     'debug' : {
//         'whitelist' : [],
//         'blacklist' : ['chrome', 'chrome-devtools'],
//     }
// });

// dictionary mapping all open items. Keyed on tabIds and containing all information to be written to the log.
open_items = [];
var active_item;
open_items_dict = {};

//tmp for dev
function update_badge() {

    chrome.browserAction.setBadgeText(
        {
            text: String(open_items.length + 1)
        });
}

function submit_to_server(item) {
    var xhr = new XMLHttpRequest();
    var url = localStorage.getItem("serverURL")+"?site="+encodeURI(item.title)+"&URL="+encodeURI(item.url);
    //console.log(url);
    xhr.open("GET",url,true);
    xhr.send();
    // the url is "http://vivid-meadow-7063.herokuapp.com/visits/myCreate?site="+encodeURI(pageInfo[2])+"&URL="+encodeURI(pageInfo[0]),
    // in this case, the args should be item.title and item.url
}

<|endoftext|>


<commit_before>import * as React from 'react';
import { debounce } from 'lodash';
import PropTypes from 'prop-types';

export default class TextEllipsis extends React.Component {
  constructor(props) {
    super(props);

    this.isSupportNativeClamp = this.props.useJsOnly ? false : 'webkitLineClamp' in document.body.style;
    this.truncate = this.truncate.bind(this);
    this.throttleProcess = debounce(this.process, this.props.debounceTimeoutOnResize);
  }

  componentDidMount() {
    this.text = this.container.innerHTML;

    this.process();

    window.addEventListener('resize', this.throttleProcess, false);
  }

  componentDidUpdate() {
    this.process();
  }

  componentWillUnmount() {
    window.removeEventListener('resize', this.throttleProcess, false);
  }

  getTextWithEllipsis(start, end) {
    this.currentText = this.text.slice(start, end);
    return this.currentText + this.props.ellipsisChars;
  }

  getLineHeight() {
    const lineHeight = this.computedStyle("line-height");

    if (lineHeight === 'normal') {
      // Normal line heights vary from browser to browser. The spec recommends
      // a value between 1.0 and 1.2 of the font size. Using 1.1 to split the diff.
      return Math.ceil(parseFloat(this.computedStyle('font-size')) * 1.2);
    }
    return Math.ceil(parseFloat(lineHeight));
  }

  populateIsTuncated() {
    if (this.props.isTruncated && (elem.scrollWidth > elem.clientWidth || elem.scrollHeight > elem.clientHeight)) {
      this.props.isTruncated();
    }
  }

  truncate() {
    if (this.container.offsetHeight > this.lineHeight * this.props.lines) {
      this.container.innerHTML = this.getTextWithEllipsis(0, this.end - 1);
      this.populateIsTuncated();
    } else if (this.end >= this.textLength) {
      this.container.innerHTML = this.currentText;
    } else {
      this.container.innerHTML = this.getTextWithEllipsis(this.start, ++this.end);
      this.truncate();
    }
  }

  process() {
    if (this.isSupportNativeClamp) {
      const sty = this.container.style;
      sty.overflow = 'hidden';
      sty.textOverflow = 'ellipsis';
      sty.webkitBoxOrient = 'vertical';
      sty.display = '-webkit-box';
      sty.webkitLineClamp = this.props.lines;

      this.container.innerHTML = this.text;
      this.populateIsTuncated();
    } else {
      this.textLength = this.text.length;
      this.currentText = '';
      this.start = 0;
      this.end = this.text.length > 0 ? 1 : 0;
      this.lineHeight = this.getLineHeight();
      this.container.innerHTML = '';

      this.truncate();
    }
  }

  computedStyle(name) {
    return document.defaultView.getComputedStyle(this.container, null).getPropertyValue(name);
  }

  render() {
    return (
      React.createElement(this.props.tag, {
          ref: (node) => {
            this.container = node;
          },
          className: this.props.tagClass ? this.props.tagClass : '',
          style: { 'width': '100%', 'word-wrap': 'break-word' },
        },
        this.props.children,
      )
    );
  }
}

TextEllipsis.propTypes = {
  lines: PropTypes.number.isRequired,
  children: PropTypes.element.isRequired,
  tag: PropTypes.string,
  ellipsisChars: PropTypes.string,
  tagClass: PropTypes.string,
  debounceTimeoutOnResize: PropTypes.number,
  useJsOnly: PropTypes.bool,
  isTruncated: PropTypes.func,
};

TextEllipsis.defaultProps = {
  tag: 'div',
  ellipsisChars: '...',
  tagClass: '',
  debounceTimeoutOnResize: 200,
  useJsOnly: false,
  isTruncated: null,
};
<commit_msg>fix isTruncated called not correct<commit_after>import * as React from 'react';
import { debounce } from 'lodash';
import PropTypes from 'prop-types';

export default class TextEllipsis extends React.Component {
  constructor(props) {
    super(props);

    this.isSupportNativeClamp = this.props.useJsOnly ? false : 'webkitLineClamp' in document.body.style;
    this.truncate = this.truncate.bind(this);
    this.throttleProcess = debounce(this.process, this.props.debounceTimeoutOnResize);
  }

  componentDidMount() {
    this.text = this.container.innerHTML;

    this.process();

    window.addEventListener('resize', this.throttleProcess, false);
  }

  componentDidUpdate() {
    this.process();
  }

  componentWillUnmount() {
    window.removeEventListener('resize', this.throttleProcess, false);
  }

  getTextWithEllipsis(start, end) {
    this.currentText = this.text.slice(start, end);
    return this.currentText + this.props.ellipsisChars;
  }

  getLineHeight() {
    const lineHeight = this.computedStyle("line-height");

    if (lineHeight === 'normal') {
      // Normal line heights vary from browser to browser. The spec recommends
      // a value between 1.0 and 1.2 of the font size. Using 1.1 to split the diff.
      return Math.ceil(parseFloat(this.computedStyle('font-size')) * 1.2);
    }
    return Math.ceil(parseFloat(lineHeight));
  }

  populateIsTuncated() {
    if (this.props.isTruncated && (elem.scrollWidth > elem.clientWidth || elem.scrollHeight > elem.clientHeight)) {
      this.props.isTruncated();
    }
  }

  truncate() {
    if (this.container.offsetHeight > this.lineHeight * this.props.lines) {
      this.container.innerHTML = this.getTextWithEllipsis(0, this.end - 1);
      this.props.isTruncated && this.props.isTruncated();
    } else if (this.end >= this.textLength) {
      this.container.innerHTML = this.currentText;
    } else {
      this.container.innerHTML = this.getTextWithEllipsis(this.start, ++this.end);
      this.truncate();
    }
  }

  process() {
    if (this.isSupportNativeClamp) {
      const sty = this.container.style;
      sty.overflow = 'hidden';
      sty.textOverflow = 'ellipsis';
      sty.webkitBoxOrient = 'vertical';
      sty.display = '-webkit-box';
      sty.webkitLineClamp = this.props.lines;

      this.container.innerHTML = this.text;
      this.populateIsTuncated();
    } else {
      this.textLength = this.text.length;
      this.currentText = '';
      this.start = 0;
      this.end = this.text.length > 0 ? 1 : 0;
      this.lineHeight = this.getLineHeight();
      this.container.innerHTML = '';

      this.truncate();
    }
  }

  computedStyle(name) {
    return document.defaultView.getComputedStyle(this.container, null).getPropertyValue(name);
  }

  render() {
    return (
      React.createElement(this.props.tag, {
          ref: (node) => {
            this.container = node;
          },
          className: this.props.tagClass ? this.props.tagClass : '',
          style: { 'width': '100%', 'word-wrap': 'break-word' },
        },
        this.props.children,
      )
    );
  }
}

TextEllipsis.propTypes = {
  lines: PropTypes.number.isRequired,
  children: PropTypes.element.isRequired,
  tag: PropTypes.string,
  ellipsisChars: PropTypes.string,
  tagClass: PropTypes.string,
  debounceTimeoutOnResize: PropTypes.number,
  useJsOnly: PropTypes.bool,
  isTruncated: PropTypes.func,
};

TextEllipsis.defaultProps = {
  tag: 'div',
  ellipsisChars: '...',
  tagClass: '',
  debounceTimeoutOnResize: 200,
  useJsOnly: false,
  isTruncated: null,
};
<|endoftext|>