<commit_before>/*
import dependencies
*/

import Vue from 'vue'
import Sidebar from '@/components/Sidebar'
import ElementUI from 'element-ui'
import VueResource from 'vue-resource'
import VueLocalStorage from 'vue-localstorage'
import VueRouter from 'vue-router'

/*
inject dependencies
*/

Vue.use(ElementUI)
Vue.use(VueLocalStorage)
Vue.use(VueResource)
Vue.use(VueRouter)

/*
test
*/

describe('Sidebar.vue', () => {

    it('should successfully get report', () => {
        Vue.localStorage.set('zeusId', 1)
        const promiseCall = sinon.stub(Vue.http, 'get').returnsPromise()
        promiseCall.resolves({
            body: {
                report: {
                    accuracy: '0',
                    similar_case: '5',
                    similar_precedents: [
                        {
                            precedent: 'AZ-1',
                            outcomes: {
                                o1: true
                            },
                            facts: {
                                f1: true
                            }
                        }
                    ],
                    curves: [],
                    data_set: '1000',
                    outcomes: {
                        o1: true
                    }

                }
            }
        })
        const spy = sinon.spy(Sidebar.methods, 'createPrecedentTable')
        const vm = new Vue(Sidebar).$mount()
        vm.view()
        expect(spy.called).to.be.true
        Sidebar.methods.createPrecedentTable.restore()
        Vue.localStorage.remove('zeusId')
        Vue.http.get.restore()
    })

    it('should successfully get report', () => {
        Vue.localStorage.set('zeusId', 1)
        const promiseCall = sinon.stub(Vue.http, 'get').returnsPromise()
        promiseCall.resolves({
            body: {
                report: {
                    accuracy: '0',
                    similar_case: '5',
                    similar_precedents: [
                        {
                            precedent: 'AZ-1',
                            outcomes: {
                                o1: true
                            },
                            facts: {
                                f1: true
                            }
                        }
                    ],
                    curves: [{
                        name: {}
                    }],
                    data_set: '1000',
                    outcomes: {
                        o1: true
                    }

                }
            }
        })
        const spy = sinon.spy(Sidebar.methods, 'createPrecedentTable')
        const vm = new Vue(Sidebar).$mount()
        vm.view()
        expect(spy.called).to.be.true
        Sidebar.methods.createPrecedentTable.restore()
        Vue.localStorage.remove('zeusId')
        Vue.http.get.restore()
    })

    it('should fail to get report', () => {
        Vue.localStorage.set('zeusId', 1)
        const promiseCall = sinon.stub(Vue.http, 'get').returnsPromise()
        promiseCall.rejects()
        const spy = sinon.spy(Sidebar.methods, 'createPrecedentTable')
        const vm = new Vue(Sidebar).$mount()
        vm.view()
        expect(spy.called).to.be.false
        Sidebar.methods.createPrecedentTable.restore()
        Vue.localStorage.remove('zeusId')
        Vue.http.get.restore()
    })

    it('should successfully submit feedback', () => {
        const promiseCall = sinon.stub(Vue.http, 'post').returnsPromise()
        promiseCall.resolves()
        const vm = new Vue(Sidebar).$mount()
        vm.openFeedbackModal = true
        vm.feedback = 'Hello'
        vm.submitFeedback()
        expect(vm.connectionError).to.be.false
        Vue.http.post.restore()
    })

    it('should fail to submit feedback', () => {
        const promiseCall = sinon.stub(Vue.http, 'post').returnsPromise()
        promiseCall.rejects()
        const vm = new Vue(Sidebar).$mount()
        vm.openFeedbackModal = true
        vm.feedback = 'Hello'
        vm.submitFeedback()
        expect(vm.connectionError).to.be.true
        Vue.http.post.restore()
    })

    it('should validate empty feedback', () => {
        const vm = new Vue(Sidebar).$mount()
        vm.openFeedbackModal = true
        vm.feedback = ''
        vm.submitFeedback()
        expect(vm.connectionError).to.be.false
    })

    it('should reset chat', () => {
        const Component = Vue.extend(Sidebar)
        const vm = new Component({
            router: new VueRouter({
                routes: [
                    {
                        path: '/'
                    }
                ]
            })
        }).$mount()
        Vue.localStorage.set('zeusId', 1)
        Vue.localStorage.set('username', 'Bruce Wayne')
        Vue.localStorage.set('usertype', 'tenant')
        vm.resetChat()
        expect(Vue.localStorage.get('zeusId')).to.be.equal(null)
        expect(Vue.localStorage.get('username')).to.be.equal(null)
        expect(Vue.localStorage.get('usertype')).to.be.equal(null)
        Vue.localStorage.remove('zeusId')
        Vue.localStorage.remove('username')
        Vue.localStorage.remove('usertype')
    })

})
<commit_msg>[#45] add unit test<commit_after>/*
import dependencies
*/

import Vue from 'vue'
import Sidebar from '@/components/Sidebar'
import ElementUI from 'element-ui'
import VueResource from 'vue-resource'
import VueLocalStorage from 'vue-localstorage'
import VueRouter from 'vue-router'
import { EventBus } from '@/components/EventBus.js'

/*
inject dependencies
*/

Vue.use(ElementUI)
Vue.use(VueLocalStorage)
Vue.use(VueResource)
Vue.use(VueRouter)

/*
test
*/

describe('Sidebar.vue', () => {

    it('should successfully listen to event', () => {
        const vm = new Vue(Sidebar).$mount()
        EventBus.$emit('hideSidebar', {
            progress: 50
        })
        expect(vm.progress).to.equal(50)
    })

    it('should successfully get report', () => {
        Vue.localStorage.set('zeusId', 1)
        const promiseCall = sinon.stub(Vue.http, 'get').returnsPromise()
        promiseCall.resolves({
            body: {
                report: {
                    accuracy: '0',
                    similar_case: '5',
                    similar_precedents: [
                        {
                            precedent: 'AZ-1',
                            outcomes: {
                                o1: true
                            },
                            facts: {
                                f1: true
                            }
                        }
                    ],
                    curves: [],
                    data_set: '1000',
                    outcomes: {
                        o1: true
                    }

                }
            }
        })
        const spy = sinon.spy(Sidebar.methods, 'createPrecedentTable')
        const vm = new Vue(Sidebar).$mount()
        vm.view()
        expect(spy.called).to.be.true
        Sidebar.methods.createPrecedentTable.restore()
        Vue.localStorage.remove('zeusId')
        Vue.http.get.restore()
    })

    it('should successfully get report', () => {
        Vue.localStorage.set('zeusId', 1)
        const promiseCall = sinon.stub(Vue.http, 'get').returnsPromise()
        promiseCall.resolves({
            body: {
                report: {
                    accuracy: '0',
                    similar_case: '5',
                    similar_precedents: [
                        {
                            precedent: 'AZ-1',
                            outcomes: {
                                o1: true
                            },
                            facts: {
                                f1: true
                            }
                        }
                    ],
                    curves: [{
                        name: {}
                    }],
                    data_set: '1000',
                    outcomes: {
                        o1: true
                    }

                }
            }
        })
        const spy = sinon.spy(Sidebar.methods, 'createPrecedentTable')
        const vm = new Vue(Sidebar).$mount()
        vm.view()
        expect(spy.called).to.be.true
        Sidebar.methods.createPrecedentTable.restore()
        Vue.localStorage.remove('zeusId')
        Vue.http.get.restore()
    })

    it('should fail to get report', () => {
        Vue.localStorage.set('zeusId', 1)
        const promiseCall = sinon.stub(Vue.http, 'get').returnsPromise()
        promiseCall.rejects()
        const spy = sinon.spy(Sidebar.methods, 'createPrecedentTable')
        const vm = new Vue(Sidebar).$mount()
        vm.view()
        expect(spy.called).to.be.false
        Sidebar.methods.createPrecedentTable.restore()
        Vue.localStorage.remove('zeusId')
        Vue.http.get.restore()
    })

    it('should successfully submit feedback', () => {
        const promiseCall = sinon.stub(Vue.http, 'post').returnsPromise()
        promiseCall.resolves()
        const vm = new Vue(Sidebar).$mount()
        vm.openFeedbackModal = true
        vm.feedback = 'Hello'
        vm.submitFeedback()
        expect(vm.connectionError).to.be.false
        Vue.http.post.restore()
    })

    it('should fail to submit feedback', () => {
        const promiseCall = sinon.stub(Vue.http, 'post').returnsPromise()
        promiseCall.rejects()
        const vm = new Vue(Sidebar).$mount()
        vm.openFeedbackModal = true
        vm.feedback = 'Hello'
        vm.submitFeedback()
        expect(vm.connectionError).to.be.true
        Vue.http.post.restore()
    })

    it('should validate empty feedback', () => {
        const vm = new Vue(Sidebar).$mount()
        vm.openFeedbackModal = true
        vm.feedback = ''
        vm.submitFeedback()
        expect(vm.connectionError).to.be.false
    })

    it('should reset chat', () => {
        const Component = Vue.extend(Sidebar)
        const vm = new Component({
            router: new VueRouter({
                routes: [
                    {
                        path: '/'
                    }
                ]
            })
        }).$mount()
        Vue.localStorage.set('zeusId', 1)
        Vue.localStorage.set('username', 'Bruce Wayne')
        Vue.localStorage.set('usertype', 'tenant')
        vm.resetChat()
        expect(Vue.localStorage.get('zeusId')).to.be.equal(null)
        expect(Vue.localStorage.get('username')).to.be.equal(null)
        expect(Vue.localStorage.get('usertype')).to.be.equal(null)
        Vue.localStorage.remove('zeusId')
        Vue.localStorage.remove('username')
        Vue.localStorage.remove('usertype')
    })

})
<|endoftext|>


<commit_before>import $ from 'jquery';

import { Foundation } from '../foundation.core';
import * as CoreUtils from '../foundation.core.utils';
import { Box } from '../foundation.util.box'
import { onImagesLoaded } from '../foundation.util.imageLoader';
import { Keyboard } from '../foundation.util.keyboard';
import { MediaQuery } from '../foundation.util.mediaQuery';
import { Motion, Move } from '../foundation.util.motion';
import { Nest } from '../foundation.util.nest';
import { Timer } from '../foundation.util.timer';
import { Touch } from '../foundation.util.touch';
import { Triggers } from '../foundation.util.triggers';
import { Abide } from '../foundation.abide';
import { Accordion } from '../foundation.accordion';
import { AccordionMenu } from '../foundation.accordionMenu';
import { Drilldown } from '../foundation.drilldown';
import { Dropdown } from '../foundation.dropdown';
import { DropdownMenu } from '../foundation.dropdownMenu';
import { Equalizer } from '../foundation.equalizer';
import { Interchange } from '../foundation.interchange';
import { Magellan } from '../foundation.magellan';
import { OffCanvas } from '../foundation.offcanvas';
import { Orbit } from '../foundation.orbit';
import { ResponsiveMenu } from '../foundation.responsiveMenu';
import { ResponsiveToggle } from '../foundation.responsiveToggle';
import { Reveal } from '../foundation.reveal';
import { Slider } from '../foundation.slider';
import { SmoothScroll } from '../foundation.smoothScroll';
import { Sticky } from '../foundation.sticky';
import { Tabs } from '../foundation.tabs';
import { Toggler } from '../foundation.toggler';
import { Tooltip } from '../foundation.tooltip';
import { ResponsiveAccordionTabs } from '../foundation.responsiveAccordionTabs';

Foundation.addToJquery($);

// Add Foundation Utils to Foundation global namespace for backwards
// compatibility.
Foundation.rtl = CoreUtils.rtl;
Foundation.GetYoDigits = CoreUtils.GetYoDigits;
Foundation.transitionend = CoreUtils.transitionend;
Foundation.RegExpEscape = CoreUtils.RegExpEscape;
Foundation.onLoad = CoreUtils.onLoad;

Foundation.Box = Box;
Foundation.onImagesLoaded = onImagesLoaded;
Foundation.Keyboard = Keyboard;
Foundation.MediaQuery = MediaQuery;
Foundation.Motion = Motion;
Foundation.Move = Move;
Foundation.Nest = Nest;
Foundation.Timer = Timer;

// Touch and Triggers previously were almost purely sede effect driven,
// so no need to add it to Foundation, just init them.
Touch.init($);
Triggers.init($, Foundation);

Foundation.plugin(Abide, 'Abide');
Foundation.plugin(Accordion, 'Accordion');
Foundation.plugin(AccordionMenu, 'AccordionMenu');
Foundation.plugin(Drilldown, 'Drilldown');
Foundation.plugin(Dropdown, 'Dropdown');
Foundation.plugin(DropdownMenu, 'DropdownMenu');
Foundation.plugin(Equalizer, 'Equalizer');
Foundation.plugin(Interchange, 'Interchange');
Foundation.plugin(Magellan, 'Magellan');
Foundation.plugin(OffCanvas, 'OffCanvas');
Foundation.plugin(Orbit, 'Orbit');
Foundation.plugin(ResponsiveMenu, 'ResponsiveMenu');
Foundation.plugin(ResponsiveToggle, 'ResponsiveToggle');
Foundation.plugin(Reveal, 'Reveal');
Foundation.plugin(Slider, 'Slider');
Foundation.plugin(SmoothScroll, 'SmoothScroll');
Foundation.plugin(Sticky, 'Sticky');
Foundation.plugin(Tabs, 'Tabs');
Foundation.plugin(Toggler, 'Toggler');
Foundation.plugin(Tooltip, 'Tooltip');
Foundation.plugin(ResponsiveAccordionTabs, 'ResponsiveAccordionTabs');

export {
  Foundation,
  CoreUtils,
  Box,
  onImagesLoaded,
  Keyboard,
  MediaQuery,
  Motion,
  Nest,
  Timer,
  Touch,
  Triggers,
  Abide,
  Accordion,
  AccordionMenu,
  Drilldown,
  Dropdown,
  DropdownMenu,
  Equalizer,
  Interchange,
  Magellan,
  OffCanvas,
  Orbit,
  ResponsiveMenu,
  ResponsiveToggle,
  Reveal,
  Slider,
  SmoothScroll,
  Sticky,
  Tabs,
  Toggler,
  Tooltip,
  ResponsiveAccordionTabs
}

export default Foundation;

<commit_msg>fix: initialize MediaQuery in global Foundation entry<commit_after>import $ from 'jquery';

import { Foundation } from '../foundation.core';
import * as CoreUtils from '../foundation.core.utils';
import { Box } from '../foundation.util.box'
import { onImagesLoaded } from '../foundation.util.imageLoader';
import { Keyboard } from '../foundation.util.keyboard';
import { MediaQuery } from '../foundation.util.mediaQuery';
import { Motion, Move } from '../foundation.util.motion';
import { Nest } from '../foundation.util.nest';
import { Timer } from '../foundation.util.timer';
import { Touch } from '../foundation.util.touch';
import { Triggers } from '../foundation.util.triggers';
import { Abide } from '../foundation.abide';
import { Accordion } from '../foundation.accordion';
import { AccordionMenu } from '../foundation.accordionMenu';
import { Drilldown } from '../foundation.drilldown';
import { Dropdown } from '../foundation.dropdown';
import { DropdownMenu } from '../foundation.dropdownMenu';
import { Equalizer } from '../foundation.equalizer';
import { Interchange } from '../foundation.interchange';
import { Magellan } from '../foundation.magellan';
import { OffCanvas } from '../foundation.offcanvas';
import { Orbit } from '../foundation.orbit';
import { ResponsiveMenu } from '../foundation.responsiveMenu';
import { ResponsiveToggle } from '../foundation.responsiveToggle';
import { Reveal } from '../foundation.reveal';
import { Slider } from '../foundation.slider';
import { SmoothScroll } from '../foundation.smoothScroll';
import { Sticky } from '../foundation.sticky';
import { Tabs } from '../foundation.tabs';
import { Toggler } from '../foundation.toggler';
import { Tooltip } from '../foundation.tooltip';
import { ResponsiveAccordionTabs } from '../foundation.responsiveAccordionTabs';

Foundation.addToJquery($);

// Add Foundation Utils to Foundation global namespace for backwards
// compatibility.
Foundation.rtl = CoreUtils.rtl;
Foundation.GetYoDigits = CoreUtils.GetYoDigits;
Foundation.transitionend = CoreUtils.transitionend;
Foundation.RegExpEscape = CoreUtils.RegExpEscape;
Foundation.onLoad = CoreUtils.onLoad;

Foundation.Box = Box;
Foundation.onImagesLoaded = onImagesLoaded;
Foundation.Keyboard = Keyboard;
Foundation.MediaQuery = MediaQuery;
Foundation.Motion = Motion;
Foundation.Move = Move;
Foundation.Nest = Nest;
Foundation.Timer = Timer;

// Touch and Triggers previously were almost purely sede effect driven,
// so no need to add it to Foundation, just init them.
Touch.init($);
Triggers.init($, Foundation);
MediaQuery._init();

Foundation.plugin(Abide, 'Abide');
Foundation.plugin(Accordion, 'Accordion');
Foundation.plugin(AccordionMenu, 'AccordionMenu');
Foundation.plugin(Drilldown, 'Drilldown');
Foundation.plugin(Dropdown, 'Dropdown');
Foundation.plugin(DropdownMenu, 'DropdownMenu');
Foundation.plugin(Equalizer, 'Equalizer');
Foundation.plugin(Interchange, 'Interchange');
Foundation.plugin(Magellan, 'Magellan');
Foundation.plugin(OffCanvas, 'OffCanvas');
Foundation.plugin(Orbit, 'Orbit');
Foundation.plugin(ResponsiveMenu, 'ResponsiveMenu');
Foundation.plugin(ResponsiveToggle, 'ResponsiveToggle');
Foundation.plugin(Reveal, 'Reveal');
Foundation.plugin(Slider, 'Slider');
Foundation.plugin(SmoothScroll, 'SmoothScroll');
Foundation.plugin(Sticky, 'Sticky');
Foundation.plugin(Tabs, 'Tabs');
Foundation.plugin(Toggler, 'Toggler');
Foundation.plugin(Tooltip, 'Tooltip');
Foundation.plugin(ResponsiveAccordionTabs, 'ResponsiveAccordionTabs');

export {
  Foundation,
  CoreUtils,
  Box,
  onImagesLoaded,
  Keyboard,
  MediaQuery,
  Motion,
  Nest,
  Timer,
  Touch,
  Triggers,
  Abide,
  Accordion,
  AccordionMenu,
  Drilldown,
  Dropdown,
  DropdownMenu,
  Equalizer,
  Interchange,
  Magellan,
  OffCanvas,
  Orbit,
  ResponsiveMenu,
  ResponsiveToggle,
  Reveal,
  Slider,
  SmoothScroll,
  Sticky,
  Tabs,
  Toggler,
  Tooltip,
  ResponsiveAccordionTabs
}

export default Foundation;

<|endoftext|>


<commit_before>/**
 * @license
 * Copyright 2015 Google Inc. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

CLASS({
  name: 'Memento',
  package: 'foam.demos',
  traits: [
    'foam.memento.MemorableTrait'
  ],

  requires: [
    'foam.demos.MemorableObject',
    'foam.demos.SubMemorableObject',
    'foam.memento.FragmentMementoMgr',
    'foam.ui.JSView'
  ],

  properties: [
    {
      name: 'object',
      memorable: true,
      view: 'foam.ui.DetailView',
      factory: function() {
        return this.MemorableObject.create({
          view: 'tree',
          subProperty: this.SubMemorableObject.create({
            title: 'hello',
            mode: 'read-write',
            zoomLevel: 0.543
          })
        });
      }
    },
    {
      name: 'memento',
      view: 'foam.ui.JSView',
      hidden: false
    },
    {
      name: 'mementoMgr',
      hidden: true,
      factory: function() {
        return this.FragmentMementoMgr.create({
          mementoValue: this.memento$
        });
      }
    }

  ]
});
<commit_msg>Fix memento demo.<commit_after>/**
 * @license
 * Copyright 2015 Google Inc. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

CLASS({
  name: 'Memento',
  package: 'foam.demos',
  traits: [
    'foam.memento.MemorableTrait'
  ],

  requires: [
    'foam.demos.MemorableObject',
    'foam.demos.SubMemorableObject',
    'foam.memento.FragmentMementoMgr',
    'foam.ui.JSView',
    'foam.ui.StringArrayView'
  ],

  properties: [
    {
      name: 'object',
      memorable: true,
      view: 'foam.ui.DetailView',
      factory: function() {
        return this.MemorableObject.create({
          view: 'tree',
          subProperty: this.SubMemorableObject.create({
            title: 'hello',
            mode: 'read-write',
            zoomLevel: 0.543
          })
        });
      }
    },
    {
      name: 'memento',
      view: 'foam.ui.JSView',
      hidden: false
    },
    {
      name: 'mementoMgr',
      hidden: true,
      factory: function() {
        return this.FragmentMementoMgr.create({
          mementoValue: this.memento$
        });
      }
    }

  ]
});
<|endoftext|>


<commit_before>/**
 * @license
 * Copyright 2015 Google Inc. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

CLASS({
  package: 'foam.node.ws',
  name: 'Frame',
  properties: [
    ['fin', 1],
    ['rsv1',0],
    ['rsv2',0],
    ['rsv3',0],
    ['opcode',1],
    ['mask',0],
    'maskingKey',
    'buffer',
    ['bufferPos', 0],
    ['needed', 0],
    ['framing', 'true'],
    ['finished', 'false'],
  ],
  methods: [
    function toData() {
      this.length = this.buffer.length;
      var headerSize = this.buffer.length > 65535 ? 10 :
          (this.buffer.length > 125 ? 4 : 2);

      var i = 0;
      var buffer = new Buffer(this.buffer.length + headerSize);
      // FIN = 1, RSV1-3 = 0
      buffer.writeUInt8(
        0x80 +
          this.opcode, i++);

      var length = this.length;
      if ( length > 65535 ) {
        buffer.writeUInt8(127, i++);
        buffer.writeUInt8(length >> 56, i++)
        buffer.writeUInt8(length >> 48, i++)
        buffer.writeUInt8(length >> 40, i++)
        buffer.writeUInt8(length >> 32, i++)
        buffer.writeUInt8(length >> 24, i++)
        buffer.writeUInt8(length >> 16, i++)
        buffer.writeUInt8((length & 0xff00) >> 8, i++)
        buffer.writeUInt8(length & 0xff, i++)
      } else if ( length > 125 ) {
        buffer.writeUInt8(126, i++);
        buffer.writeUInt8((length & 0xff00) >> 8, i++)
        buffer.writeUInt8(length & 0xff, i++)
      } else {
        buffer.writeUInt8(length & 0x7f, i++);
      }

      this.buffer.copy(buffer, i);

      return buffer;
    },
    function onData(data, i) {
      if ( this.framing ) {
        var byte = data.readUInt8(i++);
        this.opcode = byte & 0x0f;
        this.fin = !! ( byte & 0x80 );
        this.rsv1 = !! ( byte & 0x40 );
        this.rsv2  = !! ( byte & 0x20 );
        this.rsv3 = !! ( byte & 0x10 );

        byte = data.readUInt8(i++);
        this.mask = !! ( byte & 0x80 );
        var length = byte & 0x7f;

        if ( length == 126 ) {
          length = 0;
          byte = data.readUInt8(i++);
          length += byte << 8;
          byte = data.readUInt8(i++);
          length += byte;
        } else if ( length == 127 ) {
          length = 0;
          var tolarge = false;
          byte = data.readUInt8(i++);
          if ( byte !== 0 ) tolarge = true;
          //length += byte << 56;

          byte = data.readUInt8(i++);
          if ( byte !== 0 ) tolarge = true;
          //length += byte << 48;

          byte = data.readUInt8(i++);
          length += byte << 40;
          byte = data.readUInt8(i++);
          length += byte << 32;
          byte = data.readUInt8(i++);
          length += byte << 24;
          byte = data.readUInt8(i++);
          length += byte << 16;
          byte = data.readUInt8(i++);
          length += byte << 8;
          byte = data.readUInt8(i++);
          length += byte;

          if ( tolarge ) {
            console.error("Payload too large.");
            this.socket.end();
            return;
          }
        }
        this.length = length;
        this.buffer = new Buffer(this.length);
        this.bufferPos = 0;
        this.needed = this.length;
        this.framing = false;

        if ( this.mask ) {
          this.masking_key = [];
          byte = data.readUInt8(i++);
          this.masking_key.push(byte);
          byte = data.readUInt8(i++);
          this.masking_key.push(byte);
          byte = data.readUInt8(i++);
          this.masking_key.push(byte);
          byte = data.readUInt8(i++);
          this.masking_key.push(byte);
        }
      }

      var amount = Math.min(data.length - i, this.needed);
      data.copy(this.buffer, this.bufferPos, i, i + amount);

      if ( this.mask ) {
        for ( var j = this.bufferPos ; j < this.bufferPos + amount; j++ ) {
          this.buffer.writeUInt8(this.buffer.readUInt8(j) ^ this.masking_key[j % 4], j);
        }
      }

      this.bufferPos += amount;
      this.needed -= amount;
      i += amount;

      if ( this.needed == 0 ) {
        this.finished = true;
      }

      return i;
    }
  ]
});
<commit_msg>Fix WebSocket frame serializaiton for large packets.<commit_after>/**
 * @license
 * Copyright 2015 Google Inc. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

CLASS({
  package: 'foam.node.ws',
  name: 'Frame',
  properties: [
    ['fin', 1],
    ['rsv1',0],
    ['rsv2',0],
    ['rsv3',0],
    ['opcode',1],
    ['mask',0],
    'maskingKey',
    'buffer',
    ['bufferPos', 0],
    ['needed', 0],
    ['framing', 'true'],
    ['finished', 'false'],
  ],
  methods: [
    function toData() {
      this.length = this.buffer.length;
      var headerSize = this.buffer.length > 65535 ? 10 :
          (this.buffer.length > 125 ? 4 : 2);

      var i = 0;
      var buffer = new Buffer(this.buffer.length + headerSize);
      // FIN = 1, RSV1-3 = 0
      buffer.writeUInt8(
        0x80 +
          this.opcode, i++);

      var length = this.length;
      if ( length > 65535 ) {
        buffer.writeUInt8(127, i++);
        buffer.writeUInt8((length >> 56) & 0xff, i++)
        buffer.writeUInt8((length >> 48) & 0xff, i++)
        buffer.writeUInt8((length >> 40) & 0xff, i++)
        buffer.writeUInt8((length >> 32) & 0xff, i++)
        buffer.writeUInt8((length >> 24) & 0xff, i++)
        buffer.writeUInt8((length >> 16) & 0xff, i++)
        buffer.writeUInt8((length >> 8) & 0xff, i++)
        buffer.writeUInt8((length & 0xff), i++)
      } else if ( length > 125 ) {
        buffer.writeUInt8(126, i++);
        buffer.writeUInt8((length & 0xff00) >> 8, i++)
        buffer.writeUInt8(length & 0xff, i++)
      } else {
        buffer.writeUInt8(length & 0x7f, i++);
      }

      this.buffer.copy(buffer, i);

      return buffer;
    },
    function onData(data, i) {
      if ( this.framing ) {
        var byte = data.readUInt8(i++);
        this.opcode = byte & 0x0f;
        this.fin = !! ( byte & 0x80 );
        this.rsv1 = !! ( byte & 0x40 );
        this.rsv2  = !! ( byte & 0x20 );
        this.rsv3 = !! ( byte & 0x10 );

        byte = data.readUInt8(i++);
        this.mask = !! ( byte & 0x80 );
        var length = byte & 0x7f;

        if ( length == 126 ) {
          length = 0;
          byte = data.readUInt8(i++);
          length += byte << 8;
          byte = data.readUInt8(i++);
          length += byte;
        } else if ( length == 127 ) {
          length = 0;
          var tolarge = false;
          byte = data.readUInt8(i++);
          if ( byte !== 0 ) tolarge = true;
          //length += byte << 56;

          byte = data.readUInt8(i++);
          if ( byte !== 0 ) tolarge = true;
          //length += byte << 48;

          byte = data.readUInt8(i++);
          length += byte << 40;
          byte = data.readUInt8(i++);
          length += byte << 32;
          byte = data.readUInt8(i++);
          length += byte << 24;
          byte = data.readUInt8(i++);
          length += byte << 16;
          byte = data.readUInt8(i++);
          length += byte << 8;
          byte = data.readUInt8(i++);
          length += byte;

          if ( tolarge ) {
            console.error("Payload too large.");
            this.socket.end();
            return;
          }
        }
        this.length = length;
        this.buffer = new Buffer(this.length);
        this.bufferPos = 0;
        this.needed = this.length;
        this.framing = false;

        if ( this.mask ) {
          this.masking_key = [];
          byte = data.readUInt8(i++);
          this.masking_key.push(byte);
          byte = data.readUInt8(i++);
          this.masking_key.push(byte);
          byte = data.readUInt8(i++);
          this.masking_key.push(byte);
          byte = data.readUInt8(i++);
          this.masking_key.push(byte);
        }
      }

      var amount = Math.min(data.length - i, this.needed);
      data.copy(this.buffer, this.bufferPos, i, i + amount);

      if ( this.mask ) {
        for ( var j = this.bufferPos ; j < this.bufferPos + amount; j++ ) {
          this.buffer.writeUInt8(this.buffer.readUInt8(j) ^ this.masking_key[j % 4], j);
        }
      }

      this.bufferPos += amount;
      this.needed -= amount;
      i += amount;

      if ( this.needed == 0 ) {
        this.finished = true;
      }

      return i;
    }
  ]
});
<|endoftext|>


<commit_before>/**
 * @license
 * Copyright 2016 Google Inc. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
CLASS({
  package: 'foam.u2.md',
  name: 'Tooltip',
  extends: 'foam.u2.Element',

  imports: [
    'document',
    'setTimeout',
    'window',
  ],

  documentation: function() {/*
    Tooltips are designed to float above the document and clean themselves up.

See
    http://www.google.com/design/spec/components/tooltips.html#tooltips-usage
    for tooltip guidelines.

The idea is that you can create this popup on mouseenter, and it will
    delay the specified 400ms before appearing with the MD animation. The
    tooltip should clean itself up, when the user clicks or moves away.

  */},

  properties: [
    {
      name: 'text',
      documentation: 'Help text to show in the tooltip.',
    },
    {
      name: 'target',
      documentation: 'The tooltip will be positioned relative to this element.',
      required: true,
    },
    {
      type: 'Boolean',
      name: 'opened',
      defaultValue: false
    },
    {
      type: 'Boolean',
      name: 'closed',
      defaultValue: false
    },
  ],

  methods: [
    function init() {
      this.SUPER();

      this.document.previousTooltip_ = this;
      this.setTimeout(this.onTimeout, 400);
    },
  ],

  listeners: [
    function close() {
      this.closed = true;
      if ( this.opened ) {
        this.remove();
        this.opened = false;
      }
    },
    function onTimeout() {
      if ( this.document.previousTooltip_ !== this ) return;
      if ( this.closed ) return;
      if ( ! this.target || ! this.target.el() ) return;

      var oldTips = this.document.getElementsByClassName(this.myCls());
      for (var i = 0; i < oldTips.length; i++) {
        oldTips[i].remove();
      }

      this.target.on('mousedown', this.close);
      this.target.on('touchstart', this.close);
      this.target.on('unload', this.close);
      this.on('unload', function() {
        this.opened = false;
        this.closed = true;
      });

      this.cls(this.myCls());
      this.add(this.text);
      this.document.body.insertAdjacentHTML('beforeend', this.outerHTML);

      var targetE = this.target.el();
      var s = this.window.getComputedStyle(this.el());
      var pos = findViewportXY(targetE);
      var screenHeight = this.document.body.clientHeight;
      var scrollY = this.window.scrollY;
      var above = pos[1] - scrollY > screenHeight / 2;
      var left = pos[0] + ( targetE.clientWidth - toNum(s.width) ) / 2;
      var maxLeft = this.document.body.clientWidth + this.window.scrollX - 15 - this.el().clientWidth;
      var targetHeight = targetE.clientHeight || targetE.offsetHeight;

      this.opened = true;
      this.load();
      this.style({
        visibility: 'visible',
        top: (above ?
            pos[1] - targetHeight - 8 :
            pos[1] + targetHeight + 8) + 'px',
        left: Math.max(this.window.scrollX + 15, Math.min(maxLeft, left)) + 'px'
      });
    },
  ],

  templates: [
    function CSS() {/*
      ^ {
        background: rgba(80, 80, 80, 0.9);
        border-radius: 4px;
        color: white;
        font-size: 10pt;
        left: 0;
        padding: 5px 8px;
        position: absolute;
        top: 0;
        transform: translate3d(0, 0, 2px);
        z-index: 2000;
      }
    */},
  ]
});
<commit_msg>MD Tooltip: Compute positions correctly<commit_after>/**
 * @license
 * Copyright 2016 Google Inc. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
CLASS({
  package: 'foam.u2.md',
  name: 'Tooltip',
  extends: 'foam.u2.Element',

  imports: [
    'document',
    'setTimeout',
    'window',
  ],

  documentation: function() {/*
    Tooltips are designed to float above the document and clean themselves up.

See
    http://www.google.com/design/spec/components/tooltips.html#tooltips-usage
    for tooltip guidelines.

The idea is that you can create this popup on mouseenter, and it will
    delay the specified 400ms before appearing with the MD animation. The
    tooltip should clean itself up, when the user clicks or moves away.

  */},

  properties: [
    {
      name: 'text',
      documentation: 'Help text to show in the tooltip.',
    },
    {
      name: 'target',
      documentation: 'The tooltip will be positioned relative to this element.',
      required: true,
    },
    {
      type: 'Boolean',
      name: 'opened',
      defaultValue: false
    },
    {
      type: 'Boolean',
      name: 'closed',
      defaultValue: false
    },
  ],

  methods: [
    function init() {
      this.SUPER();

      this.document.previousTooltip_ = this;
      this.setTimeout(this.onTimeout, 400);
    },
  ],

  listeners: [
    function close() {
      this.closed = true;
      if ( this.opened ) {
        this.remove();
        this.opened = false;
      }
    },
    function onTimeout() {
      if ( this.document.previousTooltip_ !== this ) return;
      if ( this.closed ) return;
      if ( ! this.target || ! this.target.el() ) return;

      var oldTips = this.document.getElementsByClassName(this.myCls());
      for (var i = 0; i < oldTips.length; i++) {
        oldTips[i].remove();
      }

      this.target.on('mousedown', this.close);
      this.target.on('touchstart', this.close);
      this.target.on('unload', this.close);
      this.on('unload', function() {
        this.opened = false;
        this.closed = true;
      });

      this.cls(this.myCls());
      this.add(this.text);
      this.document.body.insertAdjacentHTML('beforeend', this.outerHTML);

      var targetE = this.target.el();
      var s = this.window.getComputedStyle(this.el());
      var pos = findViewportXY(targetE);
      var screenHeight = this.window.innerHeight;
      var scrollY = this.window.scrollY;
      var above = pos[1] - scrollY > screenHeight / 2;
      var left = pos[0] + ( targetE.clientWidth - toNum(s.width) ) / 2;
      var maxLeft = this.window.innerWidth + this.window.scrollX - 15 - this.el().clientWidth;
      var targetHeight = targetE.clientHeight || targetE.offsetHeight;

      this.opened = true;
      this.load();
      this.style({
        visibility: 'visible',
        top: (above ?
            pos[1] - targetHeight - 8 :
            pos[1] + targetHeight + 8) + 'px',
        left: Math.max(this.window.scrollX + 15, Math.min(maxLeft, left)) + 'px'
      });
    },
  ],

  templates: [
    function CSS() {/*
      ^ {
        background: rgba(80, 80, 80, 0.9);
        border-radius: 4px;
        color: white;
        font-size: 10pt;
        left: 0;
        padding: 5px 8px;
        position: absolute;
        top: 0;
        transform: translate3d(0, 0, 2px);
        z-index: 2000;
      }
    */},
  ]
});
<|endoftext|>


<commit_before>'use strict';

import $ from 'jquery';

import { GetYoDigits } from './foundation.util.core';
import { MediaQuery } from './foundation.util.mediaQuery';
import { Triggers } from './foundation.util.triggers';
import { Positionable } from './foundation.positionable';

/**
 * Tooltip module.
 * @module foundation.tooltip
 * @requires foundation.util.box
 * @requires foundation.util.mediaQuery
 * @requires foundation.util.triggers
 */

class Tooltip extends Positionable {
  /**
   * Creates a new instance of a Tooltip.
   * @class
   * @name Tooltip
   * @fires Tooltip#init
   * @param {jQuery} element - jQuery object to attach a tooltip to.
   * @param {Object} options - object to extend the default configuration.
   */
  _setup(element, options) {
    this.$element = element;
    this.options = $.extend({}, Tooltip.defaults, this.$element.data(), options);
    this.className = 'Tooltip'; // ie9 back compat

    this.isActive = false;
    this.isClick = false;

    // Triggers init is idempotent, just need to make sure it is initialized
    Triggers.init($);

    this._init();
  }

  /**
   * Initializes the tooltip by setting the creating the tip element, adding it's text, setting private variables and setting attributes on the anchor.
   * @private
   */
  _init() {
    MediaQuery._init();
    var elemId = this.$element.attr('aria-describedby') || GetYoDigits(6, 'tooltip');

    this.options.tipText = this.options.tipText || this.$element.attr('title');
    this.template = this.options.template ? $(this.options.template) : this._buildTemplate(elemId);

    if (this.options.allowHtml) {
      this.template.appendTo(document.body)
        .html(this.options.tipText)
        .hide();
    } else {
      this.template.appendTo(document.body)
        .text(this.options.tipText)
        .hide();
    }

    this.$element.attr({
      'title': '',
      'aria-describedby': elemId,
      'data-yeti-box': elemId,
      'data-toggle': elemId,
      'data-resize': elemId
    }).addClass(this.options.triggerClass);

    super._init();
    this._events();
  }

  _getDefaultPosition() {
    // handle legacy classnames
    var position = this.$element[0].className.match(/\b(top|left|right|bottom)\b/g);
    return position ? position[0] : 'top';
  }

  _getDefaultAlignment() {
    return 'center';
  }

  _getHOffset() {
    if(this.position === 'left' || this.position === 'right') {
      return this.options.hOffset + this.options.tooltipWidth;
    } else {
      return this.options.hOffset
    }
  }

  _getVOffset() {
    if(this.position === 'top' || this.position === 'bottom') {
      return this.options.vOffset + this.options.tooltipHeight;
    } else {
      return this.options.vOffset
    }
  }

  /**
   * builds the tooltip element, adds attributes, and returns the template.
   * @private
   */
  _buildTemplate(id) {
    var templateClasses = (`${this.options.tooltipClass} ${this.options.positionClass} ${this.options.templateClasses}`).trim();
    var $template =  $('

').addClass(templateClasses).attr({
      'role': 'tooltip',
      'aria-hidden': true,
      'data-is-active': false,
      'data-is-focus': false,
      'id': id
    });
    return $template;
  }

  /**
   * sets the position class of an element and recursively calls itself until there are no more possible positions to attempt, or the tooltip element is no longer colliding.
   * if the tooltip is larger than the screen width, default to full width - any user selected margin
   * @private
   */
  _setPosition() {
    super._setPosition(this.$element, this.template);
  }

  /**
   * reveals the tooltip, and fires an event to close any other open tooltips on the page
   * @fires Tooltip#closeme
   * @fires Tooltip#show
   * @function
   */
  show() {
    if (this.options.showOn !== 'all' && !MediaQuery.is(this.options.showOn)) {
      // console.error('The screen is too small to display this tooltip');
      return false;
    }

    var _this = this;
    this.template.css('visibility', 'hidden').show();
    this._setPosition();
    this.template.removeClass('top bottom left right').addClass(this.position)
    this.template.removeClass('align-top align-bottom align-left align-right align-center').addClass('align-' + this.alignment);

    /**
     * Fires to close all other open tooltips on the page
     * @event Closeme#tooltip
     */
    this.$element.trigger('closeme.zf.tooltip', this.template.attr('id'));

    this.template.attr({
      'data-is-active': true,
      'aria-hidden': false
    });
    _this.isActive = true;
    // console.log(this.template);
    this.template.stop().hide().css('visibility', '').fadeIn(this.options.fadeInDuration, function() {
      //maybe do stuff?
    });
    /**
     * Fires when the tooltip is shown
     * @event Tooltip#show
     */
    this.$element.trigger('show.zf.tooltip');
  }

  /**
   * Hides the current tooltip, and resets the positioning class if it was changed due to collision
   * @fires Tooltip#hide
   * @function
   */
  hide() {
    // console.log('hiding', this.$element.data('yeti-box'));
    var _this = this;
    this.template.stop().attr({
      'aria-hidden': true,
      'data-is-active': false
    }).fadeOut(this.options.fadeOutDuration, function() {
      _this.isActive = false;
      _this.isClick = false;
    });
    /**
     * fires when the tooltip is hidden
     * @event Tooltip#hide
     */
    this.$element.trigger('hide.zf.tooltip');
  }

  /**
   * adds event listeners for the tooltip and its anchor
   * TODO combine some of the listeners like focus and mouseenter, etc.
   * @private
   */
  _events() {
    var _this = this;
    var $template = this.template;
    var isFocus = false;

    if (!this.options.disableHover) {

      this.$element
      .on('mouseenter.zf.tooltip', function(e) {
        if (!_this.isActive) {
          _this.timeout = setTimeout(function() {
            _this.show();
          }, _this.options.hoverDelay);
        }
      })
      .on('mouseleave.zf.tooltip', function(e) {
        clearTimeout(_this.timeout);
        if (!isFocus || (_this.isClick && !_this.options.clickOpen)) {
          _this.hide();
        }
      });
    }

    if (this.options.clickOpen) {
      this.$element.on('mousedown.zf.tooltip', function(e) {
        e.stopImmediatePropagation();
        if (_this.isClick) {
          //_this.hide();
          // _this.isClick = false;
        } else {
          _this.isClick = true;
          if ((_this.options.disableHover || !_this.$element.attr('tabindex')) && !_this.isActive) {
            _this.show();
          }
        }
      });
    } else {
      this.$element.on('mousedown.zf.tooltip', function(e) {
        e.stopImmediatePropagation();
        _this.isClick = true;
      });
    }

    if (!this.options.disableForTouch) {
      this.$element
      .on('tap.zf.tooltip touchend.zf.tooltip', function(e) {
        _this.isActive ? _this.hide() : _this.show();
      });
    }

    this.$element.on({
      // 'toggle.zf.trigger': this.toggle.bind(this),
      // 'close.zf.trigger': this.hide.bind(this)
      'close.zf.trigger': this.hide.bind(this)
    });

    this.$element
      .on('focus.zf.tooltip', function(e) {
        isFocus = true;
        if (_this.isClick) {
          // If we're not showing open on clicks, we need to pretend a click-launched focus isn't
          // a real focus, otherwise on hover and come back we get bad behavior
          if(!_this.options.clickOpen) { isFocus = false; }
          return false;
        } else {
          _this.show();
        }
      })

      .on('focusout.zf.tooltip', function(e) {
        isFocus = false;
        _this.isClick = false;
        _this.hide();
      })

      .on('resizeme.zf.trigger', function() {
        if (_this.isActive) {
          _this._setPosition();
        }
      });
  }

  /**
   * adds a toggle method, in addition to the static show() & hide() functions
   * @function
   */
  toggle() {
    if (this.isActive) {
      this.hide();
    } else {
      this.show();
    }
  }

  /**
   * Destroys an instance of tooltip, removes template element from the view.
   * @function
   */
  _destroy() {
    this.$element.attr('title', this.template.text())
                 .off('.zf.trigger .zf.tooltip')
                 .removeClass('has-tip top right left')
                 .removeAttr('aria-describedby aria-haspopup data-disable-hover data-resize data-toggle data-tooltip data-yeti-box');

    this.template.remove();
  }
}

Tooltip.defaults = {
  disableForTouch: false,
  /**
   * Time, in ms, before a tooltip should open on hover.
   * @option
   * @type {number}
   * @default 200
   */
  hoverDelay: 200,
  /**
   * Time, in ms, a tooltip should take to fade into view.
   * @option
   * @type {number}
   * @default 150
   */
  fadeInDuration: 150,
  /**
   * Time, in ms, a tooltip should take to fade out of view.
   * @option
   * @type {number}
   * @default 150
   */
  fadeOutDuration: 150,
  /**
   * Disables hover events from opening the tooltip if set to true
   * @option
   * @type {boolean}
   * @default false
   */
  disableHover: false,
  /**
   * Optional addtional classes to apply to the tooltip template on init.
   * @option
   * @type {string}
   * @default ''
   */
  templateClasses: '',
  /**
   * Non-optional class added to tooltip templates. Foundation default is 'tooltip'.
   * @option
   * @type {string}
   * @default 'tooltip'
   */
  tooltipClass: 'tooltip',
  /**
   * Class applied to the tooltip anchor element.
   * @option
   * @type {string}
   * @default 'has-tip'
   */
  triggerClass: 'has-tip',
  /**
   * Minimum breakpoint size at which to open the tooltip.
   * @option
   * @type {string}
   * @default 'small'
   */
  showOn: 'small',
  /**
   * Custom template to be used to generate markup for tooltip.
   * @option
   * @type {string}
   * @default ''
   */
  template: '',
  /**
   * Text displayed in the tooltip template on open.
   * @option
   * @type {string}
   * @default ''
   */
  tipText: '',
  touchCloseText: 'Tap to close.',
  /**
   * Allows the tooltip to remain open if triggered with a click or touch event.
   * @option
   * @type {boolean}
   * @default true
   */
  clickOpen: true,
  /**
   * DEPRECATED Additional positioning classes, set by the JS
   * @option
   * @type {string}
   * @default ''
   */
  positionClass: '',
  /**
   * Position of tooltip. Can be left, right, bottom, top, or auto.
   * @option
   * @type {string}
   * @default 'auto'
   */
  position: 'auto',
  /**
   * Alignment of tooltip relative to anchor. Can be left, right, bottom, top, center, or auto.
   * @option
   * @type {string}
   * @default 'auto'
   */
  alignment: 'auto',
  /**
   * Allow overlap of container/window. If false, tooltip will first try to
   * position as defined by data-position and data-alignment, but reposition if
   * it would cause an overflow.  @option
   * @type {boolean}
   * @default false
   */
  allowOverlap: false,
  /**
   * Allow overlap of only the bottom of the container. This is the most common
   * behavior for dropdowns, allowing the dropdown to extend the bottom of the
   * screen but not otherwise influence or break out of the container.
   * Less common for tooltips.
   * @option
   * @type {boolean}
   * @default false
   */
  allowBottomOverlap: false,
  /**
   * Distance, in pixels, the template should push away from the anchor on the Y axis.
   * @option
   * @type {number}
   * @default 0
   */
  vOffset: 0,
  /**
   * Distance, in pixels, the template should push away from the anchor on the X axis
   * @option
   * @type {number}
   * @default 0
   */
  hOffset: 0,
  /**
   * Distance, in pixels, the template spacing auto-adjust for a vertical tooltip
   * @option
   * @type {number}
   * @default 14
   */
  tooltipHeight: 14,
  /**
   * Distance, in pixels, the template spacing auto-adjust for a horizontal tooltip
   * @option
   * @type {number}
   * @default 12
   */
  tooltipWidth: 12,
    /**
   * Allow HTML in tooltip. Warning: If you are loading user-generated content into tooltips,
   * allowing HTML may open yourself up to XSS attacks.
   * @option
   * @type {boolean}
   * @default false
   */
  allowHtml: false
};

/**
 * TODO utilize resize event trigger
 */

export {Tooltip};
<commit_msg>chore: remove the deprecated positionClass in tooltip<commit_after>'use strict';

import $ from 'jquery';

import { GetYoDigits } from './foundation.util.core';
import { MediaQuery } from './foundation.util.mediaQuery';
import { Triggers } from './foundation.util.triggers';
import { Positionable } from './foundation.positionable';

/**
 * Tooltip module.
 * @module foundation.tooltip
 * @requires foundation.util.box
 * @requires foundation.util.mediaQuery
 * @requires foundation.util.triggers
 */

class Tooltip extends Positionable {
  /**
   * Creates a new instance of a Tooltip.
   * @class
   * @name Tooltip
   * @fires Tooltip#init
   * @param {jQuery} element - jQuery object to attach a tooltip to.
   * @param {Object} options - object to extend the default configuration.
   */
  _setup(element, options) {
    this.$element = element;
    this.options = $.extend({}, Tooltip.defaults, this.$element.data(), options);
    this.className = 'Tooltip'; // ie9 back compat

    this.isActive = false;
    this.isClick = false;

    // Triggers init is idempotent, just need to make sure it is initialized
    Triggers.init($);

    this._init();
  }

  /**
   * Initializes the tooltip by setting the creating the tip element, adding it's text, setting private variables and setting attributes on the anchor.
   * @private
   */
  _init() {
    MediaQuery._init();
    var elemId = this.$element.attr('aria-describedby') || GetYoDigits(6, 'tooltip');

    this.options.tipText = this.options.tipText || this.$element.attr('title');
    this.template = this.options.template ? $(this.options.template) : this._buildTemplate(elemId);

    if (this.options.allowHtml) {
      this.template.appendTo(document.body)
        .html(this.options.tipText)
        .hide();
    } else {
      this.template.appendTo(document.body)
        .text(this.options.tipText)
        .hide();
    }

    this.$element.attr({
      'title': '',
      'aria-describedby': elemId,
      'data-yeti-box': elemId,
      'data-toggle': elemId,
      'data-resize': elemId
    }).addClass(this.options.triggerClass);

    super._init();
    this._events();
  }

  _getDefaultPosition() {
    // handle legacy classnames
    var position = this.$element[0].className.match(/\b(top|left|right|bottom)\b/g);
    return position ? position[0] : 'top';
  }

  _getDefaultAlignment() {
    return 'center';
  }

  _getHOffset() {
    if(this.position === 'left' || this.position === 'right') {
      return this.options.hOffset + this.options.tooltipWidth;
    } else {
      return this.options.hOffset
    }
  }

  _getVOffset() {
    if(this.position === 'top' || this.position === 'bottom') {
      return this.options.vOffset + this.options.tooltipHeight;
    } else {
      return this.options.vOffset
    }
  }

  /**
   * builds the tooltip element, adds attributes, and returns the template.
   * @private
   */
  _buildTemplate(id) {
    var templateClasses = (`${this.options.tooltipClass} ${this.options.templateClasses}`).trim();
    var $template =  $('

').addClass(templateClasses).attr({
      'role': 'tooltip',
      'aria-hidden': true,
      'data-is-active': false,
      'data-is-focus': false,
      'id': id
    });
    return $template;
  }

  /**
   * sets the position class of an element and recursively calls itself until there are no more possible positions to attempt, or the tooltip element is no longer colliding.
   * if the tooltip is larger than the screen width, default to full width - any user selected margin
   * @private
   */
  _setPosition() {
    super._setPosition(this.$element, this.template);
  }

  /**
   * reveals the tooltip, and fires an event to close any other open tooltips on the page
   * @fires Tooltip#closeme
   * @fires Tooltip#show
   * @function
   */
  show() {
    if (this.options.showOn !== 'all' && !MediaQuery.is(this.options.showOn)) {
      // console.error('The screen is too small to display this tooltip');
      return false;
    }

    var _this = this;
    this.template.css('visibility', 'hidden').show();
    this._setPosition();
    this.template.removeClass('top bottom left right').addClass(this.position)
    this.template.removeClass('align-top align-bottom align-left align-right align-center').addClass('align-' + this.alignment);

    /**
     * Fires to close all other open tooltips on the page
     * @event Closeme#tooltip
     */
    this.$element.trigger('closeme.zf.tooltip', this.template.attr('id'));

    this.template.attr({
      'data-is-active': true,
      'aria-hidden': false
    });
    _this.isActive = true;
    // console.log(this.template);
    this.template.stop().hide().css('visibility', '').fadeIn(this.options.fadeInDuration, function() {
      //maybe do stuff?
    });
    /**
     * Fires when the tooltip is shown
     * @event Tooltip#show
     */
    this.$element.trigger('show.zf.tooltip');
  }

  /**
   * Hides the current tooltip, and resets the positioning class if it was changed due to collision
   * @fires Tooltip#hide
   * @function
   */
  hide() {
    // console.log('hiding', this.$element.data('yeti-box'));
    var _this = this;
    this.template.stop().attr({
      'aria-hidden': true,
      'data-is-active': false
    }).fadeOut(this.options.fadeOutDuration, function() {
      _this.isActive = false;
      _this.isClick = false;
    });
    /**
     * fires when the tooltip is hidden
     * @event Tooltip#hide
     */
    this.$element.trigger('hide.zf.tooltip');
  }

  /**
   * adds event listeners for the tooltip and its anchor
   * TODO combine some of the listeners like focus and mouseenter, etc.
   * @private
   */
  _events() {
    var _this = this;
    var $template = this.template;
    var isFocus = false;

    if (!this.options.disableHover) {

      this.$element
      .on('mouseenter.zf.tooltip', function(e) {
        if (!_this.isActive) {
          _this.timeout = setTimeout(function() {
            _this.show();
          }, _this.options.hoverDelay);
        }
      })
      .on('mouseleave.zf.tooltip', function(e) {
        clearTimeout(_this.timeout);
        if (!isFocus || (_this.isClick && !_this.options.clickOpen)) {
          _this.hide();
        }
      });
    }

    if (this.options.clickOpen) {
      this.$element.on('mousedown.zf.tooltip', function(e) {
        e.stopImmediatePropagation();
        if (_this.isClick) {
          //_this.hide();
          // _this.isClick = false;
        } else {
          _this.isClick = true;
          if ((_this.options.disableHover || !_this.$element.attr('tabindex')) && !_this.isActive) {
            _this.show();
          }
        }
      });
    } else {
      this.$element.on('mousedown.zf.tooltip', function(e) {
        e.stopImmediatePropagation();
        _this.isClick = true;
      });
    }

    if (!this.options.disableForTouch) {
      this.$element
      .on('tap.zf.tooltip touchend.zf.tooltip', function(e) {
        _this.isActive ? _this.hide() : _this.show();
      });
    }

    this.$element.on({
      // 'toggle.zf.trigger': this.toggle.bind(this),
      // 'close.zf.trigger': this.hide.bind(this)
      'close.zf.trigger': this.hide.bind(this)
    });

    this.$element
      .on('focus.zf.tooltip', function(e) {
        isFocus = true;
        if (_this.isClick) {
          // If we're not showing open on clicks, we need to pretend a click-launched focus isn't
          // a real focus, otherwise on hover and come back we get bad behavior
          if(!_this.options.clickOpen) { isFocus = false; }
          return false;
        } else {
          _this.show();
        }
      })

      .on('focusout.zf.tooltip', function(e) {
        isFocus = false;
        _this.isClick = false;
        _this.hide();
      })

      .on('resizeme.zf.trigger', function() {
        if (_this.isActive) {
          _this._setPosition();
        }
      });
  }

  /**
   * adds a toggle method, in addition to the static show() & hide() functions
   * @function
   */
  toggle() {
    if (this.isActive) {
      this.hide();
    } else {
      this.show();
    }
  }

  /**
   * Destroys an instance of tooltip, removes template element from the view.
   * @function
   */
  _destroy() {
    this.$element.attr('title', this.template.text())
                 .off('.zf.trigger .zf.tooltip')
                 .removeClass('has-tip top right left')
                 .removeAttr('aria-describedby aria-haspopup data-disable-hover data-resize data-toggle data-tooltip data-yeti-box');

    this.template.remove();
  }
}

Tooltip.defaults = {
  disableForTouch: false,
  /**
   * Time, in ms, before a tooltip should open on hover.
   * @option
   * @type {number}
   * @default 200
   */
  hoverDelay: 200,
  /**
   * Time, in ms, a tooltip should take to fade into view.
   * @option
   * @type {number}
   * @default 150
   */
  fadeInDuration: 150,
  /**
   * Time, in ms, a tooltip should take to fade out of view.
   * @option
   * @type {number}
   * @default 150
   */
  fadeOutDuration: 150,
  /**
   * Disables hover events from opening the tooltip if set to true
   * @option
   * @type {boolean}
   * @default false
   */
  disableHover: false,
  /**
   * Optional addtional classes to apply to the tooltip template on init.
   * @option
   * @type {string}
   * @default ''
   */
  templateClasses: '',
  /**
   * Non-optional class added to tooltip templates. Foundation default is 'tooltip'.
   * @option
   * @type {string}
   * @default 'tooltip'
   */
  tooltipClass: 'tooltip',
  /**
   * Class applied to the tooltip anchor element.
   * @option
   * @type {string}
   * @default 'has-tip'
   */
  triggerClass: 'has-tip',
  /**
   * Minimum breakpoint size at which to open the tooltip.
   * @option
   * @type {string}
   * @default 'small'
   */
  showOn: 'small',
  /**
   * Custom template to be used to generate markup for tooltip.
   * @option
   * @type {string}
   * @default ''
   */
  template: '',
  /**
   * Text displayed in the tooltip template on open.
   * @option
   * @type {string}
   * @default ''
   */
  tipText: '',
  touchCloseText: 'Tap to close.',
  /**
   * Allows the tooltip to remain open if triggered with a click or touch event.
   * @option
   * @type {boolean}
   * @default true
   */
  clickOpen: true,
  /**
   * Position of tooltip. Can be left, right, bottom, top, or auto.
   * @option
   * @type {string}
   * @default 'auto'
   */
  position: 'auto',
  /**
   * Alignment of tooltip relative to anchor. Can be left, right, bottom, top, center, or auto.
   * @option
   * @type {string}
   * @default 'auto'
   */
  alignment: 'auto',
  /**
   * Allow overlap of container/window. If false, tooltip will first try to
   * position as defined by data-position and data-alignment, but reposition if
   * it would cause an overflow.  @option
   * @type {boolean}
   * @default false
   */
  allowOverlap: false,
  /**
   * Allow overlap of only the bottom of the container. This is the most common
   * behavior for dropdowns, allowing the dropdown to extend the bottom of the
   * screen but not otherwise influence or break out of the container.
   * Less common for tooltips.
   * @option
   * @type {boolean}
   * @default false
   */
  allowBottomOverlap: false,
  /**
   * Distance, in pixels, the template should push away from the anchor on the Y axis.
   * @option
   * @type {number}
   * @default 0
   */
  vOffset: 0,
  /**
   * Distance, in pixels, the template should push away from the anchor on the X axis
   * @option
   * @type {number}
   * @default 0
   */
  hOffset: 0,
  /**
   * Distance, in pixels, the template spacing auto-adjust for a vertical tooltip
   * @option
   * @type {number}
   * @default 14
   */
  tooltipHeight: 14,
  /**
   * Distance, in pixels, the template spacing auto-adjust for a horizontal tooltip
   * @option
   * @type {number}
   * @default 12
   */
  tooltipWidth: 12,
    /**
   * Allow HTML in tooltip. Warning: If you are loading user-generated content into tooltips,
   * allowing HTML may open yourself up to XSS attacks.
   * @option
   * @type {boolean}
   * @default false
   */
  allowHtml: false
};

/**
 * TODO utilize resize event trigger
 */

export {Tooltip};
<|endoftext|>