<commit_before>/**
 * Object observer
 */
(function (Object) {
    "use strict";
    /**
     * Observer key
     * @type {string}
     */
    var oKey = '__$$observer__';
    /**
     * Observer
     * @param key
     * @param val
     * @constructor
     */
    function Observer() {
        this.events = {};
        this.members = {};
    }

    /**
     * Get val
     * @param key
     * @returns {*}
     */
    Observer.prototype.getVal = function (key) {
        return this.members[key];
    };
    /**
     * Set val
     * @param key
     * @param val
     */
    Observer.prototype.setVal = function (key, val) {
        this.members[key] = val;
        return val;
    };
    /**
     * Destroy
     * @param key
     */
    Observer.prototype.destroy = function (ref, key) {
        this.trigger(ref, key, undefined, this.members[key]);
        delete this.members[key];
        delete this.events[key];
    }
    /**
     * Key
     * @param key
     */
    Observer.prototype.trigger = function (ref, key, nVal, oVal) {
        this.events[key].forEach(function (callback) {
            callback.call(ref, nVal, oVal, key);
        });
    }
    /**
     * Add
     * @param key
     * @param callback
     */
    Observer.prototype.add = function (key, callback) {
        if (typeof key === 'string' && typeof callback === 'function') {
            if (!this.events[key]) {
                this.events[key] = [];
            }
            this.events[key].push(callback);
        }
    }

    /**
     * Create an observer
     * @param name
     * @param callback
     */
    function createObserver(name, callback) {
        /**
         * Observer
         */
        if (!this[oKey]) {
            this[oKey] = new Observer();
        }
        /**
         * add
         */
        this[oKey].add(name, callback);
        /**
         * Observer
         * @type {Observer|*}
         */
        var observer = this[oKey],
            /**
             * Reference
             * @type {Object}
             */
            self = this,
            /**
             * is array
             * @type {*}
             */
            isArray = Object.prototype.toString.call(this[name]) === '[object Array]',
            /**
             * Mutator
             * @type {string[]}
             */
            mutators = ['pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift'],
            /**
             * Getter
             * @returns {*}
             */
            getter = function () {
                return observer.getVal(name);
            },
            /**
             * Setter
             * @param val
             */
            setter = function (val) {
                var oVal = observer.getVal(name),
                    nVal = observer.setVal(name, val);
                    observer.trigger(self, name, nVal, oVal);
            };
        /**
         * Set val
         */
        observer.setVal(name, this[name]);
        /**
         * Is array
         */
        if (isArray) {
            var value = observer.getVal(name);
            mutators.forEach(function (key) {
                var method = value[key];
                value[key] = function () {
                    var oVal = observer.getVal(name).slice();
                    method.apply(value, Array.prototype.slice.call(arguments));
                    observer.trigger(self, name, observer.getVal(name), oVal);
                }
            });
        }

        /**
         * define property
         */
        try {
            Object.defineProperty(this, name, {
                get: getter,
                set: setter,
                enumerable: true,
                configurable: true
            });
        } catch (e) {
            try {
                //firefox
                Object.prototype.__defineGetter__.call(this, name, getter);
                Object.prototype.__defineSetter__.call(this, name, setter);
            } catch (e2) {
                throw new Error("Browser don't support getters and setters", [e, e2]);
            }
        }
    }

    /**
     * Destroy
     * @param member
     */
    function destory(member, deleteMember) {
        if (!(this[oKey] instanceof Observer)) {
            return false;
        }
        var key, val;
        if (member && !(typeof member === 'boolean')) {
            if (!deleteMember) {
                // get the value
                val = this[member];
            }
            // destroy the member
            this[oKey].destroy(this, member);
            delete this[member];
            if (!deleteMember) {
                this[member] = val;
            }
        } else {
            if (typeof member === 'boolean') {
                deleteMember = member;
            }
            delete this[oKey];
            for (key in this) {
                if (!deleteMember) {
                    val = this[key];
                }
                delete this[key];
                if (!deleteMember) {
                    this[key] = val;
                }

            }
        }
    }

    /**
     * Don't observe values in exclude
     * @param name
     * @param callback
     * @param exclude Array of keys
     */
    function observe(name, callback, exclude) {
        var key;
        if (typeof name === 'string' && typeof callback === 'function') {
            if (Array.isArray(exclude)) {
                if (exclude.indexOf(name) === -1) {
                    createObserver.call(this, name, callback);
                }
            } else {
                createObserver.call(this, name, callback);
            }
        } else if (typeof name === 'function') {
            for (key in this) {
                if (this.hasOwnProperty(key) && key !== oKey) {
                    observe.call(this, key, name, callback);
                }
            }
        } else {
            throw new Error('Object.observer is not called correctly parameters are not provided correctly')
        }

    }

    /**
     * Observer
     */
    Object.observe = function (ob) {
        var args = Array.prototype.slice.call(arguments, 1);
        observe.apply(ob, args);
    }
    /**
     * Destroy
     * @param ob
     */
    Object.destroy = function (ob) {
        var args = Array.prototype.slice.call(arguments, 1);
        destory.apply(ob, args);
    }

}(Object));

<commit_msg>Update of exclude<commit_after>/**
 * Object observer
 */
(function (Object) {
    "use strict";
    /**
     * Observer key
     * @type {string}
     */
    var oKey = '__$$observer__';
    /**
     * Observer
     * @param key
     * @param val
     * @constructor
     */
    function Observer() {
        this.events = {};
        this.members = {};
    }

    /**
     * Get val
     * @param key
     * @returns {*}
     */
    Observer.prototype.getVal = function (key) {
        return this.members[key];
    };
    /**
     * Set val
     * @param key
     * @param val
     */
    Observer.prototype.setVal = function (key, val) {
        this.members[key] = val;
        return val;
    };
    /**
     * Destroy
     * @param key
     */
    Observer.prototype.destroy = function (ref, key) {
        this.trigger(ref, key, undefined, this.members[key]);
        delete this.members[key];
        delete this.events[key];
    }
    /**
     * Key
     * @param key
     */
    Observer.prototype.trigger = function (ref, key, nVal, oVal) {
        this.events[key].forEach(function (callback) {
            callback.call(ref, nVal, oVal, key);
        });
    }
    /**
     * Add
     * @param key
     * @param callback
     */
    Observer.prototype.add = function (key, callback) {
        if (typeof key === 'string' && typeof callback === 'function') {
            if (!this.events[key]) {
                this.events[key] = [];
            }
            this.events[key].push(callback);
        }
    }

    /**
     * Create an observer
     * @param name
     * @param callback
     */
    function createObserver(name, callback) {
        /**
         * Observer
         */
        if (!this[oKey]) {
            this[oKey] = new Observer();
        }
        /**
         * add
         */
        this[oKey].add(name, callback);
        /**
         * Observer
         * @type {Observer|*}
         */
        var observer = this[oKey],
            /**
             * Reference
             * @type {Object}
             */
            self = this,
            /**
             * is array
             * @type {*}
             */
            isArray = Object.prototype.toString.call(this[name]) === '[object Array]',
            /**
             * Mutator
             * @type {string[]}
             */
            mutators = ['pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift'],
            /**
             * Getter
             * @returns {*}
             */
            getter = function () {
                return observer.getVal(name);
            },
            /**
             * Setter
             * @param val
             */
            setter = function (val) {
                var oVal = observer.getVal(name),
                    nVal = observer.setVal(name, val);
                    observer.trigger(self, name, nVal, oVal);
            };
        /**
         * Set val
         */
        observer.setVal(name, this[name]);
        /**
         * Is array
         */
        if (isArray) {
            var value = observer.getVal(name);
            mutators.forEach(function (key) {
                var method = value[key];
                value[key] = function () {
                    var oVal = observer.getVal(name).slice();
                    method.apply(value, Array.prototype.slice.call(arguments));
                    observer.trigger(self, name, observer.getVal(name), oVal);
                }
            });
        }

        /**
         * define property
         */
        try {
            Object.defineProperty(this, name, {
                get: getter,
                set: setter,
                enumerable: true,
                configurable: true
            });
        } catch (e) {
            try {
                //firefox
                Object.prototype.__defineGetter__.call(this, name, getter);
                Object.prototype.__defineSetter__.call(this, name, setter);
            } catch (e2) {
                throw new Error("Browser don't support getters and setters", [e, e2]);
            }
        }
    }

    /**
     * Destroy
     * @param member
     */
    function destory(member, deleteMember) {
        if (!(this[oKey] instanceof Observer)) {
            return false;
        }
        var key, val;
        if (member && !(typeof member === 'boolean')) {
            if (!deleteMember) {
                // get the value
                val = this[member];
            }
            // destroy the member
            this[oKey].destroy(this, member);
            delete this[member];
            if (!deleteMember) {
                this[member] = val;
            }
        } else {
            if (typeof member === 'boolean') {
                deleteMember = member;
            }
            delete this[oKey];
            for (key in this) {
                if (!deleteMember) {
                    val = this[key];
                }
                delete this[key];
                if (!deleteMember) {
                    this[key] = val;
                }

            }
        }
    }

    /**
     * Don't observe values in exclude
     * @param name
     * @param callback
     * @param exclude Array of keys
     */
    function observe(name, callback, exclude) {
        var key;
        if (typeof name === 'string' && typeof callback === 'function') {
            if (Array.isArray(exclude) && !Array.isArray(this)) {
                if (exclude.indexOf(name) === -1) {
                    createObserver.call(this, name, callback);
                }
            } else {
                createObserver.call(this, name, callback);
            }
        } else if (typeof name === 'function') {
            for (key in this) {
                if (this.hasOwnProperty(key) && key !== oKey) {
                    observe.call(this, key, name, callback);
                }
            }
        } else {
            throw new Error('Object.observer is not called correctly parameters are not provided correctly')
        }

    }

    /**
     * Observer
     */
    Object.observe = function (ob) {
        var args = Array.prototype.slice.call(arguments, 1);
        observe.apply(ob, args);
    }
    /**
     * Destroy
     * @param ob
     */
    Object.destroy = function (ob) {
        var args = Array.prototype.slice.call(arguments, 1);
        destory.apply(ob, args);
    }

}(Object));

<|endoftext|>


<commit_before>
/* global Vue, global document, global location*/

'use strict';

var publicDomain;

var vm = new Vue({

  el: '#addInstance',

  ready : function () {

    var self = this;

    self.$http.get('/-/v3/config.js').then(function (result) {
      self.$set('publicDomain', result.data.publicDomain);
      publicDomain = result.data.publicDomain;
    }, console.error);
  },

  methods : {

    displayFormAddInstance : function (event) {
      document.getElementById('modal-add-instance').style.display = 'block';
    },

    cancelAddInstance : function (event) {
      document.getElementById('modal-add-instance').style.display = 'none';
    },

    addInstance : function (event) {
      this.longName = document.getElementById('inputLongName').value;
      this.project = document.getElementById('inputProject').value;
      this.study = document.getElementById('inputStudy').value;
      this.version = document.getElementById('inputVersion').value;

      document.getElementById('save').style.display = 'none';
      document.getElementById('close_modal').style.display = 'none';
      document.getElementById('loader').style.display = 'block';

      if (this.longName == '') { this.longName = 'Free comment of '+this.technicalName; }

      var data = {
        'longName' : this.longName,
        'project' : this.project,
        'version' : this.version,
        'study': this.study,
        'technicalName' :  this.technicalName,
        'app' : document.getElementById('app').value
      };
      this.$http.post('/-/v1/instances', data).then(function (result) {
        if (result.status == 200) { location.reload(); }
      }, function (error) {
        if (error.status == 400) {
          this.codeErrorPull = error.status;
          this.messageErrorPull = error.data;
          document.getElementById('loader').style.display = 'none';
          document.getElementById('errorLoader').style.display = 'block';
        }
      });
    },

    tryAgain : function (event) {
      location.reload();
    }
  },
  data : {
    longName : '',
    project: '',
    version : '',
    study : '',
    technicalName : '',
    urlPreview : '',
    messageErrorPull : '',
    codeErrorPull : ''
  }

});

module.exports = vm;

// Manage field color changes in the addInstance form.

vm.$watch('longName', function(data) {

  var value = document.forms['Form']['inputLongName'].value;

  if (value == '')
    document.getElementById('inputLongName').style.backgroundColor='#FFCDD2';
  else
    document.getElementById('inputLongName').style.backgroundColor='#C5E1A5';

});

vm.$watch('study', function(data) {

  var value = document.forms['Form']['inputStudy'].value;

  if (/^[a-z0-9]+$/.test(value)==false || value == '')
    document.getElementById('inputStudy').style.backgroundColor='#FFCDD2';
  else
    document.getElementById('inputStudy').style.backgroundColor='#C5E1A5';

});

vm.$watch('project', function(data) {

  var value = document.forms['Form']['inputProject'].value;

  if (/^[a-z0-9]+$/.test(value)==false || value == '')
    document.getElementById('inputProject').style.backgroundColor='#FFCDD2';
  else
    document.getElementById('inputProject').style.backgroundColor='#C5E1A5';

});

vm.$watch('project', function(data) {

  this.project = data;

  if (this.version == '') { this.technicalName = this.project + '-' + this.study; }
  else { this.technicalName = this.project + '-' + this.study + '-' + this.version; }

  this.urlPreview = 'http://'+this.technicalName+'.'+publicDomain;
  verif(this.technicalName);
});

vm.$watch('study', function (data) {
  this.study = data;

  if (this.version == '') { this.technicalName = this.project + '-' + this.study; }
  else { this.technicalName = this.project + '-' + this.study + '-' + this.version; }

  this.urlPreview = 'http://'+this.technicalName+'.'+publicDomain;
  verif(this.technicalName);
});

vm.$watch('version', function (data) {
  this.version = data;

  if (this.version == '') { this.technicalName = this.project + '-' + this.study; }
  else { this.technicalName = this.project + '-' + this.study + '-' + this.version; }

  this.urlPreview = 'http://'+this.technicalName+'.'+publicDomain;
  verif(this.technicalName);
});

function verif (tn) {
  var data = {
    'technicalName' : tn
  };
  vm.$http.get('/-/v1/instances/verif', data).then(function (result) {
    if (result.status == 200) {
      document.getElementById('technicalNameExists').style.display = 'none';
      document.getElementById('save').style.display = 'block';
      document.getElementById('saveTechnicalExists').style.display = 'none';
    }
  }, function (error) {
    if (error.status == 409) {
      document.getElementById('technicalNameExists').style.display = 'block';
      document.getElementById('save').style.display = 'none';
      document.getElementById('saveTechnicalExists').style.display = 'block';
    }
  });
}<commit_msg>Minor changes.<commit_after>
/* global Vue, global document, global location*/

'use strict';

var publicDomain;

var vm = new Vue({

  el: '#addInstance',

  ready : function () {

    var self = this;

    self.$http.get('/-/v3/config.js').then(function (result) {
      self.$set('publicDomain', result.data.publicDomain);
      publicDomain = result.data.publicDomain;
    }, console.error);
  },

  methods : {

    displayFormAddInstance : function (event) {
      document.getElementById('modal-add-instance').style.display = 'block';
    },

    cancelAddInstance : function (event) {
      document.getElementById('modal-add-instance').style.display = 'none';
    },

    addInstance : function (event) {
      this.longName = document.getElementById('inputLongName').value;
      this.project = document.getElementById('inputProject').value;
      this.study = document.getElementById('inputStudy').value;
      this.version = document.getElementById('inputVersion').value;

      document.getElementById('save').style.display = 'none';
      document.getElementById('close_modal').style.display = 'none';
      document.getElementById('loader').style.display = 'block';

      if (this.longName == '') { this.longName = 'Free comment of '+this.technicalName; }

      var data = {
        'longName' : this.longName,
        'project' : this.project,
        'version' : this.version,
        'study': this.study,
        'technicalName' :  this.technicalName,
        'app' : document.getElementById('app').value
      };
      this.$http.post('/-/v1/instances', data).then(function (result) {
        if (result.status == 200) { location.reload(); }
      }, function (error) {
        if (error.status == 400) {
          this.codeErrorPull = error.status;
          this.messageErrorPull = error.data;
          document.getElementById('loader').style.display = 'none';
          document.getElementById('errorLoader').style.display = 'block';
        }
      });
    },

    tryAgain : function (event) {
      location.reload();
    }
  },
  data : {
    longName : '',
    project: '',
    version : '',
    study : '',
    technicalName : '',
    urlPreview : '',
    messageErrorPull : '',
    codeErrorPull : ''
  }

});

module.exports = vm;

// The 3 first vm.$watch manage field color changes in the addInstance form.

vm.$watch('longName', function(longName) {

  // The longName parameter contains the longName field value.

  if (longName == '')
    document.getElementById('inputLongName').style.backgroundColor='#FFCDD2';
  else
    document.getElementById('inputLongName').style.backgroundColor='#C5E1A5';

});

vm.$watch('study', function(study) {

  // The study parameter contains the study field value.

  if (/^[a-z0-9]+$/.test(study)==false || study == '')
    document.getElementById('inputStudy').style.backgroundColor='#FFCDD2';
  else
    document.getElementById('inputStudy').style.backgroundColor='#C5E1A5';

});

vm.$watch('project', function(project) {

  // The project parameter contains the project field value.

  if (/^[a-z0-9]+$/.test(project)==false || project == '')
    document.getElementById('inputProject').style.backgroundColor='#FFCDD2';
  else
    document.getElementById('inputProject').style.backgroundColor='#C5E1A5';

});

vm.$watch('project', function(project) {

  // The project parameter contains the project field value.

  this.project = project;

  if (this.version == '') { this.technicalName = this.project + '-' + this.study; }
  else { this.technicalName = this.project + '-' + this.study + '-' + this.version; }

  this.urlPreview = 'http://'+this.technicalName+'.'+publicDomain;
  verif(this.technicalName);

});

vm.$watch('study', function (study) {

  // The study parameter contains the study field value.

  this.study = study;

  if (this.version == '') { this.technicalName = this.project + '-' + this.study; }
  else { this.technicalName = this.project + '-' + this.study + '-' + this.version; }

  this.urlPreview = 'http://'+this.technicalName+'.'+publicDomain;
  verif(this.technicalName);

});

vm.$watch('version', function (version) {

  // The version parameter contains the version field value.

  this.version = version;

  if (this.version == '') { this.technicalName = this.project + '-' + this.study; }
  else { this.technicalName = this.project + '-' + this.study + '-' + this.version; }

  this.urlPreview = 'http://'+this.technicalName+'.'+publicDomain;
  verif(this.technicalName);

});

function verif (tn) {

  var data = {
    'technicalName' : tn
  };

  vm.$http.get('/-/v1/instances/verif', data).then(function (result) {

    if (result.status == 200) {
      document.getElementById('technicalNameExists').style.display = 'none';
      document.getElementById('save').style.display = 'block';
      document.getElementById('saveTechnicalExists').style.display = 'none';
    }

  }, function (error) {

    if (error.status == 409) {
      document.getElementById('technicalNameExists').style.display = 'block';
      document.getElementById('save').style.display = 'none';
      document.getElementById('saveTechnicalExists').style.display = 'block';
    }

  });

}<|endoftext|>


<commit_before>var Tooltip = function()
{
    var body = d3.select('body').node()

    // internatls
    var element     // currently shown element (or undefined)
    var tooltip     // tooltip d3js selection object
    var tooltip_content     // inner tooltip HTML container (place where content - result of templating - will be inserted)

    // configurable
    var selection
    var viewport

    // pointer offsets
    var pointerOffsetX = 0
    var pointerOffsetY = 0

    // state
    var stuck = false

    function addEventListener(selection, type, listener)
    {

        if(selection.empty())
            return

        var old_callback = selection.on(type)

        if(old_callback)
        {
            selection.on(type, function(e){
                old_callback(e);
                listener(e);
            })
        }
        else
        {
            selection.on(type, listener)
        }
    }

    var _template = function(d)
    {
        return d.title
    }

    function _move(left, top)
    {
        var size = tooltip.node().getBoundingClientRect()

        var viewport_size = viewport.getBoundingClientRect()

        left = Math.min(left, viewport_size.right - size.width)
        left = Math.max(left, viewport_size.left)
        top = Math.max(top, viewport_size.top)
        top = Math.min(top, viewport_size.bottom - size.height)

        var scrollTop = window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop
        var scrollLeft = window.pageXOffset || document.documentElement.scrollLeft || document.body.scrollLeft

        tooltip
            .style('left', scrollLeft + left + 'px')
            .style('top', scrollTop + top + 'px')
    }

    var publicSpace = {
        init: function(custom_template, id, custom_viewport)
        {
            tooltip = d3.select('body')
                .append('div')
                .attr('class', 'tooltip popover')
                .style('opacity', 0)
                .style('pointer-events', 'none')

            var wrapper = tooltip
                .append('div')
                .attr('class', 'wrapper')

            tooltip_content = wrapper
                .append('div')
                .attr('class', 'popover-content')

            if(custom_template !== undefined)
            {
                _template = custom_template
            }
            d3.select('body')
                .on('click.' + id, publicSpace.unstick)

            // create a close button
            wrapper.append('button')
                .attr('class', 'close')
                .html('x')
                .on('mouseup', publicSpace.unstick)

            if(custom_viewport)
                viewport = custom_viewport
            else
                viewport = body
        },
        show: function(d)
        {
            if(stuck)
                return

            element = selection
                .filter(function(el){ return checkEquality(el, d) })
                .node()

            tooltip.transition()
                .duration(50)
                .style('opacity', 1)
            tooltip_content.html(_template(d))

            publicSpace.moveToPointer()
        },
        hide: function(v)
        {
            if(stuck)
                return

            element = null

            tooltip.transition()
                .duration(200)
                .style('opacity', 0)
        },
        stick: function(d)
        {
            publicSpace.unstick()
            publicSpace.show(d)
            tooltip.style('pointer-events', 'auto')
            stuck = true
            d3.event.stopPropagation()
        },
        unstick: function()
        {
            stuck = false
            publicSpace.hide(true)
            tooltip.style('pointer-events', 'none')
        },
        moveToPointer: function()
        {
            if(stuck)
                return

            size = element.getBoundingClientRect()
            pointerOffsetX = d3.event.clientX - size.left
            pointerOffsetY = d3.event.clientY - size.top

            // move to pointer coordinates, as provided by d3 event
            _move(d3.event.clientX, d3.event.clientY)
        },
        moveToElement: function()
        {
            if(!element)
                return

            size = element.getBoundingClientRect()
            _move(size.left + pointerOffsetX, size.top + pointerOffsetY)
        },
        bind: function(input_selection)
        {
            selection = input_selection

            addEventListener(selection, 'click', publicSpace.stick)
            addEventListener(selection, 'mouseover', publicSpace.show)
            addEventListener(selection, 'mousemove', publicSpace.moveToPointer)
            addEventListener(selection, 'mouseout', publicSpace.hide)

            // do not close the tooltip when selecting
            tooltip
                .on('click', function(){ d3.event.stopPropagation() })

        }
    }

    return publicSpace
}
<commit_msg>Speed up and simplify tooltips binding and showing<commit_after>var Tooltip = function()
{
    var body = d3.select('body').node()

    // internatls
    var element     // currently shown element (or undefined)
    var tooltip     // tooltip d3js selection object
    var tooltip_content     // inner tooltip HTML container (place where content - result of templating - will be inserted)

    // configurable
    var viewport

    // pointer offsets
    var pointerOffsetX = 0
    var pointerOffsetY = 0

    // state
    var stuck = false

    function addEventListener(selection, type, listener)
    {

        if(selection.empty())
            return

        var old_callback = selection.on(type)

        if(old_callback)
        {
            selection.on(type, function(e)
            {
                // passing `this` context when calling
                old_callback.call(this, e)
                listener.call(this, e)
            })
        }
        else
        {
            selection.on(type, listener)
        }
    }

    var _template = function(d)
    {
        return d.title
    }

    function _move(left, top)
    {
        var size = tooltip.node().getBoundingClientRect()

        var viewport_size = viewport.getBoundingClientRect()

        left = Math.min(left, viewport_size.right - size.width)
        left = Math.max(left, viewport_size.left)
        top = Math.max(top, viewport_size.top)
        top = Math.min(top, viewport_size.bottom - size.height)

        var scrollTop = window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop
        var scrollLeft = window.pageXOffset || document.documentElement.scrollLeft || document.body.scrollLeft

        tooltip
            .style('left', scrollLeft + left + 'px')
            .style('top', scrollTop + top + 'px')
    }

    var publicSpace = {
        init: function(custom_template, id, custom_viewport)
        {
            tooltip = d3.select('body')
                .append('div')
                .attr('class', 'tooltip popover')
                .style('opacity', 0)
                .style('pointer-events', 'none')

            var wrapper = tooltip
                .append('div')
                .attr('class', 'wrapper')

            tooltip_content = wrapper
                .append('div')
                .attr('class', 'popover-content')

            if(custom_template !== undefined)
            {
                _template = custom_template
            }
            d3.select('body')
                .on('click.' + id, publicSpace.unstick)

            // create a close button
            wrapper.append('button')
                .attr('class', 'close')
                .html('x')
                .on('mouseup', publicSpace.unstick)

            if(custom_viewport)
                viewport = custom_viewport
            else
                viewport = body
        },
        show: function(d)
        {
            if(stuck)
                return

            // `d` provides data whereas `this` gives the DOM element
            element = this

            tooltip.transition()
                .duration(50)
                .style('opacity', 1)
            tooltip_content.html(_template(d))

            publicSpace.moveToPointer()
        },
        hide: function(v)
        {
            if(stuck)
                return

            element = null

            tooltip.transition()
                .duration(200)
                .style('opacity', 0)
        },
        stick: function(d)
        {
            publicSpace.unstick()
            // call `show` method passing `this` context
            publicSpace.show.call(this, d)
            tooltip.style('pointer-events', 'auto')
            stuck = true
            d3.event.stopPropagation()
        },
        unstick: function()
        {
            stuck = false
            publicSpace.hide(true)
            tooltip.style('pointer-events', 'none')
        },
        moveToPointer: function()
        {
            if(stuck)
                return

            size = element.getBoundingClientRect()
            pointerOffsetX = d3.event.clientX - size.left
            pointerOffsetY = d3.event.clientY - size.top

            // move to pointer coordinates, as provided by d3 event
            _move(d3.event.clientX, d3.event.clientY)
        },
        moveToElement: function()
        {
            if(!element)
                return

            size = element.getBoundingClientRect()
            _move(size.left + pointerOffsetX, size.top + pointerOffsetY)
        },
        bind: function(selection)
        {
            addEventListener(selection, 'click', publicSpace.stick)
            addEventListener(selection, 'mouseover', publicSpace.show)
            addEventListener(selection, 'mousemove', publicSpace.moveToPointer)
            addEventListener(selection, 'mouseout', publicSpace.hide)

            // do not close the tooltip when selecting
            tooltip
                .on('click', function(){ d3.event.stopPropagation() })

        }
    }

    return publicSpace
}
<|endoftext|>


<commit_before>
const EventEmitter = require('events');
const net = require('./protocol');

let NEXT_ROOM_ID = 0;

class Room extends EventEmitter {

	/**
	 * Create a new instance of Room
	 * @param {User} owner the room owner
	 */
	constructor(owner) {
		super();

		this.id = 0;
		this.owner = owner;
		this.driver = null;

		this.users = new Set;
		this.addUser(owner);
	}

	/**
	 * Find a user by user id
	 * @param {number} id user id
	 */
	findUser(id) {
		for (let user of this.users) {
			if (user.id === id) {
				return user;
			}
		}
		return null;
	}

	/**
	 * Add a user into this room
	 * @param {User} user
	 */
	addUser(user) {
		if (user.room) {
			user.room.removeUser(user);
		}
		user.room = this;
		this.users.add(user);

		user.on('close', () => this.removeUser(user));
	}

	/**
	 * Remove a user from this room
	 * @param {User} user
	 */
	removeUser(user) {
		user.room = null;
		this.users.delete(user);

		if (this.users.size <= 0) {
			this.emit('close');
		}
	}

	/**
	 * Broadcast a command to all users in this room
	 * @param {number} command
	 * @param {object} args
	 */
	broadcast(command, args = null) {
		for (let user of this.users) {
			user.send(command, args);
		}
	}

	/**
	 * Broadcast a command to all users except one
	 * @param {User} except
	 * @param {number} command
	 * @param {object} args
	 */
	broadcastExcept(except, command, args = null) {
		for (let user of this.users) {
			if (user === except) {
				continue;
			}
			user.send(command, args);
		}
	}

	/**
	 * Getter of room configuration
	 */
	getConfig() {
		let config = {
			id: this.id,
			owner: {
				id: this.owner.id
			},
			driver: null
		};

		if (this.driver && this.driver.getConfig) {
			config.driver = this.driver.getConfig();
			config.driver.name = this.driver.name;
		}
	}

	/**
	 * Setter of room configuration
	 * @param {*} config
	 */
	updateConfig(config) {
		if (this.driver && this.driver.setConfig) {
			this.driver.setConfig(config);
		}
	}

	/**
	 * Load game extension
	 * @param {string} name driver name
	 * @return {boolean}
	 */
	loadExtension(name) {
		try {
			const GameDriver = require('../extension/' + name);
			this.driver = new GameDriver(this);
			return true;
		} catch (error) {
			console.log(error.stack);
			return false;
		}
	}

}

module.exports = Room;
<commit_msg>Close room after 10 minutes<commit_after>
const EventEmitter = require('events');
const net = require('./protocol');

let NEXT_ROOM_ID = 0;

class Room extends EventEmitter {

	/**
	 * Create a new instance of Room
	 * @param {User} owner the room owner
	 */
	constructor(owner) {
		super();

		this.id = 0;
		this.owner = owner;
		this.driver = null;

		this.users = new Set;
		this.addUser(owner);
	}

	/**
	 * Find a user by user id
	 * @param {number} id user id
	 */
	findUser(id) {
		for (let user of this.users) {
			if (user.id === id) {
				return user;
			}
		}
		return null;
	}

	/**
	 * Add a user into this room
	 * @param {User} user
	 */
	addUser(user) {
		if (user.room) {
			user.room.removeUser(user);
		}
		user.room = this;
		this.users.add(user);

		user.on('close', () => this.removeUser(user));
	}

	/**
	 * Remove a user from this room
	 * @param {User} user
	 */
	removeUser(user) {
		user.room = null;
		this.users.delete(user);

		if (this.closeTimeout) {
			clearTimeout(this.closeTimeout);
		}
		this.closeTimeout = setTimeout(() => {
			if (this.users.size <= 0) {
				this.emit('close');
			}
		}, 10 * 60 * 1000);
	}

	/**
	 * Broadcast a command to all users in this room
	 * @param {number} command
	 * @param {object} args
	 */
	broadcast(command, args = null) {
		for (let user of this.users) {
			user.send(command, args);
		}
	}

	/**
	 * Broadcast a command to all users except one
	 * @param {User} except
	 * @param {number} command
	 * @param {object} args
	 */
	broadcastExcept(except, command, args = null) {
		for (let user of this.users) {
			if (user === except) {
				continue;
			}
			user.send(command, args);
		}
	}

	/**
	 * Getter of room configuration
	 */
	getConfig() {
		let config = {
			id: this.id,
			owner: {
				id: this.owner.id
			},
			driver: null
		};

		if (this.driver && this.driver.getConfig) {
			config.driver = this.driver.getConfig();
			config.driver.name = this.driver.name;
		}
	}

	/**
	 * Setter of room configuration
	 * @param {*} config
	 */
	updateConfig(config) {
		if (this.driver && this.driver.setConfig) {
			this.driver.setConfig(config);
		}
	}

	/**
	 * Load game extension
	 * @param {string} name driver name
	 * @return {boolean}
	 */
	loadExtension(name) {
		try {
			const GameDriver = require('../extension/' + name);
			this.driver = new GameDriver(this);
			return true;
		} catch (error) {
			console.log(error.stack);
			return false;
		}
	}

}

module.exports = Room;
<|endoftext|>


<commit_before>'use strict';

var http = require('http')
  , https = require('https')
  , url = require('url')
  , cloneextend = require('cloneextend')
  , request = require('request')
  , winston = require('winston')
  , FormData = require('form-data');

var apiBase;

exports.setApiBase = function(ab) { apiBase = ab; }

/**
 * Configure the connection with options.
 */
exports.init = function(accessToken) {

  var parsedBaseUrl = url.parse(apiBase);
  var proto = parsedBaseUrl.protocol.indexOf('https')>-1 ? https : http;

  var baseOpts = {
    hostname: parsedBaseUrl.hostname,
    port: parsedBaseUrl.port,
    headers: { 'Content-Type': 'application/json' }
  };

  return {
    makeRequest: function(opts, body, callback) {
      if(opts.path.indexOf('?') != -1){
        opts.path += '&access_token='+accessToken;
      }else{
        opts.path += '?access_token='+accessToken;
      }

      var req = proto.request(opts, function(res) {
        res.setEncoding('utf-8');
        var response = '';
        res.on('data', function(chunk) {
          response+=chunk;
        });

        res.on('end', function() {
          if(res.statusCode==200) {
            callback(null, JSON.parse(response));
          } else {
            winston.error('error accessing information from API');
            winston.error(res.statusCode);
            winston.error(response);
            callback('API request got status code: ' + res.statusCode + '.  ' + response, JSON.parse(response));
          }
        });
      });

      if(body) {
        req.write(body);
      }

      req.end();
    },

    get: function(path, callback) {
      var opts = cloneextend.clone(baseOpts);
      opts.path = path;
      opts.method = 'GET';

      this.makeRequest(opts, null, callback);
    },

    post: function(path, body, callback) {
      var opts = cloneextend.clone(baseOpts);
      opts.path = path;
      var bodyString;
      if(body){
        bodyString = JSON.stringify(body);
        opts.headers['Content-Length'] = Buffer.byteLength(bodyString, 'utf8');
      }else{
        delete opts.headers['Content-Type'];
      }
      opts.method = 'POST';

      this.makeRequest(opts, bodyString, callback);
    },

    postMultipart: function(path, parts, callback) {
      var url = apiBase + path + (path.indexOf('?') === -1 ? '?' : '&') + 'access_token=' + accessToken;

      var form = new FormData();
      for(var name in parts) {
        form.append(name, parts[name]);
      }

      form.getLength(function(err, length) {
        var r = request.post(url, callback);
        r._form = form;
        r.setHeader('content-length', length);
      });
    },

    put: function(path, body, callback) {
      var opts = cloneextend.clone(baseOpts);
      opts.path = path;
      var bodyString = JSON.stringify(body);
      opts.headers['Content-Length'] = Buffer.byteLength(bodyString, 'utf8');
      opts.method = 'PUT';

      this.makeRequest(opts, bodyString, callback);
    },

    delete: function(path, callback) {
      var opts = cloneextend.clone(baseOpts);
      delete opts.headers['Content-Type'];
      opts.path = path;
      opts.method = 'DELETE';

      this.makeRequest(opts, null, callback);
    }
  };
}
<commit_msg>Try/Catch JSON formatting of response<commit_after>'use strict';

var http = require('http')
  , https = require('https')
  , url = require('url')
  , cloneextend = require('cloneextend')
  , request = require('request')
  , winston = require('winston')
  , FormData = require('form-data');

var apiBase;

exports.setApiBase = function(ab) { apiBase = ab; }

/**
 * Configure the connection with options.
 */
exports.init = function(accessToken) {

  var parsedBaseUrl = url.parse(apiBase);
  var proto = parsedBaseUrl.protocol.indexOf('https')>-1 ? https : http;

  var baseOpts = {
    hostname: parsedBaseUrl.hostname,
    port: parsedBaseUrl.port,
    headers: { 'Content-Type': 'application/json' }
  };

  return {
    makeRequest: function(opts, body, callback) {
      if(opts.path.indexOf('?') != -1){
        opts.path += '&access_token='+accessToken;
      }else{
        opts.path += '?access_token='+accessToken;
      }

      var req = proto.request(opts, function(res) {
        res.setEncoding('utf-8');
        var response = '';
        res.on('data', function(chunk) {
          response+=chunk;
        });

        res.on('end', function() {
          if(res.statusCode==200) {
            callback(null, JSON.parse(response));
          } else {
            winston.error('error accessing information from API');
            winston.error(res.statusCode);
            winston.error(response);
            var jsonresponse = null;
            try {
              jsonresponse = JSON.parse(response);
            } catch(err){
              jsonresponse = response;
            }
            callback('API request got status code: ' + res.statusCode + '.  ' + response, jsonresponse);
          }
        });
      });

      if(body) {
        req.write(body);
      }

      req.end();
    },

    get: function(path, callback) {
      var opts = cloneextend.clone(baseOpts);
      opts.path = path;
      opts.method = 'GET';

      this.makeRequest(opts, null, callback);
    },

    post: function(path, body, callback) {
      var opts = cloneextend.clone(baseOpts);
      opts.path = path;
      var bodyString;
      if(body){
        bodyString = JSON.stringify(body);
        opts.headers['Content-Length'] = Buffer.byteLength(bodyString, 'utf8');
      }else{
        delete opts.headers['Content-Type'];
      }
      opts.method = 'POST';

      this.makeRequest(opts, bodyString, callback);
    },

    postMultipart: function(path, parts, callback) {
      var url = apiBase + path + (path.indexOf('?') === -1 ? '?' : '&') + 'access_token=' + accessToken;

      var form = new FormData();
      for(var name in parts) {
        form.append(name, parts[name]);
      }

      form.getLength(function(err, length) {
        var r = request.post(url, callback);
        r._form = form;
        r.setHeader('content-length', length);
      });
    },

    put: function(path, body, callback) {
      var opts = cloneextend.clone(baseOpts);
      opts.path = path;
      var bodyString = JSON.stringify(body);
      opts.headers['Content-Length'] = Buffer.byteLength(bodyString, 'utf8');
      opts.method = 'PUT';

      this.makeRequest(opts, bodyString, callback);
    },

    delete: function(path, callback) {
      var opts = cloneextend.clone(baseOpts);
      delete opts.headers['Content-Type'];
      opts.path = path;
      opts.method = 'DELETE';

      this.makeRequest(opts, null, callback);
    }
  };
}
<|endoftext|>


<commit_before>const $ = require('jquery');
const Analytics = require('@dosomething/analytics');
const Validation = require('dosomething-validation');
const { Engine } = require('@dosomething/puck-client');
const {
  flattenDeep,
  isNul,
  isObjectLike,
  mapValues,
  omitBy,
  snakeCase,
  startCase,
} = require("lodash");

// App name prefix used for analytics event naming.
const APP_PREFIX = 'northstar';

// Variable that stores the instance of PuckClient.
let puckClient = null;

/**
 * Stringify all properties on an object whose value is object with properties.
 *
 * @param  {Object} data
 * @return {Object}
 */
export function stringifyNestedObjects(data) {
  return mapValues(data, value => {
    if (isObjectLike(value)) {
      return JSON.stringify(value);
    }

    return value;
  });
}

/**
 * Return a boolean indicating whether the provided argument is a string.
 *
 * @param  {Mixed}  string
 * @return {Boolean}
 */
export function isEmptyString(string) {
  return string === '';
}

/**
 * Remove items from object with null, undefined, or empty string values.
 *
 * @param  {Object} data
 * @return {Object}
 */
export function withoutValueless(data) {
  return omitBy(omitBy(data, isNil), isEmptyString);
}

/**
 * Parse analytics event name parameters into a snake cased string.
 *
 * @param  {String}      verb
 * @param  {String}      noun
 * @param  {String|Null} [adjective=null]
 * @return {void}
 */
const formatEventName = (verb, noun, adjective = null) => {
  let eventName = `${APP_PREFIX}_${snakeCase(verb)}_${snakeCase(noun)}`;
  // Append adjective if defined.
  eventName += adjective ? `_${snakeCase(adjective)}` : '';

  return eventName;
};

/**
 * Return an instantiated Puck Client (Engine).
 *
 * @return {Object}
 */
const puckClientInit = () => (
  new Engine({
    source: 'northstar',
    puckUrl: window.ENV.PUCK_URL,
    getUser: () => window.NORTHSTAR_ID,
  })
);

/**
 * Send event to analyze with Puck.
 *
 * @param  {String} name
 * @param  {Object} data
 * @return {void}
 */
export function analyzeWithPuck(name, data) {
  if (!puckClient) {
    puckClient = puckClientInit();
  }

  puckClient.trackEvent(name, data);
}

/**
 * Send event to analyze with Google Analytics.
 *
 * @param  {String} category
 * @param  {String} action
 * @param  {String} label
 * @param  {Object} data
 * @return {void}
 */
export function analyzeWithGoogle(name, category, action, label, data) {
  if (!category || !action) {
    console.error('The Category or Action is missing!');
    return;
  }

  // @DEPRECATE
  Analytics.analyze(category, action, label);

  const flattenedData = stringifyNestedObjects(data);

  const analyticsEvent = {
    event: name,
    eventAction: startCase(action),
    eventCategory: startCase(category),
    eventLabel: startCase(label),
    ...flattenedData,
  }

  // Push event action to Google Tag Manager's data layer.
  window.dataLayer = window.dataLayer || [];
  window.dataLayer.push(analyticsEvent);
}

/**
 * Dispatch analytics event to specified service, or all services by default.
 *
 * @param  {String}      category
 * @param  {String}      name
 * @param  {Object|Null} [data]
 * @param  {String|Null} [service]
 * @return {void}
 */
const sendToServices = (name, category, action, label, data, service) => {
  switch (service) {
    case 'ga':
      analyzeWithGoogle(name, category, action, label, data);
      break;

    case 'puck':
      analyzeWithPuck(name, data);
      break;

    default:
      analyzeWithGoogle(name, category, action, label, data);
      analyzeWithPuck(name, data);
  }
};

/**
 * Track an analytics event with a specified service.
 * (Defaults to tracking with all services.)
 *
 * @param  {Object} options
 * @param  {Object} options.metadata
 * @param  {Object} options.context
 * @param  {String} options.service
 * @return {void}
 */
export function trackAnalyticsEvent({ metadata, context = {}, service }) {
  if (!metadata) {
    console.error('The metadata object is missing!');
    return;
  }

  const { adjective, category, target, noun, verb } = metadata;
  const label = metadata.label || noun;

  const name = formatEventName(verb, noun, adjective);

  const action = snakeCase(`${target}_${verb}`);

  sendToServices(name, category, action, label, context, service);
}

// Helper method to track field focus analytics events.
function trackInputFocus(inputName) {
  if (!inputName) {
    return;
  }

  trackAnalyticsEvent({
    metadata: {
      adjective: inputName,
      category: 'focused_field',
      label: inputName,
      noun: 'field',
      target: 'field',
      verb: 'focused',
    },
  });
}

function init() {
  Analytics.init();

  if (!puckClient) {
    puckClient = puckClientInit();
  }

  // Validation Events for the Register form.
  Validation.Events.subscribe('Validation:InlineError', (topic, args) => {
    // Tracks each individual inline error.
    trackAnalyticsEvent({
      metadata: {
        adjective: `field_${args}`,
        category: 'authentication',
        label: args,
        noun: 'error',
        target: 'error',
        verb: 'triggered',
      },
    });
  });

  Validation.Events.subscribe('Validation:Suggestion', (topic, args) => {
    // Tracks email fix suggestion.
    trackAnalyticsEvent({
      context: {
        suggestion: args,
      },
      metadata: {
        adjective: 'field_email',
        category: 'authentication',
        label: 'email_suggestion',
        noun: 'suggestion',
        target: 'suggestion',
        verb: 'triggered',
      },
    });
  });

  Validation.Events.subscribe('Validation:SuggestionUsed', (topic, args) => {
    // Tracks when email fix suggestion is used.
    trackAnalyticsEvent({
      metadata: {
        adjective: 'field_email',
        category: 'authentication',
        label: 'email_suggestion',
        noun: 'suggestion',
        target: 'suggestion',
        verb: 'used',
      },
    });
  });

  Validation.Events.subscribe('Validation:Submitted', (topic, args) => {
    // Tracks when an inline validation error free submission is made.
    trackAnalyticsEvent({
      context: {
        suggestion: args,
      },
      metadata: {
        category: 'authentication',
        noun: 'register',
        target: 'form',
        verb: 'submitted',
      },
    });
  });

  Validation.Events.subscribe('Validation:SubmitError', (topic, args) => {
    // Tracks when a submission is prevented due to inline validation errors.
    trackAnalyticsEvent({
      metadata: {
        adjective: 'submit_register',
        category: 'authentication',
        label: 'submit_register',
        noun: 'error',
        target: 'error',
        verb: 'triggered',
      },
    });
  });

  // Custom tracking events.
  $(document).ready(() => {
    // Tracks an auto focused form field (which will already be focused upon page load).
    const focusedElement = $('input:focus');
    if (focusedElement.length) {
      const inputName = focusedElement.attr('name');
      trackInputFocus(inputName);
    }

    // Tracks when user focuses on form field.
    $('input').on('focus', (element) => {
      const inputName = element.target.name;
      trackInputFocus(inputName);
    })

    $('#profile-login-form').on('submit', () => {
      // Tracks login form submissions.
      trackAnalyticsEvent({
        metadata: {
          category: 'authentication',
          noun: 'login',
          target: 'form',
          verb: 'submitted',
        },
      });
    });

    $('#profile-edit-form').on('submit', () => {
      // Tracks profile edit form submissions.
      trackAnalyticsEvent({
        metadata: {
          category: 'account_edit',
          noun: 'edit_profile',
          target: 'form',
          verb: 'submitted',
        },
      });
    });

    $('#forgot-password-form').on('submit', () => {
      // Tracks forgot password email form submissions.
      trackAnalyticsEvent({
        metadata: {
          category: 'account_edit',
          noun: 'forgot_password',
          target: 'form',
          verb: 'submitted',
        },
      });
    })

    $('#password-reset-form').on('submit', () => {
      // Tracks password reset form submissions.
      trackAnalyticsEvent({
        metadata: {
          category: 'account_edit',
          noun: 'reset_password',
          target: 'form',
          verb: 'submitted',
        },
      });
    })

    $('.facebook-login').on('click', () => {
      // Tracks clicking on the Login With Facebook button.
      trackAnalyticsEvent({
        metadata: {
          category: 'login_facebook',
          label: 'authentication',
          noun: 'login_facebook',
          target: 'button',
          verb: 'clicked',
        },
      });
    });

    $('.login-link').on('click', () => {
      // Tracks clicking on any of the 'Log in' buttons and links.
      trackAnalyticsEvent({
        metadata: {
          category: 'authentication',
          noun: 'login',
          target: 'button',
          verb: 'clicked',
        },
      });
    })

    $('.register-link').on('click', () => {
      // Tracks clicking on any of the 'Register' or 'Create account' buttons and links.
      trackAnalyticsEvent({
        metadata: {
          category: 'authentication',
          noun: 'register',
          target: 'button',
          verb: 'clicked',
        },
      });
    })

    $('.forgot-password-link').on('click', () => {
      // Tracks clicking on the 'Forgot Password' link.
      trackAnalyticsEvent({
        metadata: {
          category: 'account_edit',
          noun: 'forgot_password',
          target: 'button',
          verb: 'clicked',
        },
      });
    })

    // Check for and track validation errors returned from the backend after form submission.
    const $validationErrors = $('.validation-error');
    if ($validationErrors && $validationErrors.length) {
      const errors = window.ERRORS || {};
      const invalidFields = Object.keys(errors);

      const validationMessages = flattenDeep(Object.values(errors));

      trackAnalyticsEvent({
        context: {
          invalidFields,
          validationMessages,
        },
        metadata: {
          category: 'authentication',
          label: 'validation_error',
          noun: 'validation',
          target: 'validation',
          verb: 'failed',
        },
      });
    }
  });
}

export default { init };
<commit_msg>Append userId to GA/GTM context data<commit_after>const $ = require('jquery');
const Analytics = require('@dosomething/analytics');
const Validation = require('dosomething-validation');
const { Engine } = require('@dosomething/puck-client');
const {
  flattenDeep,
  isNul,
  isObjectLike,
  mapValues,
  omitBy,
  snakeCase,
  startCase,
} = require("lodash");

// App name prefix used for analytics event naming.
const APP_PREFIX = 'northstar';

// Variable that stores the instance of PuckClient.
let puckClient = null;

/**
 * Stringify all properties on an object whose value is object with properties.
 *
 * @param  {Object} data
 * @return {Object}
 */
export function stringifyNestedObjects(data) {
  return mapValues(data, value => {
    if (isObjectLike(value)) {
      return JSON.stringify(value);
    }

    return value;
  });
}

/**
 * Return a boolean indicating whether the provided argument is a string.
 *
 * @param  {Mixed}  string
 * @return {Boolean}
 */
export function isEmptyString(string) {
  return string === '';
}

/**
 * Remove items from object with null, undefined, or empty string values.
 *
 * @param  {Object} data
 * @return {Object}
 */
export function withoutValueless(data) {
  return omitBy(omitBy(data, isNil), isEmptyString);
}

/**
 * Parse analytics event name parameters into a snake cased string.
 *
 * @param  {String}      verb
 * @param  {String}      noun
 * @param  {String|Null} [adjective=null]
 * @return {void}
 */
const formatEventName = (verb, noun, adjective = null) => {
  let eventName = `${APP_PREFIX}_${snakeCase(verb)}_${snakeCase(noun)}`;
  // Append adjective if defined.
  eventName += adjective ? `_${snakeCase(adjective)}` : '';

  return eventName;
};

/**
 * Return an instantiated Puck Client (Engine).
 *
 * @return {Object}
 */
const puckClientInit = () => (
  new Engine({
    source: 'northstar',
    puckUrl: window.ENV.PUCK_URL,
    getUser: () => window.NORTHSTAR_ID,
  })
);

/**
 * Send event to analyze with Puck.
 *
 * @param  {String} name
 * @param  {Object} data
 * @return {void}
 */
export function analyzeWithPuck(name, data) {
  if (!puckClient) {
    puckClient = puckClientInit();
  }

  puckClient.trackEvent(name, data);
}

/**
 * Send event to analyze with Google Analytics.
 *
 * @param  {String} category
 * @param  {String} action
 * @param  {String} label
 * @param  {Object} data
 * @return {void}
 */
export function analyzeWithGoogle(name, category, action, label, data) {
  if (!category || !action) {
    console.error('The Category or Action is missing!');
    return;
  }

  // @DEPRECATE
  Analytics.analyze(category, action, label);

  const flattenedData = stringifyNestedObjects(data);

  if (window.NORTHSTAR_ID) {
    flattenedData.userId = window.NORTHSTAR_ID;
  }

  const analyticsEvent = {
    event: name,
    eventAction: startCase(action),
    eventCategory: startCase(category),
    eventLabel: startCase(label),
    ...flattenedData,
  }

  // Push event action to Google Tag Manager's data layer.
  window.dataLayer = window.dataLayer || [];
  window.dataLayer.push(analyticsEvent);
}

/**
 * Dispatch analytics event to specified service, or all services by default.
 *
 * @param  {String}      category
 * @param  {String}      name
 * @param  {Object|Null} [data]
 * @param  {String|Null} [service]
 * @return {void}
 */
const sendToServices = (name, category, action, label, data, service) => {
  switch (service) {
    case 'ga':
      analyzeWithGoogle(name, category, action, label, data);
      break;

    case 'puck':
      analyzeWithPuck(name, data);
      break;

    default:
      analyzeWithGoogle(name, category, action, label, data);
      analyzeWithPuck(name, data);
  }
};

/**
 * Track an analytics event with a specified service.
 * (Defaults to tracking with all services.)
 *
 * @param  {Object} options
 * @param  {Object} options.metadata
 * @param  {Object} options.context
 * @param  {String} options.service
 * @return {void}
 */
export function trackAnalyticsEvent({ metadata, context = {}, service }) {
  if (!metadata) {
    console.error('The metadata object is missing!');
    return;
  }

  const { adjective, category, target, noun, verb } = metadata;
  const label = metadata.label || noun;

  const name = formatEventName(verb, noun, adjective);

  const action = snakeCase(`${target}_${verb}`);

  sendToServices(name, category, action, label, context, service);
}

// Helper method to track field focus analytics events.
function trackInputFocus(inputName) {
  if (!inputName) {
    return;
  }

  trackAnalyticsEvent({
    metadata: {
      adjective: inputName,
      category: 'focused_field',
      label: inputName,
      noun: 'field',
      target: 'field',
      verb: 'focused',
    },
  });
}

function init() {
  Analytics.init();

  if (!puckClient) {
    puckClient = puckClientInit();
  }

  // Validation Events for the Register form.
  Validation.Events.subscribe('Validation:InlineError', (topic, args) => {
    // Tracks each individual inline error.
    trackAnalyticsEvent({
      metadata: {
        adjective: `field_${args}`,
        category: 'authentication',
        label: args,
        noun: 'error',
        target: 'error',
        verb: 'triggered',
      },
    });
  });

  Validation.Events.subscribe('Validation:Suggestion', (topic, args) => {
    // Tracks email fix suggestion.
    trackAnalyticsEvent({
      context: {
        suggestion: args,
      },
      metadata: {
        adjective: 'field_email',
        category: 'authentication',
        label: 'email_suggestion',
        noun: 'suggestion',
        target: 'suggestion',
        verb: 'triggered',
      },
    });
  });

  Validation.Events.subscribe('Validation:SuggestionUsed', (topic, args) => {
    // Tracks when email fix suggestion is used.
    trackAnalyticsEvent({
      metadata: {
        adjective: 'field_email',
        category: 'authentication',
        label: 'email_suggestion',
        noun: 'suggestion',
        target: 'suggestion',
        verb: 'used',
      },
    });
  });

  Validation.Events.subscribe('Validation:Submitted', (topic, args) => {
    // Tracks when an inline validation error free submission is made.
    trackAnalyticsEvent({
      context: {
        suggestion: args,
      },
      metadata: {
        category: 'authentication',
        noun: 'register',
        target: 'form',
        verb: 'submitted',
      },
    });
  });

  Validation.Events.subscribe('Validation:SubmitError', (topic, args) => {
    // Tracks when a submission is prevented due to inline validation errors.
    trackAnalyticsEvent({
      metadata: {
        adjective: 'submit_register',
        category: 'authentication',
        label: 'submit_register',
        noun: 'error',
        target: 'error',
        verb: 'triggered',
      },
    });
  });

  // Custom tracking events.
  $(document).ready(() => {
    // Tracks an auto focused form field (which will already be focused upon page load).
    const focusedElement = $('input:focus');
    if (focusedElement.length) {
      const inputName = focusedElement.attr('name');
      trackInputFocus(inputName);
    }

    // Tracks when user focuses on form field.
    $('input').on('focus', (element) => {
      const inputName = element.target.name;
      trackInputFocus(inputName);
    })

    $('#profile-login-form').on('submit', () => {
      // Tracks login form submissions.
      trackAnalyticsEvent({
        metadata: {
          category: 'authentication',
          noun: 'login',
          target: 'form',
          verb: 'submitted',
        },
      });
    });

    $('#profile-edit-form').on('submit', () => {
      // Tracks profile edit form submissions.
      trackAnalyticsEvent({
        metadata: {
          category: 'account_edit',
          noun: 'edit_profile',
          target: 'form',
          verb: 'submitted',
        },
      });
    });

    $('#forgot-password-form').on('submit', () => {
      // Tracks forgot password email form submissions.
      trackAnalyticsEvent({
        metadata: {
          category: 'account_edit',
          noun: 'forgot_password',
          target: 'form',
          verb: 'submitted',
        },
      });
    })

    $('#password-reset-form').on('submit', () => {
      // Tracks password reset form submissions.
      trackAnalyticsEvent({
        metadata: {
          category: 'account_edit',
          noun: 'reset_password',
          target: 'form',
          verb: 'submitted',
        },
      });
    })

    $('.facebook-login').on('click', () => {
      // Tracks clicking on the Login With Facebook button.
      trackAnalyticsEvent({
        metadata: {
          category: 'login_facebook',
          label: 'authentication',
          noun: 'login_facebook',
          target: 'button',
          verb: 'clicked',
        },
      });
    });

    $('.login-link').on('click', () => {
      // Tracks clicking on any of the 'Log in' buttons and links.
      trackAnalyticsEvent({
        metadata: {
          category: 'authentication',
          noun: 'login',
          target: 'button',
          verb: 'clicked',
        },
      });
    })

    $('.register-link').on('click', () => {
      // Tracks clicking on any of the 'Register' or 'Create account' buttons and links.
      trackAnalyticsEvent({
        metadata: {
          category: 'authentication',
          noun: 'register',
          target: 'button',
          verb: 'clicked',
        },
      });
    })

    $('.forgot-password-link').on('click', () => {
      // Tracks clicking on the 'Forgot Password' link.
      trackAnalyticsEvent({
        metadata: {
          category: 'account_edit',
          noun: 'forgot_password',
          target: 'button',
          verb: 'clicked',
        },
      });
    })

    // Check for and track validation errors returned from the backend after form submission.
    const $validationErrors = $('.validation-error');
    if ($validationErrors && $validationErrors.length) {
      const errors = window.ERRORS || {};
      const invalidFields = Object.keys(errors);

      const validationMessages = flattenDeep(Object.values(errors));

      trackAnalyticsEvent({
        context: {
          invalidFields,
          validationMessages,
        },
        metadata: {
          category: 'authentication',
          label: 'validation_error',
          noun: 'validation',
          target: 'validation',
          verb: 'failed',
        },
      });
    }
  });
}

export default { init };
<|endoftext|>


<commit_before>/**
	_moon.CalendarPicker_ is a control that displays a monthly calendar, with the
	month name at the top and a grid of days, grouped into rows by week, below.

	The header buttons are used to navigate to the desired month; the desired day
	is selected by tapping on it.

	{kind: "moon.CalendarPicker", content: "Calendar Title"}
*/
enyo.kind({
	name: "moon.CalendarPickerDate",
	kind: "moon.Button",
	small: true,
	marquee: false,
	classes: "moon-calendar-picker-date enyo-unselectable",
	published: {
		value: null,
		color: 0
	},
	create: function() {
		this.inherited(arguments);
		if (typeof ilib !== "undefined") {
			this._tf = new ilib.DateFmt({
				type: "date",	//only format the date component, not the time
				date: "d",		//'d' is the date of month
				length: "short"	//it uses 2 chars to abbreviate properly
			});
		}
	},
	colorChanged: function(inOld) {
		this.addRemoveClass("moon-calendar-picker-date-shadow", this.color);
	},
	valueChanged: function() {
		if (typeof ilib !== "undefined") {
			var date = ilib.Date.newInstance({unixtime: (this.value.getDate() - 1) * (24*60*60*1000)});
			this.setContent(this._tf.format(date));
		} else {
			this.setContent(this.value.getDate());
		}
	}
});

enyo.kind({
	name: "moon.CalendarPicker",
	classes: "moon-calendar-picker",
	events: {
		/**
			Fires when the date changes.

			_inEvent.name_ contains the name of this control.

			_inEvent.value_ contains a standard JavaScript Date object representing
			the current date.
		*/
		onChange: ""
	},
	handlers: {
		ontap: "selectDate"
	},
	published: {
		/**
			ilib locale info instance. It gives information about the paarticular locale.
		*/
		ilibLocaleInfo: null,
		/**
			Current locale used for formatting. May be set after the control is
			created, in which case the control will be updated to reflect the
			new value.
		*/
		locale: "en_us",
		/**
			The current Date object. When a Date object is passed to _setValue_,
			the control is updated to reflect the new value. _getValue_ returns
			a Date object.
		*/
		value: null,
		/**
			The day of week that starts weeks in current locale.
			As a default, Sunday is the first day of week.
		*/
		firstDayOfWeek: 0,
		/**
			The maximum number of weeks to display in a screen.
			If this value is greater than 9, dates two months in the future may be
			shown. Unexpected input may result in errors.
		*/
		maxWeeks: 6,
		months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
		days: ["S","M","T","W","T","F","S"]
	},
	components: [
		{name: "monthPicker", kind: "moon.SimplePicker", onChange: "selectMonthPicker"},
		{name: "days", kind: "enyo.Group"},
		{name: "dates", kind: "enyo.Group"}
	],
	create: function() {
		this.inherited(arguments);
		this.initMonthPicker();
		this.initDays();
		this.initCalendar();

		if (typeof ilib !== "undefined") {
			this._tf = new ilib.DateFmt({
				type: "date",	//only format the date component, not the time
				date: "w",		//'w' is the day of the week
				length: "medium"//it uses 3 chars to abbreviate properly
			});

			this.ilibLocaleInfo = new ilib.LocaleInfo();
			this.setLocale(this.ilibLocaleInfo.locale);
		}
		this.initDefaults();
	},
	initDefaults: function() {
		this.setValue(this.value || new Date());
		//Attempt to use the ilib lib (assuming that it is loaded)
		if (typeof ilib !== "undefined") {
			var dayOfWeek = this.ilibLocaleInfo.getFirstDayOfWeek();
			this.setFirstDayOfWeek(dayOfWeek);
		}
		this.updateDays();
	},
	/**
		Populates SimplePicker with months of the year, from JAN to DEC.
	*/
	initMonthPicker: function() {
		var months = this.months;
		for (var i = 0; i < 12; i++) {
			this.$.monthPicker.createComponent(
				{content: months[i], classes: "picker-content", style: "width: 200px"}
			);
		}
	},
	/**
		Initiate days of the week from first day to last day.
		Initially, SUN is the first day and SAT is the last day of week.
	*/
	initDays: function() {
		var days = this.days;
		for(var i = 0; i < days.length; i++) {
			this.$.days.createComponent({
				kind: "moon.Button",
				classes: "moon-calendar-picker-day",
				disabled: true,
				content: days[i]
			});
		}
	},
	/**
		Compose calendar with number of calendarDate
	*/
	initCalendar: function() {
		if (!this.$.dates.controls.length) {
			for (var i = 1; i <= this.maxWeeks * 7; i++) {
				this.$.dates.createComponent({kind: "moon.CalendarPickerDate"});
			}
		}
	},
	parseDate: function() {
		if (this._tf) {
			return this._tf.format(new ilib.Date.GregDate({unixtime: this.value.getTime(), timezone:"UTC"}));
		} else {
			return this.months[this.value.getMonth()] + " " + this.value.getDate() + ", " + this.value.getFullYear();
		}
	},
	/**
		Month name could be various depends on
	*/
	updateMonthPicker: function() {
		if (typeof ilib !== "undefined") {
			var fmt = new ilib.DateFmt({
				locale: this.locale,
				type: "date",	//only format the date component, not the time
				date: "m",		//'m' is the month of year
				length: "long"	//it uses 2 chars to abbreviate properly
			});
			var monthPickerControls = this.$.monthPicker.getClientControls();
			for (var i = 0; i < 12; i++) {
				var date = ilib.Date.newInstance({unixtime: i * 31 * (24*60*60*1000)});
				monthPickerControls[i].setContent(fmt.format(date));
			}
		}
	},
	/**
		Update days of the week from first day to last day.
	*/
	updateDays: function() {
		var days = this.days;
		var daysControls = this.$.days.getClientControls();
		for(var i = 0; i < days.length; i++) {
			daysControls[i].setContent(days[i]);
		}
	},
	/**
		Sets up the first week of this month.
		Before the first day of this month, days from the previous month will be
		used to fill the calendar.
	*/
	updatePrevMonth: function() {
		var value = this.value;
		var dt = new Date(value.getFullYear(), value.getMonth(), value.getDate());
		dt.setDate(0);
		var thisYear = dt.getFullYear(),
			datesOfPrevMonth = dt.getDate(),
			dayOfLastDate = dt.getDay() - this.firstDayOfWeek,
			prevMonth = dt.getMonth();
		if (dayOfLastDate < 0) {
			dayOfLastDate += 7;
		}
		if (dayOfLastDate !== 6) {
			var dates = this.$.dates.getControls();
			for (var i = 0; i <= dayOfLastDate; i++) {
				dates[i].setValue(new Date(thisYear, prevMonth, datesOfPrevMonth - dayOfLastDate + i));
				dates[i].setColor(1);
			}
			return i;
		}
		return 0;
	},
	/**
		Sets up the last week of this month.
		After the last day of this month, days from the next month will be used to
		fill the calendar.
	*/
	updateNextMonth: function(startIndex) {
		var value = this.value;
		var dt = new Date(value.getFullYear(), value.getMonth(), value.getDate());
		dt.setMonth(dt.getMonth() + 1);
		var thisYear = dt.getFullYear(),
			nextMonth = dt.getMonth();
		var dates = this.$.dates.getControls();
		for (var i = 0; i < this.$.dates.controls.length - startIndex; i++) {
			dates[startIndex + i].setValue(new Date(thisYear, nextMonth, i + 1));
			dates[startIndex + i].setColor(1);
		}
	},
	updateDates: function() {
		var datesOfPrevMonth = this.updatePrevMonth();

		var thisYear = this.value.getFullYear(),
			thisMonth = this.value.getMonth();
		var	monthLength = this.getMonthLength(thisYear, thisMonth);
		var dates = this.$.dates.getControls();
		for (var i = 0; i < monthLength; i++) {
			dates[datesOfPrevMonth + i].setValue(new Date(thisYear, thisMonth, i + 1));
			dates[datesOfPrevMonth + i].setColor(0);
		}
		this.$.dates.setActive(dates[datesOfPrevMonth - 1 + this.value.getDate()]);
		this.updateNextMonth(datesOfPrevMonth + monthLength);
	},
	setYear: function(newYear) {
		var value = this.value,
			newValue,
			newMonthLength = this.getMonthLength(newYear, value.getMonth());
		if(newMonthLength < value.getDate()) {
			newValue = new Date(newYear, value.getMonth(), newMonthLength);
		} else {
			newValue = new Date(newYear, value.getMonth(), value.getDate());
		}
		this.setValue(newValue);
	},
	setMonth: function(newMonth) {
		var value = this.value,
			newValue,
			newMonthLength = this.getMonthLength(value.getFullYear(), newMonth - 1);
		if(newMonthLength < value.getDate()) {
			newValue = new Date(value.getFullYear(), newMonth - 1, newMonthLength);
		} else {
			newValue = new Date(value.getFullYear(), newMonth - 1, value.getDate());
		}
		this.setValue(newValue);
	},
	setDate: function(newDate) {
		var value = this.value,
			newValue,
			monthLength = this.getMonthLength(value.getFullYear(), value.getMonth());
		if(monthLength < newDate) {
			newValue = new Date(value.getFullYear(), value.getMonth(), monthLength);
		} else {
			newValue = new Date(value.getFullYear(), value.getMonth(), newDate);
		}
		this.setValue(newValue);
	},
	/**
		Select a control in calendar with tapping a date button in calendar
	*/
	selectDate: function(inSender, inEvent) {
		if (inEvent.originator.owner.kind == "moon.CalendarPickerDate") {
			var newValue = inEvent.originator.owner.value;
			this.setValue(newValue);
		}
		return true;
	},
	/**
		Select a control in monthPicker using left and right arrow button
	*/
	selectMonthPicker: function(inSender, inEvent) {
		var month = this.$.monthPicker.getSelectedIndex();
		this.value.setMonth(month);
	},
	/**
		Returns number of days in a particular month/year.
		@param inYear
		@param inMonth
		@return Number of dates in given year and month
	*/
	getMonthLength: function(inYear, inMonth) {
		if (typeof ilib !== "undefined") {
			var d = ilib.Date.newInstance({unixtime: this.value.getTime()});
			var cal = ilib.Cal.newInstance({name: d.getCalendar()});
			return cal.getMonLength(inMonth + 1, inYear);
		} else {
			return 32 - new Date(inYear, inMonth, 32).getDate();
		}
	},
	localeChanged: function() {
		if (typeof ilib !== "undefined") {
			this.ilibLocaleInfo = new ilib.LocaleInfo(this.locale);
			this._tf = new ilib.DateFmt({
				locale: this.locale,
				type: "date",	//only format the date component, not the time
				date: "w",		//'w' is the day of the week
				length: "medium"//it uses 3 chars to abbreviate properly
			});
		}
		this.updateMonthPicker();
		this.initDefaults();
		//this.refresh();
		this.doChange({value: this.value});
	},
	valueChanged: function(inOld) {
		if (this.$.monthPicker.getSelectedIndex() != this.value.getMonth()) {
			this.$.monthPicker.setSelectedIndex(this.value.getMonth());
		}
		this.updateDates();
		if (this.value) {
			this.doChange({value: this.value});
		}
	},
	/**
		Sometimes first day of week is channged based on locale changing.
		In this case, destroy day label and reconsturct it.
		Create new ilib.Date instance with time of given day, and get Gregorian
		date instance that represents the first date of the week.

	*/
	firstDayOfWeekChanged: function() {
		var d = ilib.Date.newInstance({unixtime: this.value.getTime()});
		var firstDate = d.onOrBefore(this.firstDayOfWeek);
		var firstTime = firstDate.getTime();	//get unix time
		var days = [];
		for(var i = 0; i < 7; i++) {
			var date = ilib.Date.newInstance({unixtime: i*(24*60*60*1000) + firstTime});
			days.push(this._tf.format(date));
		}
		this.days = days;
		this.updateDays();
		this.updateDates();
	},
	refresh: function(){
		this.initDefaults();
		this.render();
	}
});<commit_msg>Defect Fix - GF-38377 -  Incomplete Month Displays in Calendar<commit_after>/**
	_moon.CalendarPicker_ is a control that displays a monthly calendar, with the
	month name at the top and a grid of days, grouped into rows by week, below.

	The header buttons are used to navigate to the desired month; the desired day
	is selected by tapping on it.

	{kind: "moon.CalendarPicker", content: "Calendar Title"}
*/
enyo.kind({
	name: "moon.CalendarPickerDate",
	kind: "moon.Button",
	small: true,
	marquee: false,
	classes: "moon-calendar-picker-date enyo-unselectable",
	published: {
		value: null,
		color: 0
	},
	create: function() {
		this.inherited(arguments);
		if (typeof ilib !== "undefined") {
			this._tf = new ilib.DateFmt({
				type: "date",	//only format the date component, not the time
				date: "d",		//'d' is the date of month
				length: "short"	//it uses 2 chars to abbreviate properly
			});
		}
	},
	colorChanged: function(inOld) {
		this.addRemoveClass("moon-calendar-picker-date-shadow", this.color);
	},
	valueChanged: function() {
		if (typeof ilib !== "undefined") {
			var date = ilib.Date.newInstance({unixtime: (this.value.getDate() - 1) * (24*60*60*1000)});
			this.setContent(this._tf.format(date));
		} else {
			this.setContent(this.value.getDate());
		}
	}
});

enyo.kind({
	name: "moon.CalendarPicker",
	classes: "moon-calendar-picker",
	events: {
		/**
			Fires when the date changes.

			_inEvent.name_ contains the name of this control.

			_inEvent.value_ contains a standard JavaScript Date object representing
			the current date.
		*/
		onChange: ""
	},
	handlers: {
		ontap: "selectDate"
	},
	published: {
		/**
			ilib locale info instance. It gives information about the paarticular locale.
		*/
		ilibLocaleInfo: null,
		/**
			Current locale used for formatting. May be set after the control is
			created, in which case the control will be updated to reflect the
			new value.
		*/
		locale: "en_us",
		/**
			The current Date object. When a Date object is passed to _setValue_,
			the control is updated to reflect the new value. _getValue_ returns
			a Date object.
		*/
		value: null,
		/**
			The day of week that starts weeks in current locale.
			As a default, Sunday is the first day of week.
		*/
		firstDayOfWeek: 0,
		/**
			The maximum number of weeks to display in a screen.
			If this value is greater than 9, dates two months in the future may be
			shown. Unexpected input may result in errors.
		*/
		maxWeeks: 6,
		months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
		days: ["S","M","T","W","T","F","S"]
	},
	components: [
		{name: "monthPicker", kind: "moon.SimplePicker", onChange: "selectMonthPicker"},
		{name: "days", kind: "enyo.Group"},
		{name: "dates", kind: "enyo.Group"}
	],
	create: function() {
		this.inherited(arguments);
		this.initMonthPicker();
		this.initDays();
		this.initCalendar();

		if (typeof ilib !== "undefined") {
			this._tf = new ilib.DateFmt({
				type: "date",	//only format the date component, not the time
				date: "w",		//'w' is the day of the week
				length: "medium"//it uses 3 chars to abbreviate properly
			});

			this.ilibLocaleInfo = new ilib.LocaleInfo();
			this.setLocale(this.ilibLocaleInfo.locale);
		}
		this.initDefaults();
	},
	initDefaults: function() {
		this.setValue(this.value || new Date());
		//Attempt to use the ilib lib (assuming that it is loaded)
		if (typeof ilib !== "undefined") {
			var dayOfWeek = this.ilibLocaleInfo.getFirstDayOfWeek();
			this.setFirstDayOfWeek(dayOfWeek);
		}
		this.updateDays();
	},
	/**
		Populates SimplePicker with months of the year, from JAN to DEC.
	*/
	initMonthPicker: function() {
		var months = this.months;
		for (var i = 0; i < 12; i++) {
			this.$.monthPicker.createComponent(
				{content: months[i], classes: "picker-content"}
			);
		}
	},
	/**
		Initiate days of the week from first day to last day.
		Initially, SUN is the first day and SAT is the last day of week.
	*/
	initDays: function() {
		var days = this.days;
		for(var i = 0; i < days.length; i++) {
			this.$.days.createComponent({
				kind: "moon.Button",
				classes: "moon-calendar-picker-day",
				disabled: true,
				content: days[i]
			});
		}
	},
	/**
		Compose calendar with number of calendarDate
	*/
	initCalendar: function() {
		if (!this.$.dates.controls.length) {
			for (var i = 1; i <= this.maxWeeks * 7; i++) {
				this.$.dates.createComponent({kind: "moon.CalendarPickerDate"});
			}
		}
	},
	parseDate: function() {
		if (this._tf) {
			return this._tf.format(new ilib.Date.GregDate({unixtime: this.value.getTime(), timezone:"UTC"}));
		} else {
			return this.months[this.value.getMonth()] + " " + this.value.getDate() + ", " + this.value.getFullYear();
		}
	},
	/**
		Month name could be various depends on
	*/
	updateMonthPicker: function() {
		if (typeof ilib !== "undefined") {
			var fmt = new ilib.DateFmt({
				locale: this.locale,
				type: "date",	//only format the date component, not the time
				date: "m",		//'m' is the month of year
				length: "long"	//it uses 2 chars to abbreviate properly
			});
			var monthPickerControls = this.$.monthPicker.getClientControls();
			for (var i = 0; i < 12; i++) {
				var date = ilib.Date.newInstance({unixtime: i * 31 * (24*60*60*1000)});
				monthPickerControls[i].setContent(fmt.format(date));
			}
		}
	},
	/**
		Update days of the week from first day to last day.
	*/
	updateDays: function() {
		var days = this.days;
		var daysControls = this.$.days.getClientControls();
		for(var i = 0; i < days.length; i++) {
			daysControls[i].setContent(days[i]);
		}
	},
	/**
		Sets up the first week of this month.
		Before the first day of this month, days from the previous month will be
		used to fill the calendar.
	*/
	updatePrevMonth: function() {
		var value = this.value;
		var dt = new Date(value.getFullYear(), value.getMonth(), value.getDate());
		dt.setDate(0);
		var thisYear = dt.getFullYear(),
			datesOfPrevMonth = dt.getDate(),
			dayOfLastDate = dt.getDay() - this.firstDayOfWeek,
			prevMonth = dt.getMonth();
		if (dayOfLastDate < 0) {
			dayOfLastDate += 7;
		}
		if (dayOfLastDate !== 6) {
			var dates = this.$.dates.getControls();
			for (var i = 0; i <= dayOfLastDate; i++) {
				dates[i].setValue(new Date(thisYear, prevMonth, datesOfPrevMonth - dayOfLastDate + i));
				dates[i].setColor(1);
			}
			return i;
		}
		return 0;
	},
	/**
		Sets up the last week of this month.
		After the last day of this month, days from the next month will be used to
		fill the calendar.
	*/
	updateNextMonth: function(startIndex) {
		var value = this.value;
		var dt = new Date(value.getFullYear(), value.getMonth(), value.getDate());
		dt.setMonth(dt.getMonth() + 1);
		var thisYear = dt.getFullYear(),
			nextMonth = dt.getMonth();
		var dates = this.$.dates.getControls();
		for (var i = 0; i < this.$.dates.controls.length - startIndex; i++) {
			dates[startIndex + i].setValue(new Date(thisYear, nextMonth, i + 1));
			dates[startIndex + i].setColor(1);
		}
	},
	updateDates: function() {
		var datesOfPrevMonth = this.updatePrevMonth();

		var thisYear = this.value.getFullYear(),
			thisMonth = this.value.getMonth();
		var	monthLength = this.getMonthLength(thisYear, thisMonth);
		var dates = this.$.dates.getControls();
		for (var i = 0; i < monthLength; i++) {
			dates[datesOfPrevMonth + i].setValue(new Date(thisYear, thisMonth, i + 1));
			dates[datesOfPrevMonth + i].setColor(0);
		}
		this.$.dates.setActive(dates[datesOfPrevMonth - 1 + this.value.getDate()]);
		this.updateNextMonth(datesOfPrevMonth + monthLength);
	},
	setYear: function(newYear) {
		var value = this.value,
			newValue,
			newMonthLength = this.getMonthLength(newYear, value.getMonth());
		if(newMonthLength < value.getDate()) {
			newValue = new Date(newYear, value.getMonth(), newMonthLength);
		} else {
			newValue = new Date(newYear, value.getMonth(), value.getDate());
		}
		this.setValue(newValue);
	},
	setMonth: function(newMonth) {
		var value = this.value,
			newValue,
			newMonthLength = this.getMonthLength(value.getFullYear(), newMonth - 1);
		if(newMonthLength < value.getDate()) {
			newValue = new Date(value.getFullYear(), newMonth - 1, newMonthLength);
		} else {
			newValue = new Date(value.getFullYear(), newMonth - 1, value.getDate());
		}
		this.setValue(newValue);
	},
	setDate: function(newDate) {
		var value = this.value,
			newValue,
			monthLength = this.getMonthLength(value.getFullYear(), value.getMonth());
		if(monthLength < newDate) {
			newValue = new Date(value.getFullYear(), value.getMonth(), monthLength);
		} else {
			newValue = new Date(value.getFullYear(), value.getMonth(), newDate);
		}
		this.setValue(newValue);
	},
	/**
		Select a control in calendar with tapping a date button in calendar
	*/
	selectDate: function(inSender, inEvent) {
		if (inEvent.originator.owner.kind == "moon.CalendarPickerDate") {
			var newValue = inEvent.originator.owner.value;
			this.setValue(newValue);
		}
		return true;
	},
	/**
		Select a control in monthPicker using left and right arrow button
	*/
	selectMonthPicker: function(inSender, inEvent) {
		var month = this.$.monthPicker.getSelectedIndex();
		this.value.setMonth(month);
	},
	/**
		Returns number of days in a particular month/year.
		@param inYear
		@param inMonth
		@return Number of dates in given year and month
	*/
	getMonthLength: function(inYear, inMonth) {
		if (typeof ilib !== "undefined") {
			var d = ilib.Date.newInstance({unixtime: this.value.getTime()});
			var cal = ilib.Cal.newInstance({name: d.getCalendar()});
			return cal.getMonLength(inMonth + 1, inYear);
		} else {
			return 32 - new Date(inYear, inMonth, 32).getDate();
		}
	},
	localeChanged: function() {
		if (typeof ilib !== "undefined") {
			this.ilibLocaleInfo = new ilib.LocaleInfo(this.locale);
			this._tf = new ilib.DateFmt({
				locale: this.locale,
				type: "date",	//only format the date component, not the time
				date: "w",		//'w' is the day of the week
				length: "medium"//it uses 3 chars to abbreviate properly
			});
		}
		this.updateMonthPicker();
		this.initDefaults();
		//this.refresh();
		this.doChange({value: this.value});
	},
	valueChanged: function(inOld) {
		if (this.$.monthPicker.getSelectedIndex() != this.value.getMonth()) {
			this.$.monthPicker.setSelectedIndex(this.value.getMonth());
		}
		this.updateDates();
		if (this.value) {
			this.doChange({value: this.value});
		}
	},
	/**
		Sometimes first day of week is channged based on locale changing.
		In this case, destroy day label and reconsturct it.
		Create new ilib.Date instance with time of given day, and get Gregorian
		date instance that represents the first date of the week.

	*/
	firstDayOfWeekChanged: function() {
		var d = ilib.Date.newInstance({unixtime: this.value.getTime()});
		var firstDate = d.onOrBefore(this.firstDayOfWeek);
		var firstTime = firstDate.getTime();	//get unix time
		var days = [];
		for(var i = 0; i < 7; i++) {
			var date = ilib.Date.newInstance({unixtime: i*(24*60*60*1000) + firstTime});
			days.push(this._tf.format(date));
		}
		this.days = days;
		this.updateDays();
		this.updateDates();
	},
	refresh: function(){
		this.initDefaults();
		this.render();
	}
});<|endoftext|>


<commit_before>'use strict';

chrome.runtime.onInstalled.addListener(function (details) {
  console.log('previousVersion', details.previousVersion);
});

console.log('\'Allo \'Allo! Event Page');
<commit_msg>Remove logs<commit_after>'use strict';

chrome.runtime.onInstalled.addListener(function (details) {
  console.log('previousVersion', details.previousVersion);
});
<|endoftext|>


<commit_before>describe("autosubmit-plugin", function() {
    var pattern;

    requireDependencies(["patterns/autosubmit"], function(cls) {
        pattern = cls;
    });

    // Reset the lab before each test
    beforeEach(function() {
        $("#lab *").remove();
    });

    describe("parse", function() {
        it("Shorthand notation", function() {
            var $trigger = $("<input data-pat-autosubmit='500'/>");
                options = pattern.parse($trigger);
            expect(options.delay).toBe("500");
        });

        it("Multiple options not accepted", function() {
            var $trigger = $("<input data-pat-autosubmit='500 && 400'/>");
                options = pattern.parse($trigger);
            expect(options.delay).toBe("500");
        });
    });

    describe("validateOptions", function() {
        var defaultKeyupDelay = 400;  // as defined in patterns/autosubmit.js

        it("Integer delay", function() {
            var options = pattern.validateOptions({delay: 500});
            expect(options.delay).toBe(500);
        });

        it("String integer delay", function() {
            var options = pattern.validateOptions({delay: "500"});
            expect(options.delay).toBe(500);
        });

        it("Delay keyword", function() {
            var options = pattern.validateOptions({delay: "delay"});
            expect(options.delay).toBe(defaultKeyupDelay);
        });

        it("Delay set to true", function() {
            var options = pattern.validateOptions({delay: "true"});
            expect(options.delay).toBe(defaultKeyupDelay);
        });

        it("Invalid syntax", function() {
            var options = pattern.validateOptions({delay: "invalid"});
            expect(options.delay).toBe(defaultKeyupDelay);
        });

        it("Bad type", function() {
            var options = pattern.validateOptions({delay: [500]});
            expect(options.delay).toBe(defaultKeyupDelay);
        });

        it("Negative delay", function() {
            var options = pattern.validateOptions({delay: -500});
            expect(options.delay).toBe(defaultKeyupDelay);
        });

    });
});

// jshint indent: 4, browser: true, jquery: true, quotmark: double
// vim: sw=4 expandtab
<commit_msg>remove autosubmit tests concerned with option validation<commit_after>describe("autosubmit-plugin", function() {
    var pattern;

    requireDependencies(["patterns/autosubmit"], function(cls) {
        pattern = cls;
    });

    // Reset the lab before each test
    beforeEach(function() {
        $("#lab *").remove();
    });

    describe("parse", function() {
        it("Shorthand notation", function() {
            var $trigger = $("<input data-pat-autosubmit='500'/>");
                options = pattern.parser.parse($trigger);
            expect(options.delay).toBe(500);
        });
    });
});

// jshint indent: 4, browser: true, jquery: true, quotmark: double
// vim: sw=4 expandtab
<|endoftext|>


<commit_before>// Copyright (c) 2013, Web Notes Technologies Pvt. Ltd.
// MIT License. See license.txt

// todo
// - basic search in documents

wn.pages['finder'].onload = function(wrapper) {
	wn.ui.make_app_page({
		parent: wrapper,
		title: 'Finder',
		single_column: true
	});
	wrapper.appframe.add_module_icon("Finder");

	var $body = $(wrapper).find(".layout-main").addClass("row");
	var start = 0,
		doctype = null,
		module = null;

	var get_col = function(colsize, icon, label, panel_class) {
		return $('
\

\

 '+label+'
\

\

\

\

').appendTo($body).find(".list-group");
	}

	var $modules = get_col(3, "briefcase", "Modules", "default");
	var $doctypes = get_col(3, "folder-close", "Document Types", "default");
	var $list = get_col(6, "file", "Documents", "info");

	var reset_module = function() {
		$doctypes.empty();
		$('
\
			Select Module
').appendTo($doctypes);
		module = null;
		reset_doctype();
	}

	var reset_doctype = function() {
		$list.empty();
		$('
\
			Select Document Type
').appendTo($list);
		start=0;
	}

	reset_module();

	// modules
	$.each(keys(wn.boot.notification_info.module_doctypes).sort(), function(i, module) {
		$($r('%(module)s\
				',
			{module: module})).appendTo($modules);
	});

	$modules.on("click", ".module-link", function() {
		// list doctypes
		reset_module();

		// select module
		$modules.find(".list-group-item.active").removeClass("active");
		$(this).addClass("active");

		// show doctypes
		$doctypes.find(".row-select").remove();
		module = $(this).attr("data-module");

		$.each(wn.boot.notification_info.module_doctypes[module].sort(), function(i, doctype) {
			$($r('%(doctype)s\
					',
				{doctype: doctype})).appendTo($doctypes)
		});
	});

	$doctypes.on("click", ".doctype-link", function() {
		reset_doctype();

		// select doctype
		$doctypes.find(".list-group-item.active").removeClass("active");
		$(this).addClass("active");

		doctype = $(this).attr("data-doctype");
		render_list();
	})

	$list.on("click", ".btn-more", function() {
		start = start+20;
		render_list();
	});

	$list.on("click", ".btn-search", function() {
		filter_list();
	})

	$list.on("keypress", ".input-search", function(e) {
		if(e.which===13)
			filter_list();
	})

	var filter_list = function() {
		start = 0;
		$list.find(".document-item").remove();
		render_list();
	}

	var render_list = function() {
		// remove more btn if any
		$list.find(".row-more, .row-select").remove();

		// loading indicator...
		add_list_row('')
			.addClass("row-loading text-center")

		var args = {
			doctype: doctype,
			fields: ["name", "modified", "owner"],
			limit_start: start || 0,
			limit_page_length: 20
		};

		if($(".input-search").val()) {
			args.filters = [[doctype, "name", "like", "%" + $(".input-search").val() + "%"]]
		}

		wn.call({
			method: "webnotes.widgets.reportview.get",
			args: args,
			callback: function(r) {
				$list.find(".row-loading").remove();

				if(!$list.find(".input-search").length) {
					// make search
					$('
\

\
							<input type="text" class="form-control input-search">\
							\
								<button class="btn btn-default btn-search" type="button">\
									</button>\
							\

\

').appendTo($list);
				}

				if(r.message.values) {
					$.each(r.message.values, function(i, v) {
						$($r('%(name)s\
							%(owner)s\
							%(modified)s', {
								doctype: doctype,
								name: v[1],
								owner: v[0],
								modified: comment_when(v[2])
							})).appendTo($list);
					})
					if(r.message.values.length==20) {
						add_list_row('More...').addClass("row-more text-center btn-more text-muted");
					}
				} else {
					add_list_row('').addClass("text-center text-muted");
				}
			}
		})
	}

	var add_list_row = function(html) {
		return $(''+html+'').appendTo($list);
	}
}<commit_msg>[minor] fixes to finder added new<commit_after>// Copyright (c) 2013, Web Notes Technologies Pvt. Ltd.
// MIT License. See license.txt

// todo
// - basic search in documents

wn.pages['finder'].onload = function(wrapper) {
	wn.ui.make_app_page({
		parent: wrapper,
		title: wn._('Finder'),
		single_column: true
	});
	wrapper.appframe.add_module_icon("Finder");

	var $body = $(wrapper).find(".layout-main").addClass("row");
	var start = 0,
		doctype = null,
		module = null;

	var get_col = function(colsize, icon, label, panel_class) {
		return $('
\

\

 \
					'+label+'\
					\

\

\

\

\

').appendTo($body);
	}

	var $modules = get_col(3, "briefcase", wn._("Modules"), "default").find(".list-group");
	var $doctypes = get_col(3, "folder-close", wn._("Document Types"), "default").find(".list-group");

	var $list = get_col(6, "file", wn._("Documents"), "info");
	var $doctype_label = $list.find(".col-heading")
	var $new_link = $list.find(".panel-heading .pull-right")
		.html('')
		.click(function() { new_doc(doctype); })
		.toggle(false);
	$list = $list.find(".list-group");

	var reset_module = function() {
		$doctypes.empty();
		$('
'+
			wn._("Select Module")+'
').appendTo($doctypes);
		module = null;
		reset_doctype();
	}

	var reset_doctype = function() {
		$list.empty();
		$new_link.toggle(false);
		$doctype_label.html(wn._("Documents"));
		$('
'+
			wn._("Select Document Type")+'
').appendTo($list);
		start=0;
	}

	reset_module();

	// modules
	$.each(keys(wn.boot.notification_info.module_doctypes).sort(), function(i, module) {
		$($r('%(module)s\
				',
			{module: module})).appendTo($modules);
	});

	$modules.on("click", ".module-link", function() {
		// list doctypes
		reset_module();

		// select module
		$modules.find(".list-group-item.active").removeClass("active");
		$(this).addClass("active");

		// show doctypes
		$doctypes.find(".row-select").remove();
		module = $(this).attr("data-module");

		$.each(wn.boot.notification_info.module_doctypes[module].sort(), function(i, doctype) {
			$($r('%(doctype)s\
					',
				{doctype: doctype})).appendTo($doctypes)
		});
	});

	$doctypes.on("click", ".doctype-link", function() {
		reset_doctype();

		// select doctype
		$doctypes.find(".list-group-item.active").removeClass("active");
		$(this).addClass("active");

		doctype = $(this).attr("data-doctype");

		// label
		$doctype_label.html(wn._(doctype));

		// new link
		$new_link.toggle(!!wn.model.can_create(doctype));

		render_list();
	})

	$list.on("click", ".btn-more", function() {
		start = start+20;
		render_list();
	});

	$list.on("click", ".btn-search", function() {
		filter_list();
	})

	$list.on("keypress", ".input-search", function(e) {
		if(e.which===13)
			filter_list();
	})

	var filter_list = function() {
		start = 0;
		$list.find(".document-item .row-empty").remove();
		render_list();
	}

	var render_list = function() {
		// remove more btn if any
		$list.find(".row-more, .row-select").remove();

		// loading indicator...
		add_list_row('')
			.addClass("row-loading text-center")

		var args = {
			doctype: doctype,
			fields: ["name", "modified", "owner"],
			limit_start: start || 0,
			limit_page_length: 20
		};

		if($(".input-search").val()) {
			args.filters = [[doctype, "name", "like", "%" + $(".input-search").val() + "%"]]
		}

		wn.call({
			method: "webnotes.widgets.reportview.get",
			args: args,
			callback: function(r) {
				$list.find(".row-loading").remove();

				if(!$list.find(".input-search").length) {
					// make search
					$('
\

\
							<input type="text" class="form-control input-search">\
							\
								<button class="btn btn-default btn-search" type="button">\
									</button>\
							\

\

').appendTo($list);
				}

				if(r.message.values) {
					$.each(r.message.values, function(i, v) {
						$($r('%(name)s\
							%(owner)s\
							%(modified)s', {
								doctype: doctype,
								name: v[1],
								owner: v[0],
								modified: comment_when(v[2])
							})).appendTo($list);
					})
					if(r.message.values.length==20) {
						add_list_row('More...').addClass("row-more text-center btn-more text-muted");
					}
				} else {
					add_list_row('').addClass("text-center text-muted row-empty");
				}
			}
		})
	}

	var add_list_row = function(html) {
		return $(''+html+'').appendTo($list);
	}
}<|endoftext|>