<commit_before>define([
    '../views/ModalView',
    'views/Dialog',
    '../rejectionListing/ReadRejectionJustificationView',
    '../models/ServiceItem',
    '../collections/CustomFieldsCollection',
    './jsonForm',
    './PrimarySection',
    './TechnicalSection',
    './OWFSection',
    './ListingTypeSection',
    './FranchiseStoreSection',
    'marketplace',
    'jquery',
    'underscore',
    'handlebars',
    'backbone',
    './fields/RequiredItemsInputField',
    './fields/ResourcesInputField',
    './fields/ScreenshotsInputField',
    './fields/IntentsInputField',
    './fields/ContactsInputField',
    './fields/TagsInputField'
],
function(
    ModalView, Dialog, ReadRejectionJustificationView, ServiceItem, CustomFieldsCollection,
    JsonForm, PrimarySection, TechnicalSection, OWFSection, ListingTypeSection, FranchiseStoreSection,
    Marketplace, $, _, Handlebars, Backbone
) {

    var SuperClass = ModalView,
        $bootstrapActive = $('

'),
        APP_COMPONENT = 'App Component',
        APP = 'OZONE App',
        customFieldsCollection = new CustomFieldsCollection([], {
            context: Marketplace.context
        }),
        customFieldsCollectionFetchPromise = customFieldsCollection.fetch();

    var CreateEditModal = SuperClass.extend({

        id: 'create-edit-listing',

        className: 'bootstrap-active modal hide',

        footer: true,

        animate: false,

        okText: 'Save',
        cancelText: 'Close',

        title: null,

        backdrop: 'static',

        escape: false,

        // serviceItem model when editing or copying
        model: null,

        // serviceItem type object
        type: null,

        template: Handlebars.compile(
            '{{#if title}}' +
                '
' +
                    '{{#if closable}}' +
                        '<button type="button" class="close" data-dismiss="modal" aria-hidden="true" tabindex="-1">×</button>' +
                    '{{/if}}' +
                    '
{{title}}' +
                        '{{#if isRejected}}' +
                            '(why?)' +
                        '{{/if}}' +
                    '
' +
                '
' +
            '{{/if}}' +
            '
{{content}}
' +
            '{{#if footer}}' +
                '
' +
                    '<button class="btn {{okClassName}} ok" data-loading-text="Saving..." data-complete-text="Saved">{{okText}}</button>' +
                    '{{#if cancelText}}' +
                        '<button class="btn {{cancelClassName}} cancel">{{cancelText}}</button>' +
                    '{{/if}}' +
                '
' +
            '{{/if}}'
        ),

        events: _.extend({}, SuperClass.prototype.events, {
            'change [name="types[id]"]': 'updateFields',
            'click .rejection-justification': 'showRejectionJustification'
        }),

        freeTextWarning: _.findWhere(Marketplace.appconfigs, { code: 'free.warning.content' }).value,

        initialize: function (options) {
            SuperClass.prototype.initialize.call(this, options);
            this.type = this.model.get('types');

            this.$form = $('<form class="form-horizontal"></form>');
        },

        _doRender: function () {
            SuperClass.prototype.render.call(this);

            if(this.title) {
                this.$el.find('.modal-header').addClass(this.title.toLowerCase().replace(' ', '-'));
            }

            this.$body.append('
' + _.escape(this.freeTextWarning) + '
');

            this.jsonForm = new JsonForm.Form(this.$form, [
                this.buildPrimarySection(),
                this.buildTechnicalSection(),
                this.buildFranchiseStoreSection()
            ]).render();

            this.primarySection = this.jsonForm.fields[0];
            this.technicalSection = this.jsonForm.fields[1];
            this.franchiseStoreSection = this.jsonForm.fields[2];

            this.updateFields();

            this.doLayout();
            this.$form.appendTo(this.$body);

            this.initTooltips();
        },

        render: function () {
            var me = this,
                dfd = $.Deferred();

            customFieldsCollectionFetchPromise
                .done(function () {
                    me._doRender();
                    dfd.resolve();
                })
                .fail(function () {
                    dfd.reject();
                });

            return dfd.promise();
        },

        isRejected: function () {
            return this.model.isRejected();
        },

        initTooltips: function () {
            var $tooltips = this.$form.find('[data-toggle="tooltip"]');
            if($tooltips.length) {
                this.$tooltips = $tooltips.tooltip({
                    container: this.$el
                });
            }
        },

        destroyTooltips: function () {
            if(this.$tooltips) {
                this.$tooltips = this.$tooltips.tooltip('destroy');
            }
        },

        showRejectionJustification: function (evt) {
            if(!this.model.isRejected()) { return; }

            var $body = this.$el,
                readRejectionJustificationView = new ReadRejectionJustificationView({
                    model: this.model
                });

            readRejectionJustificationView.render().promise().then(function () {
                this.$rejectionJustificationPopover = $(evt.target).popover({
                    trigger: 'manual',
                    placement: 'bottom',
                    cls: 'rejection-justification-popover',
                    container: $body,
                    title: 'Rejection Justification',
                    html: true,
                    content: readRejectionJustificationView.el
                }).popover('toggle');
            });
        },

        updateFields: function (evt) {
            var type = evt ? evt.added : this.type,
                listingTypeSectionIndex;

            if(!type) {
                return;
            }

            this.$form.detach();
            this.destroyTooltips();

            this.type = type;

            console.log('Type selected:', type.title);

            if(type.title === APP || !type.ozoneAware) {
                this.jsonForm.remove(this.owfSection);
                this.owfSection = null;
            }

            if(type.title === APP) {
                this.listingTypeSection = this.listingTypeSection ?
                                            this.jsonForm.update(this.listingTypeSection, this.buildListingTypeSection()) :
                                            this.jsonForm.add(this.buildListingTypeSection(), 2);
            }
            else {
                if(type.ozoneAware) {
                    if(!this.owfSection) {
                        this.owfSection = this.jsonForm.add(this.buildOWFSection(), 2);
                    }
                    listingTypeSectionIndex = 3;
                }
                else {
                    listingTypeSectionIndex = 2;
                }
                this.listingTypeSection = this.listingTypeSection ?
                                            this.jsonForm.update(this.listingTypeSection, this.buildListingTypeSection()) :
                                            this.jsonForm.add(this.buildListingTypeSection(), listingTypeSectionIndex);
            }

            // these sections only need to be updated if this method is invoked because of select-change event
            if(evt && evt.added) {
                this.primarySection = this.jsonForm.update(this.primarySection, this.buildPrimarySection());
                this.technicalSection = this.jsonForm.update(this.technicalSection, this.buildTechnicalSection());
                this.franchiseStoreSection = this.jsonForm.update(this.franchiseStoreSection, this.buildFranchiseStoreSection());
            }

            this.$form.appendTo(this.$body);
            this.initTooltips();
            this.doLayout();
        },

        /**
        * Forces layout in IE7.
        * For some reasoe IE7, doesn't layout fields that are not in view correctly.
        **/
        doLayout: function () {
            if($.browser.msie && $.browser.versionNumber === 7) {
                this.$form.find('*').each(function (i, elem) {
                    var $elem = $(elem);
                    if($elem.css('position') !== 'absolute') {
                        $elem.css('position', 'relative');
                    }
                });
            }
        },

        buildPrimarySection: function () {
            return PrimarySection.build(this.model, this.type, customFieldsCollection);
        },

        buildTechnicalSection: function () {
            return TechnicalSection.build(this.model, this.type, customFieldsCollection);
        },

        buildOWFSection: function () {
            return OWFSection.build(this.model);
        },

        buildListingTypeSection: function () {
            return ListingTypeSection.build(this.model, this.type, customFieldsCollection);
        },

        buildFranchiseStoreSection: function () {
            return FranchiseStoreSection.build(this.model, this.type, customFieldsCollection);
        },

        ok: function () {
            console.log('Saving listing...');
            var me = this,
                formJson = me.$form.serializeObject();

            me.$form
                .one('submit', function () {
                    me.save(formJson);
                    return false;
                }).submit();
        },

        cancel: function (evt) {
            console.log('Cancelled ' + this.title);

            var me = this,
                $btn = $(evt.target);

            $btn.confirm({
                trigger: 'manual',
                placement: 'top',
                container: this.$el,
                title: 'Discard changes',
                content: 'If you click OK, changes will not be saved.',
                ok: function() {
                    $btn.confirm('destroy');
                    me.hide();
                }
            }).confirm('toggle');
        },

        onShown: function () {
            SuperClass.prototype.onShown.apply(this, arguments);

            this.$form.validate();
        },

        onHidden: function () {
            this.jsonForm.remove();
        },

        remove: function () {
            if(this.$rejectionJustificationPopover) {
                this.$rejectionJustificationPopover.popover('destroy');
            }
            SuperClass.prototype.remove.call(this);
            $bootstrapActive.remove();
        },

        save: function (json) {
            if(!this.$form.valid()) {
                return false;
            }

            var me = this,
                $ok = this.$el.find('.btn.ok');

            _.forEach(['agency', 'state', 'types'], function (prop) {
                var id = json[prop].id;

                if(id === '') {
                    delete json[prop];
                }
                else {
                    json[prop].id = parseInt(id, 10);
                }

            });

            _.each(['categories', 'relatedItems', 'owners'], function (key, i) {
                var values = json[key];
                if(values && values.length) {
                    values = _.map(values, function (id) {
                        return { id: parseInt(id, 10) };
                    });
                    json[key] = values;
                }
            });

            json.customFields = _.compact(json.customFields);

            json.relationships = [{
                relationshipType: 'REQUIRE',
                relatedItems: json.relatedItems || []
            }];
            delete json.relatedItems;

            !json.contacts && (json.contacts = []);
            !json.docUrls && (json.docUrls = []);
            !json.screenshots && (json.screenshots = []);

            var ozoneAware = _.findWhere(Marketplace.types, { id : json.types.id }).ozoneAware;
            !ozoneAware && (delete json.owfProperties);
            json.owfProperties && !json.owfProperties.intents && (json.owfProperties.intents = []);

            console.log('Saving...' + json.title);
            console.log(json);

            $ok.button('loading');

            this.model
                    .set(json)
                    .save()
                    .then(function(response) {
                        var dfd = $.Deferred(),
                            tags = me.model.tags();

                        tags = tags.filter(function (model) {
                            return !model.get('tag');
                        });

                        if(tags.length === 0) {
                            dfd.resolve();
                        }
                        else {

                            console.log(tags);

                            _.each(tags, function(tag){
                                tag.set('serviceItemId', response.id);
                            });

                            return me.model.saveTags(tags);
                        }

                        return dfd.promise();
                    })
                    .then(function () {
                        return me.hide();
                    })
                    .done(function () {
                        Backbone.history.stop();
                        window.location = window.location.href.replace(/#.*/, '') + '#quickview/' + me.model.get('id') + '/admin';
                        window.location.reload();
                    })
                    .fail(function (jqXHR, textStatus, errorThrown) {
                        $ok.button('reset');
                        var resolvedMessges = jqXHR.responseJSON.resolvedMessges;
                        Dialog.show('Server Error!', resolvedMessges ? resolvedMessges.join('

') : errorThrown);
                    });
        }

    });

    function renderAndShowModal (modal) {
        modal.render().then(function () {
            $bootstrapActive.appendTo(document.body).append(modal.el);
            modal.show();
        });
    }

    return {
        create: function () {
            var modal = new CreateEditModal({
                title: 'Draft',
                model: new ServiceItem()
            });

            renderAndShowModal(modal);
        },
        edit: function (model) {
            var modal = new CreateEditModal({
                title: model.get('approvalStatus'),
                model: model
            });

            renderAndShowModal(modal);
        },
        copy: function (model) {
            var modal = new CreateEditModal({
                title: 'Draft',
                model: model.clone()
            });

            renderAndShowModal(modal);
        }
    };

});
<commit_msg>OP-6048: Fixing ServiceItem copy<commit_after>define([
    '../views/ModalView',
    'views/Dialog',
    '../rejectionListing/ReadRejectionJustificationView',
    '../models/ServiceItem',
    '../collections/CustomFieldsCollection',
    './jsonForm',
    './PrimarySection',
    './TechnicalSection',
    './OWFSection',
    './ListingTypeSection',
    './FranchiseStoreSection',
    'marketplace',
    'jquery',
    'underscore',
    'handlebars',
    'backbone',
    './fields/RequiredItemsInputField',
    './fields/ResourcesInputField',
    './fields/ScreenshotsInputField',
    './fields/IntentsInputField',
    './fields/ContactsInputField',
    './fields/TagsInputField'
],
function(
    ModalView, Dialog, ReadRejectionJustificationView, ServiceItem, CustomFieldsCollection,
    JsonForm, PrimarySection, TechnicalSection, OWFSection, ListingTypeSection, FranchiseStoreSection,
    Marketplace, $, _, Handlebars, Backbone
) {

    var SuperClass = ModalView,
        $bootstrapActive = $('

'),
        APP_COMPONENT = 'App Component',
        APP = 'OZONE App',
        customFieldsCollection = new CustomFieldsCollection([], {
            context: Marketplace.context
        }),
        customFieldsCollectionFetchPromise = customFieldsCollection.fetch();

    var CreateEditModal = SuperClass.extend({

        id: 'create-edit-listing',

        className: 'bootstrap-active modal hide',

        footer: true,

        animate: false,

        okText: 'Save',
        cancelText: 'Close',

        title: null,

        backdrop: 'static',

        escape: false,

        // serviceItem model when editing or copying
        model: null,

        // serviceItem type object
        type: null,

        template: Handlebars.compile(
            '{{#if title}}' +
                '
' +
                    '{{#if closable}}' +
                        '<button type="button" class="close" data-dismiss="modal" aria-hidden="true" tabindex="-1">×</button>' +
                    '{{/if}}' +
                    '
{{title}}' +
                        '{{#if isRejected}}' +
                            '(why?)' +
                        '{{/if}}' +
                    '
' +
                '
' +
            '{{/if}}' +
            '
{{content}}
' +
            '{{#if footer}}' +
                '
' +
                    '<button class="btn {{okClassName}} ok" data-loading-text="Saving..." data-complete-text="Saved">{{okText}}</button>' +
                    '{{#if cancelText}}' +
                        '<button class="btn {{cancelClassName}} cancel">{{cancelText}}</button>' +
                    '{{/if}}' +
                '
' +
            '{{/if}}'
        ),

        events: _.extend({}, SuperClass.prototype.events, {
            'change [name="types[id]"]': 'updateFields',
            'click .rejection-justification': 'showRejectionJustification'
        }),

        freeTextWarning: _.findWhere(Marketplace.appconfigs, { code: 'free.warning.content' }).value,

        initialize: function (options) {
            SuperClass.prototype.initialize.call(this, options);
            this.type = this.model.get('types');

            this.$form = $('<form class="form-horizontal"></form>');
        },

        _doRender: function () {
            SuperClass.prototype.render.call(this);

            if(this.title) {
                this.$el.find('.modal-header').addClass(this.title.toLowerCase().replace(' ', '-'));
            }

            this.$body.append('
' + _.escape(this.freeTextWarning) + '
');

            this.jsonForm = new JsonForm.Form(this.$form, [
                this.buildPrimarySection(),
                this.buildTechnicalSection(),
                this.buildFranchiseStoreSection()
            ]).render();

            this.primarySection = this.jsonForm.fields[0];
            this.technicalSection = this.jsonForm.fields[1];
            this.franchiseStoreSection = this.jsonForm.fields[2];

            this.updateFields();

            this.doLayout();
            this.$form.appendTo(this.$body);

            this.initTooltips();
        },

        render: function () {
            var me = this,
                dfd = $.Deferred();

            customFieldsCollectionFetchPromise
                .done(function () {
                    me._doRender();
                    dfd.resolve();
                })
                .fail(function () {
                    dfd.reject();
                });

            return dfd.promise();
        },

        isRejected: function () {
            return this.model.isRejected();
        },

        initTooltips: function () {
            var $tooltips = this.$form.find('[data-toggle="tooltip"]');
            if($tooltips.length) {
                this.$tooltips = $tooltips.tooltip({
                    container: this.$el
                });
            }
        },

        destroyTooltips: function () {
            if(this.$tooltips) {
                this.$tooltips = this.$tooltips.tooltip('destroy');
            }
        },

        showRejectionJustification: function (evt) {
            if(!this.model.isRejected()) { return; }

            var $body = this.$el,
                readRejectionJustificationView = new ReadRejectionJustificationView({
                    model: this.model
                });

            readRejectionJustificationView.render().promise().then(function () {
                this.$rejectionJustificationPopover = $(evt.target).popover({
                    trigger: 'manual',
                    placement: 'bottom',
                    cls: 'rejection-justification-popover',
                    container: $body,
                    title: 'Rejection Justification',
                    html: true,
                    content: readRejectionJustificationView.el
                }).popover('toggle');
            });
        },

        updateFields: function (evt) {
            var type = evt ? evt.added : this.type,
                listingTypeSectionIndex;

            if(!type) {
                return;
            }

            this.$form.detach();
            this.destroyTooltips();

            this.type = type;

            console.log('Type selected:', type.title);

            if(type.title === APP || !type.ozoneAware) {
                this.jsonForm.remove(this.owfSection);
                this.owfSection = null;
            }

            if(type.title === APP) {
                this.listingTypeSection = this.listingTypeSection ?
                                            this.jsonForm.update(this.listingTypeSection, this.buildListingTypeSection()) :
                                            this.jsonForm.add(this.buildListingTypeSection(), 2);
            }
            else {
                if(type.ozoneAware) {
                    if(!this.owfSection) {
                        this.owfSection = this.jsonForm.add(this.buildOWFSection(), 2);
                    }
                    listingTypeSectionIndex = 3;
                }
                else {
                    listingTypeSectionIndex = 2;
                }
                this.listingTypeSection = this.listingTypeSection ?
                                            this.jsonForm.update(this.listingTypeSection, this.buildListingTypeSection()) :
                                            this.jsonForm.add(this.buildListingTypeSection(), listingTypeSectionIndex);
            }

            // these sections only need to be updated if this method is invoked because of select-change event
            if(evt && evt.added) {
                this.primarySection = this.jsonForm.update(this.primarySection, this.buildPrimarySection());
                this.technicalSection = this.jsonForm.update(this.technicalSection, this.buildTechnicalSection());
                this.franchiseStoreSection = this.jsonForm.update(this.franchiseStoreSection, this.buildFranchiseStoreSection());
            }

            this.$form.appendTo(this.$body);
            this.initTooltips();
            this.doLayout();
        },

        /**
        * Forces layout in IE7.
        * For some reasoe IE7, doesn't layout fields that are not in view correctly.
        **/
        doLayout: function () {
            if($.browser.msie && $.browser.versionNumber === 7) {
                this.$form.find('*').each(function (i, elem) {
                    var $elem = $(elem);
                    if($elem.css('position') !== 'absolute') {
                        $elem.css('position', 'relative');
                    }
                });
            }
        },

        buildPrimarySection: function () {
            return PrimarySection.build(this.model, this.type, customFieldsCollection);
        },

        buildTechnicalSection: function () {
            return TechnicalSection.build(this.model, this.type, customFieldsCollection);
        },

        buildOWFSection: function () {
            return OWFSection.build(this.model);
        },

        buildListingTypeSection: function () {
            return ListingTypeSection.build(this.model, this.type, customFieldsCollection);
        },

        buildFranchiseStoreSection: function () {
            return FranchiseStoreSection.build(this.model, this.type, customFieldsCollection);
        },

        ok: function () {
            console.log('Saving listing...');
            var me = this,
                formJson = me.$form.serializeObject();

            me.$form
                .one('submit', function () {
                    me.save(formJson);
                    return false;
                }).submit();
        },

        cancel: function (evt) {
            console.log('Cancelled ' + this.title);

            var me = this,
                $btn = $(evt.target);

            $btn.confirm({
                trigger: 'manual',
                placement: 'top',
                container: this.$el,
                title: 'Discard changes',
                content: 'If you click OK, changes will not be saved.',
                ok: function() {
                    $btn.confirm('destroy');
                    me.hide();
                }
            }).confirm('toggle');
        },

        onShown: function () {
            SuperClass.prototype.onShown.apply(this, arguments);

            this.$form.validate();
        },

        onHidden: function () {
            this.jsonForm.remove();
        },

        remove: function () {
            if(this.$rejectionJustificationPopover) {
                this.$rejectionJustificationPopover.popover('destroy');
            }
            SuperClass.prototype.remove.call(this);
            $bootstrapActive.remove();
        },

        save: function (json) {
            if(!this.$form.valid()) {
                return false;
            }

            var me = this,
                $ok = this.$el.find('.btn.ok');

            _.forEach(['agency', 'state', 'types'], function (prop) {
                var id = json[prop].id;

                if(id === '') {
                    delete json[prop];
                }
                else {
                    json[prop].id = parseInt(id, 10);
                }

            });

            _.each(['categories', 'relatedItems', 'owners'], function (key, i) {
                var values = json[key];
                if(values && values.length) {
                    values = _.map(values, function (id) {
                        return { id: parseInt(id, 10) };
                    });
                    json[key] = values;
                }
            });

            json.customFields = _.compact(json.customFields);

            json.relationships = [{
                relationshipType: 'REQUIRE',
                relatedItems: json.relatedItems || []
            }];
            delete json.relatedItems;

            !json.contacts && (json.contacts = []);
            !json.docUrls && (json.docUrls = []);
            !json.screenshots && (json.screenshots = []);

            var ozoneAware = _.findWhere(Marketplace.types, { id : json.types.id }).ozoneAware;
            !ozoneAware && (delete json.owfProperties);
            json.owfProperties && !json.owfProperties.intents && (json.owfProperties.intents = []);

            console.log('Saving...' + json.title);
            console.log(json);

            $ok.button('loading');

            this.model
                    .set(json)
                    .save()
                    .then(function(response) {
                        var dfd = $.Deferred(),
                            tags = me.model.tags();

                        tags = tags.filter(function (model) {
                            return !model.get('tag');
                        });

                        if(tags.length === 0) {
                            dfd.resolve();
                        }
                        else {

                            console.log(tags);

                            _.each(tags, function(tag){
                                tag.set('serviceItemId', response.id);
                            });

                            return me.model.saveTags(tags);
                        }

                        return dfd.promise();
                    })
                    .then(function () {
                        return me.hide();
                    })
                    .done(function () {
                        Backbone.history.stop();
                        window.location = window.location.href.replace(/#.*/, '') + '#quickview/' + me.model.get('id') + '/admin';
                        window.location.reload();
                    })
                    .fail(function (jqXHR, textStatus, errorThrown) {
                        $ok.button('reset');
                        var resolvedMessges = jqXHR.responseJSON.resolvedMessges;
                        Dialog.show('Server Error!', resolvedMessges ? resolvedMessges.join('

') : errorThrown);
                    });
        }

    });

    function renderAndShowModal (modal) {
        modal.render().then(function () {
            $bootstrapActive.appendTo(document.body).append(modal.el);
            modal.show();
        });
    }

    return {
        create: function () {
            var modal = new CreateEditModal({
                title: 'Draft',
                model: new ServiceItem()
            });

            renderAndShowModal(modal);
        },
        edit: function (model) {
            var modal = new CreateEditModal({
                title: model.get('approvalStatus'),
                model: model
            });

            renderAndShowModal(modal);
        },
        copy: function (model) {
            var newModel = model.clone();
            newModel.set('uuid', null);

            var modal = new CreateEditModal({
                title: 'Draft',
                model: newModel
            });

            renderAndShowModal(modal);
        }
    };

});
<|endoftext|>


<commit_before>import {inject} from 'aurelia-dependency-injection';
import {
  ObserverLocator,
  calcSplices,
  getChangeRecords,
  createOverrideContext
} from 'aurelia-binding';
import {
  BoundViewFactory,
  ViewSlot,
  TargetInstruction,
  customAttribute,
  bindable,
  templateController
} from 'aurelia-templating';
import {
  updateOverrideContext,
  createFullOverrideContext,
  updateOverrideContexts,
  getItemsSourceExpression,
  isOneTime,
  unwrapExpression
} from 'aurelia-templating-resources/repeat-utilities';
import {viewsRequireLifecycle} from 'aurelia-templating-resources/analyze-view-factory';
import {
  calcScrollHeight,
  calcOuterHeight,
  getNthNode,
  moveViewFirst,
  moveViewLast
} from './utilities';
import {VirtualRepeatStrategyLocator} from './virtual-repeat-strategy-locator';
import {ViewStrategyLocator} from './view-strategy';

@customAttribute('virtual-repeat')
@templateController
@inject(Element, BoundViewFactory, TargetInstruction, ViewSlot, ObserverLocator, VirtualRepeatStrategyLocator, ViewStrategyLocator)
export class VirtualRepeat {
  _first = 0;
  _previousFirst = 0;
  _viewsLength = 0;
  _lastRebind = 0;
  _topBufferHeight = 0;
  _bottomBufferHeight = 0;
  _bufferSize = 5;
  _scrollingDown = false;
  _scrollingUp = false;
  _switchedDirection = false;
  _isAttached = false;
  _ticking = false;

  @bindable items
  @bindable local
  constructor(element, viewFactory, instruction, viewSlot, observerLocator, strategyLocator, viewStrategyLocator){
    this.element = element;
    this.viewFactory = viewFactory;
    this.instruction = instruction;
    this.viewSlot = viewSlot;
    this.observerLocator = observerLocator;
    this.strategyLocator = strategyLocator;
    this.viewStrategyLocator = viewStrategyLocator;
    this.local = 'item';
    this.sourceExpression = getItemsSourceExpression(this.instruction, 'virtual-repeat.for');
    this.isOneTime = isOneTime(this.sourceExpression);
    this.viewsRequireLifecycle = viewsRequireLifecycle(viewFactory);
  }

  attached(){
    this._isAttached = true;
    let element = this.element;
    this.viewStrategy = this.viewStrategyLocator.getStrategy(element);
    this.scrollList = this.viewStrategy.getScrollList(element);
    this.scrollContainer = this.viewStrategy.getScrollContainer(element);
    this.topBuffer = this.viewStrategy.createTopBufferElement(this.scrollList, element);
    this.bottomBuffer = this.viewStrategy.createBottomBufferElement(this.scrollList, element);
    this.itemsChanged();
    this.scrollListener = () => this._onScroll();
    this.scrollContainer.addEventListener('scroll', this.scrollListener);
  }

  bind(bindingContext, overrideContext){
    this.scope = { bindingContext, overrideContext };
    this._itemsLength = this.items.length;

    // TODO Fix this
    window.onresize = () => { this._handleResize(); };
  }

  call(context, changes) {
    this[context](this.items, changes);
  }

  detached() {
    this.scrollContainer.removeEventListener('scroll');
    this._first = 0;
    this._previousFirst = 0;
    this._viewsLength = 0;
    this._lastRebind = 0;
    this._topBufferHeight = 0;
    this._bottomBufferHeight = 0;
    this._scrollingDown = false;
    this._scrollingUp = false;
    this._switchedDirection = false;
    this._isAttached = false;
    this.viewStrategy.removeBufferElements(this.scrollList, this.topBuffer, this.bottomBuffer);
    this.isLastIndex = false;
    this.scrollList = null;
    this.scrollContainer = null;
    this.scrollContainerHeight = null;
    this.viewSlot.removeAll(true);
    if(this.scrollHandler) {
      this.scrollHandler.dispose();
    }
    this._unsubscribeCollection();

  }

  itemsChanged() {
    this._unsubscribeCollection();
    // still bound?
    if (!this.scope) {
      return;
    }
    let items = this.items;
    this.strategy = this.strategyLocator.getStrategy(items);
    this.strategy.createFirstItem(this);
    this._calcInitialHeights();
    if (!this.isOneTime && !this._observeInnerCollection()) {
      this._observeCollection();
    }

    this.strategy.instanceChanged(this, items, this._viewsLength);
  }

  unbind(){
    this.scope = null;
    this.items = null;
    this._itemsLength = null;
  }

  handleCollectionMutated(collection, changes) {
    this._itemsLength = collection.length;
    this.strategy.instanceMutated(this, collection, changes);
  }

  handleInnerCollectionMutated(collection, changes) {
    // guard against source expressions that have observable side-effects that could
    // cause an infinite loop- eg a value converter that mutates the source array.
    if (this.ignoreMutation) {
      return;
    }
    this.ignoreMutation = true;
    let newItems = this.sourceExpression.evaluate(this.scope, this.lookupFunctions);
    this.observerLocator.taskQueue.queueMicroTask(() => this.ignoreMutation = false);

    // call itemsChanged...
    if (newItems === this.items) {
      // call itemsChanged directly.
      this.itemsChanged();
    } else {
      // call itemsChanged indirectly by assigning the new collection value to
      // the items property, which will trigger the self-subscriber to call itemsChanged.
      this.items = newItems;
    }
  }

  _onScroll() {
    if(!this._ticking) {
      requestAnimationFrame(() => this._handleScroll());
      this._ticking = true;
    }
  }

  _handleScroll() {
    if (!this._isAttached) {
      return;
    }
    let itemHeight = this.itemHeight;
    let scrollTop = this.scrollContainer.scrollTop;
    this._first = Math.floor(scrollTop / itemHeight);
    this._checkScrolling();
    // TODO if and else paths do almost same thing, refactor?
    // move views down?
    if(this._isScrolling && this._scrollingDown && (this._hasScrolledDownTheBuffer() || (this._switchedDirection && this._hasScrolledDownTheBufferFromTop()))) {
      let viewsToMove = this._first - this._lastRebind;
      if(this._switchedDirection) {
        viewsToMove = this.isAtTop ? this._first : this._bufferSize - (this._lastRebind - this._first);
      }
      this.isAtTop = false;
      this._lastRebind = this._first;
      let movedViewsCount = this._moveViews(viewsToMove);
      let adjustHeight = movedViewsCount < viewsToMove ? this._bottomBufferHeight : itemHeight * movedViewsCount;
      var test = 0
      this._switchedDirection = false;
      this._topBufferHeight = this._topBufferHeight + adjustHeight;
      this._bottomBufferHeight = this._bottomBufferHeight - adjustHeight;
      if (this._bottomBufferHeight >= 0) {
        this._adjustBufferHeights();
      }
    // move view up?
  } else if (this._isScrolling && this._scrollingUp && (this._hasScrolledUpTheBuffer() || (this._switchedDirection && this._hasScrolledUpTheBufferFromBottom()))) {
      let viewsToMove = this._lastRebind - this._first;
      if(this._switchedDirection) {
          if(this.isLastIndex) {
            viewsToMove = this.items.length - this._first - this.elementsInView;
          } else {
            viewsToMove = this._bufferSize - (this._first - this._lastRebind);
          }
      }
      this.isLastIndex = false;
      this._lastRebind = this._first;
      let movedViewsCount = this._moveViews(viewsToMove);
      this.movedViewsCount = movedViewsCount;
      let adjustHeight = movedViewsCount < viewsToMove ? this._topBufferHeight : itemHeight * movedViewsCount;
      this._switchedDirection = false;
      this._topBufferHeight = this._topBufferHeight - adjustHeight;
      this._bottomBufferHeight = this._bottomBufferHeight + adjustHeight;
      if (this._topBufferHeight >= 0) {
        this._adjustBufferHeights();
      }
    }
    this._previousFirst = this._first;

    this._ticking = false;
  }

  _handleResize() {
    var children = this.viewSlot.children,
      childrenLength = children.length,
      overrideContext, view, addIndex;

    this.scrollContainerHeight = calcScrollHeight(this.scrollContainer);
    this._viewsLength = Math.ceil(this.scrollContainerHeight / this.itemHeight) + 1;

    if(this._viewsLength > childrenLength){
      addIndex = children[childrenLength - 1].overrideContext.$index + 1;
      overrideContext = createFullOverrideContext(this, this.items[addIndex], addIndex, this.items.length);
      view = this.viewFactory.create();
      view.bind(overrideContext.bindingContext, overrideContext);
      this.viewSlot.insert(childrenLength, view);
    }else if (this._viewsLength < childrenLength){
      this._viewsLength = childrenLength;
    }
  }

  _checkScrolling() {
    if (this._first > this._previousFirst && (this._bottomBufferHeight > 0 || !this.isLastIndex)) {
      if (!this._scrollingDown) {
        this._scrollingDown = true;
        this._scrollingUp = false;
        this._switchedDirection = true;
      } else {
        this._switchedDirection = false;
      }
      this._isScrolling = true;
    } else if (this._first < this._previousFirst && (this._topBufferHeight >= 0 || !this.isAtTop)) {
      if (!this._scrollingUp) {
        this._scrollingDown = false;
        this._scrollingUp = true;
        this._switchedDirection = true;
      } else {
        this._switchedDirection = false;
      }
      this._isScrolling = true;
    } else {
      this._isScrolling = false;
    }
  }

  _hasScrolledDownTheBuffer() {
    let atBottom = (this._first + this._viewsLength) >= this.items.length;
    let itemsAddedWhileAtBottom = atBottom && this._first > this._lastRebind;
    return this._first - this._lastRebind >= this._bufferSize || itemsAddedWhileAtBottom;
  }

  _hasScrolledDownTheBufferFromTop() {
    return this._first - this._bufferSize > 0;
  }

  _hasScrolledUpTheBuffer() {
    return this._lastRebind - this._first >= this._bufferSize;
  }

  _hasScrolledUpTheBufferFromBottom() {
    return this._first + this._bufferSize < this.items.length;
  }

  _adjustBufferHeights() {
    this.topBuffer.setAttribute('style', `height:  ${this._topBufferHeight}px`);
    this.bottomBuffer.setAttribute("style", `height: ${this._bottomBufferHeight}px`);
  }

  _unsubscribeCollection() {
    if (this.collectionObserver) {
      this.collectionObserver.unsubscribe(this.callContext, this);
      this.collectionObserver = null;
      this.callContext = null;
    }
  }

  _moveViews(length) {
    let getNextIndex = this._scrollingDown ? (index, i) =>  index + i : (index, i) =>  index - i;
    let isAtFirstOrLastIndex = () => this._scrollingDown ? this.isLastIndex : this.isAtTop;
    let viewSlot = this.viewSlot;
    let childrenLength = viewSlot.children.length;
    let viewIndex = this._scrollingDown ? 0 : childrenLength - 1;
    let items = this.items;
    let scrollList = this.scrollList;
    let index = this._scrollingDown ? this._getIndexOfLastView() + 1 : this._getIndexOfFirstView() - 1;
    let i = 0;
    while(i < length && !isAtFirstOrLastIndex()) {
      let view = viewSlot.children[viewIndex];
      let nextIndex = getNextIndex(index, i);
      updateOverrideContext(view.overrideContext, nextIndex, items.length);
      view.bindingContext[this.local] = items[nextIndex];
      if(this._scrollingDown) {
        viewSlot.children.push(viewSlot.children.shift());
        this.viewStrategy.moveViewLast(view, scrollList, childrenLength);
        this.isLastIndex = nextIndex >= items.length - 1;
      } else {
        viewSlot.children.unshift(viewSlot.children.splice(-1,1)[0]);
        this.viewStrategy.moveViewFirst(view, scrollList);
        this.isAtTop = nextIndex <= 0;
      }
      i++;
    }
    return length - (length - i);
  }

  _getIndexOfLastView(){
    let children = this.viewSlot.children;
    return children[children.length - 1].overrideContext.$index;
  }

  _getIndexOfFirstView(){
    let children = this.viewSlot.children;
    return children[0].overrideContext.$index;
  }

  _calcInitialHeights() {
    if (this._viewsLength > 0 && this._itemsLength == this.items.length) {
      return;
    }
    this._itemsLength = this.items.length;
    let listItems = this.scrollList.children;
    this.itemHeight = calcOuterHeight(listItems[1]);
    this.scrollContainerHeight = calcScrollHeight(this.scrollContainer);
    this.elementsInView = Math.ceil(this.scrollContainerHeight / this.itemHeight) + 1;
    this._viewsLength = (this.elementsInView * 2) + this._bufferSize;
    this._bottomBufferHeight = this.itemHeight * this.items.length - this.itemHeight * this._viewsLength;
    this.bottomBuffer.setAttribute("style", `height: ${this._bottomBufferHeight}px`);
    this._topBufferHeight = 0;
    this.topBuffer.setAttribute("style", `height: ${this._topBufferHeight}px`);
    // TODO This will cause scrolling back to top when swapping collection instances that have different lengths - instead should keep the scroll position
    this.scrollContainer.scrollTop = 0;
    this._first = 0;
  }

  _observeInnerCollection() {
    let items = this._getInnerCollection();
    let strategy = this.strategyLocator.getStrategy(items);
    if (!strategy) {
      return false;
    }
    this.collectionObserver = strategy.getCollectionObserver(this.observerLocator, items);
    if (!this.collectionObserver) {
      return false;
    }
    this.callContext = 'handleInnerCollectionMutated';
    this.collectionObserver.subscribe(this.callContext, this);
    return true;
  }

  _getInnerCollection() {
    let expression = unwrapExpression(this.sourceExpression);
    if (!expression) {
      return null;
    }
    return expression.evaluate(this.scope, null);
  }

  _observeCollection() {
    let items = this.items;
    this.collectionObserver = this.strategy.getCollectionObserver(this.observerLocator, items);
    if (this.collectionObserver) {
      this.callContext = 'handleCollectionMutated';
      this.collectionObserver.subscribe(this.callContext, this);
    }
  }
}
<commit_msg>fix(virtual-repeat): correctly remove EventListener<commit_after>import {inject} from 'aurelia-dependency-injection';
import {
  ObserverLocator,
  calcSplices,
  getChangeRecords,
  createOverrideContext
} from 'aurelia-binding';
import {
  BoundViewFactory,
  ViewSlot,
  TargetInstruction,
  customAttribute,
  bindable,
  templateController
} from 'aurelia-templating';
import {
  updateOverrideContext,
  createFullOverrideContext,
  updateOverrideContexts,
  getItemsSourceExpression,
  isOneTime,
  unwrapExpression
} from 'aurelia-templating-resources/repeat-utilities';
import {viewsRequireLifecycle} from 'aurelia-templating-resources/analyze-view-factory';
import {
  calcScrollHeight,
  calcOuterHeight,
  getNthNode,
  moveViewFirst,
  moveViewLast
} from './utilities';
import {VirtualRepeatStrategyLocator} from './virtual-repeat-strategy-locator';
import {ViewStrategyLocator} from './view-strategy';

@customAttribute('virtual-repeat')
@templateController
@inject(Element, BoundViewFactory, TargetInstruction, ViewSlot, ObserverLocator, VirtualRepeatStrategyLocator, ViewStrategyLocator)
export class VirtualRepeat {
  _first = 0;
  _previousFirst = 0;
  _viewsLength = 0;
  _lastRebind = 0;
  _topBufferHeight = 0;
  _bottomBufferHeight = 0;
  _bufferSize = 5;
  _scrollingDown = false;
  _scrollingUp = false;
  _switchedDirection = false;
  _isAttached = false;
  _ticking = false;

  @bindable items
  @bindable local
  constructor(element, viewFactory, instruction, viewSlot, observerLocator, strategyLocator, viewStrategyLocator){
    this.element = element;
    this.viewFactory = viewFactory;
    this.instruction = instruction;
    this.viewSlot = viewSlot;
    this.observerLocator = observerLocator;
    this.strategyLocator = strategyLocator;
    this.viewStrategyLocator = viewStrategyLocator;
    this.local = 'item';
    this.sourceExpression = getItemsSourceExpression(this.instruction, 'virtual-repeat.for');
    this.isOneTime = isOneTime(this.sourceExpression);
    this.viewsRequireLifecycle = viewsRequireLifecycle(viewFactory);
  }

  attached(){
    this._isAttached = true;
    let element = this.element;
    this.viewStrategy = this.viewStrategyLocator.getStrategy(element);
    this.scrollList = this.viewStrategy.getScrollList(element);
    this.scrollContainer = this.viewStrategy.getScrollContainer(element);
    this.topBuffer = this.viewStrategy.createTopBufferElement(this.scrollList, element);
    this.bottomBuffer = this.viewStrategy.createBottomBufferElement(this.scrollList, element);
    this.itemsChanged();
    this.scrollListener = () => this._onScroll();
    this.scrollContainer.addEventListener('scroll', this.scrollListener);
  }

  bind(bindingContext, overrideContext){
    this.scope = { bindingContext, overrideContext };
    this._itemsLength = this.items.length;

    // TODO Fix this
    window.onresize = () => { this._handleResize(); };
  }

  call(context, changes) {
    this[context](this.items, changes);
  }

  detached() {
    this.scrollContainer.removeEventListener('scroll', this.scrollListener);
    this._first = 0;
    this._previousFirst = 0;
    this._viewsLength = 0;
    this._lastRebind = 0;
    this._topBufferHeight = 0;
    this._bottomBufferHeight = 0;
    this._scrollingDown = false;
    this._scrollingUp = false;
    this._switchedDirection = false;
    this._isAttached = false;
    this._ticking = false;
    this.viewStrategy.removeBufferElements(this.scrollList, this.topBuffer, this.bottomBuffer);
    this.isLastIndex = false;
    this.scrollList = null;
    this.scrollContainer = null;
    this.scrollContainerHeight = null;
    this.viewSlot.removeAll(true);
    if(this.scrollHandler) {
      this.scrollHandler.dispose();
    }
    this._unsubscribeCollection();

  }

  itemsChanged() {
    this._unsubscribeCollection();
    // still bound?
    if (!this.scope) {
      return;
    }
    let items = this.items;
    this.strategy = this.strategyLocator.getStrategy(items);
    this.strategy.createFirstItem(this);
    this._calcInitialHeights();
    if (!this.isOneTime && !this._observeInnerCollection()) {
      this._observeCollection();
    }

    this.strategy.instanceChanged(this, items, this._viewsLength);
  }

  unbind(){
    this.scope = null;
    this.items = null;
    this._itemsLength = null;
  }

  handleCollectionMutated(collection, changes) {
    this._itemsLength = collection.length;
    this.strategy.instanceMutated(this, collection, changes);
  }

  handleInnerCollectionMutated(collection, changes) {
    // guard against source expressions that have observable side-effects that could
    // cause an infinite loop- eg a value converter that mutates the source array.
    if (this.ignoreMutation) {
      return;
    }
    this.ignoreMutation = true;
    let newItems = this.sourceExpression.evaluate(this.scope, this.lookupFunctions);
    this.observerLocator.taskQueue.queueMicroTask(() => this.ignoreMutation = false);

    // call itemsChanged...
    if (newItems === this.items) {
      // call itemsChanged directly.
      this.itemsChanged();
    } else {
      // call itemsChanged indirectly by assigning the new collection value to
      // the items property, which will trigger the self-subscriber to call itemsChanged.
      this.items = newItems;
    }
  }

  _onScroll() {
    if(!this._ticking) {
      requestAnimationFrame(() => this._handleScroll());
      this._ticking = true;
    }
  }

  _handleScroll() {
    if (!this._isAttached) {
      return;
    }
    let itemHeight = this.itemHeight;
    let scrollTop = this.scrollContainer.scrollTop;
    this._first = Math.floor(scrollTop / itemHeight);
    this._checkScrolling();
    // TODO if and else paths do almost same thing, refactor?
    // move views down?
    if(this._scrollingDown && (this._hasScrolledDownTheBuffer() || (this._switchedDirection && this._hasScrolledDownTheBufferFromTop()))) {
      let viewsToMove = this._first - this._lastRebind;
      if(this._switchedDirection) {
        viewsToMove = this.isAtTop ? this._first : this._bufferSize - (this._lastRebind - this._first);
      }
      this.isAtTop = false;
      this._lastRebind = this._first;
      let movedViewsCount = this._moveViews(viewsToMove);
      let adjustHeight = movedViewsCount < viewsToMove ? this._bottomBufferHeight : itemHeight * movedViewsCount;
      var test = 0
      this._switchedDirection = false;
      this._topBufferHeight = this._topBufferHeight + adjustHeight;
      this._bottomBufferHeight = this._bottomBufferHeight - adjustHeight;
      if (this._bottomBufferHeight >= 0) {
        this._adjustBufferHeights();
      }
    // move view up?
  } else if (this._scrollingUp && (this._hasScrolledUpTheBuffer() || (this._switchedDirection && this._hasScrolledUpTheBufferFromBottom()))) {
      let viewsToMove = this._lastRebind - this._first;
      if(this._switchedDirection) {
          if(this.isLastIndex) {
            viewsToMove = this.items.length - this._first - this.elementsInView;
          } else {
            viewsToMove = this._bufferSize - (this._first - this._lastRebind);
          }
      }
      this.isLastIndex = false;
      this._lastRebind = this._first;
      let movedViewsCount = this._moveViews(viewsToMove);
      this.movedViewsCount = movedViewsCount;
      let adjustHeight = movedViewsCount < viewsToMove ? this._topBufferHeight : itemHeight * movedViewsCount;
      this._switchedDirection = false;
      this._topBufferHeight = this._topBufferHeight - adjustHeight;
      this._bottomBufferHeight = this._bottomBufferHeight + adjustHeight;
      if (this._topBufferHeight >= 0) {
        this._adjustBufferHeights();
      }
    }
    this._previousFirst = this._first;

    this._ticking = false;
  }

  _handleResize() {
    var children = this.viewSlot.children,
      childrenLength = children.length,
      overrideContext, view, addIndex;

    this.scrollContainerHeight = calcScrollHeight(this.scrollContainer);
    this._viewsLength = Math.ceil(this.scrollContainerHeight / this.itemHeight) + 1;

    if(this._viewsLength > childrenLength){
      addIndex = children[childrenLength - 1].overrideContext.$index + 1;
      overrideContext = createFullOverrideContext(this, this.items[addIndex], addIndex, this.items.length);
      view = this.viewFactory.create();
      view.bind(overrideContext.bindingContext, overrideContext);
      this.viewSlot.insert(childrenLength, view);
    }else if (this._viewsLength < childrenLength){
      this._viewsLength = childrenLength;
    }
  }

  _checkScrolling() {
    if (this._first > this._previousFirst && (this._bottomBufferHeight > 0 || !this.isLastIndex)) {
      if (!this._scrollingDown) {
        this._scrollingDown = true;
        this._scrollingUp = false;
        this._switchedDirection = true;
      } else {
        this._switchedDirection = false;
      }
      this._isScrolling = true;
    } else if (this._first < this._previousFirst && (this._topBufferHeight >= 0 || !this.isAtTop)) {
      if (!this._scrollingUp) {
        this._scrollingDown = false;
        this._scrollingUp = true;
        this._switchedDirection = true;
      } else {
        this._switchedDirection = false;
      }
      this._isScrolling = true;
    } else {
      this._isScrolling = false;
    }
  }

  _hasScrolledDownTheBuffer() {
    let atBottom = (this._first + this._viewsLength) >= this.items.length;
    let itemsAddedWhileAtBottom = atBottom && this._first > this._lastRebind;
    return this._first - this._lastRebind >= this._bufferSize || itemsAddedWhileAtBottom;
  }

  _hasScrolledDownTheBufferFromTop() {
    return this._first - this._bufferSize > 0;
  }

  _hasScrolledUpTheBuffer() {
    return this._lastRebind - this._first >= this._bufferSize;
  }

  _hasScrolledUpTheBufferFromBottom() {
    return this._first + this._bufferSize < this.items.length;
  }

  _adjustBufferHeights() {
    this.topBuffer.setAttribute('style', `height:  ${this._topBufferHeight}px`);
    this.bottomBuffer.setAttribute("style", `height: ${this._bottomBufferHeight}px`);
  }

  _unsubscribeCollection() {
    if (this.collectionObserver) {
      this.collectionObserver.unsubscribe(this.callContext, this);
      this.collectionObserver = null;
      this.callContext = null;
    }
  }

  _moveViews(length) {
    let getNextIndex = this._scrollingDown ? (index, i) =>  index + i : (index, i) =>  index - i;
    let isAtFirstOrLastIndex = () => this._scrollingDown ? this.isLastIndex : this.isAtTop;
    let viewSlot = this.viewSlot;
    let childrenLength = viewSlot.children.length;
    let viewIndex = this._scrollingDown ? 0 : childrenLength - 1;
    let items = this.items;
    let scrollList = this.scrollList;
    let index = this._scrollingDown ? this._getIndexOfLastView() + 1 : this._getIndexOfFirstView() - 1;
    let i = 0;
    while(i < length && !isAtFirstOrLastIndex()) {
      let view = viewSlot.children[viewIndex];
      let nextIndex = getNextIndex(index, i);
      updateOverrideContext(view.overrideContext, nextIndex, items.length);
      view.bindingContext[this.local] = items[nextIndex];
      if(this._scrollingDown) {
        viewSlot.children.push(viewSlot.children.shift());
        this.viewStrategy.moveViewLast(view, scrollList, childrenLength);
        this.isLastIndex = nextIndex >= items.length - 1;
      } else {
        viewSlot.children.unshift(viewSlot.children.splice(-1,1)[0]);
        this.viewStrategy.moveViewFirst(view, scrollList);
        this.isAtTop = nextIndex <= 0;
      }
      i++;
    }
    return length - (length - i);
  }

  _getIndexOfLastView(){
    let children = this.viewSlot.children;
    return children[children.length - 1].overrideContext.$index;
  }

  _getIndexOfFirstView(){
    let children = this.viewSlot.children;
    return children[0].overrideContext.$index;
  }

  _calcInitialHeights() {
    if (this._viewsLength > 0 && this._itemsLength == this.items.length) {
      return;
    }
    this._itemsLength = this.items.length;
    let listItems = this.scrollList.children;
    this.itemHeight = calcOuterHeight(listItems[1]);
    this.scrollContainerHeight = calcScrollHeight(this.scrollContainer);
    this.elementsInView = Math.ceil(this.scrollContainerHeight / this.itemHeight) + 1;
    this._viewsLength = (this.elementsInView * 2) + this._bufferSize;
    this._bottomBufferHeight = this.itemHeight * this.items.length - this.itemHeight * this._viewsLength;
    this.bottomBuffer.setAttribute("style", `height: ${this._bottomBufferHeight}px`);
    this._topBufferHeight = 0;
    this.topBuffer.setAttribute("style", `height: ${this._topBufferHeight}px`);
    // TODO This will cause scrolling back to top when swapping collection instances that have different lengths - instead should keep the scroll position
    this.scrollContainer.scrollTop = 0;
    this._first = 0;
  }

  _observeInnerCollection() {
    let items = this._getInnerCollection();
    let strategy = this.strategyLocator.getStrategy(items);
    if (!strategy) {
      return false;
    }
    this.collectionObserver = strategy.getCollectionObserver(this.observerLocator, items);
    if (!this.collectionObserver) {
      return false;
    }
    this.callContext = 'handleInnerCollectionMutated';
    this.collectionObserver.subscribe(this.callContext, this);
    return true;
  }

  _getInnerCollection() {
    let expression = unwrapExpression(this.sourceExpression);
    if (!expression) {
      return null;
    }
    return expression.evaluate(this.scope, null);
  }

  _observeCollection() {
    let items = this.items;
    this.collectionObserver = this.strategy.getCollectionObserver(this.observerLocator, items);
    if (this.collectionObserver) {
      this.callContext = 'handleCollectionMutated';
      this.collectionObserver.subscribe(this.callContext, this);
    }
  }
}
<|endoftext|>


<commit_before>const contentRes = require('./../helpers/contentRes');

module.exports = (app) => {
	app.get('*', (req, res) => {
		// load content
		contentRes(app, req, res);
	});
};
<commit_msg>test 3<commit_after>const contentRes = require('./../helpers/contentRes');
//
module.exports = (app) => {
	app.get('*', (req, res) => {
		// load content
		contentRes(app, req, res);
	});
};
<|endoftext|>


<commit_before>'use strict';

var MEDIA_URL = '/staticuploads/';

/**
 * Handle login with facebook button.
 */
$('.loginBtn--facebook').click(function(){
  event.preventDefault();

  // Check if the "share" button was clicked.
  if($(this).hasClass('fb-share')) {
    shareThisPost();
  } else {
    checkLoginState();
  }
});

/**
 * Share a post to facebook.
 */
function shareThisPost(){
  var captionText = $('#description').text();
  var url = $(location).attr('href');
  console.log(url);
  url = url.split("/")[0] + '//' + url.split("/")[2];
  var image = url + $('#FB-image-game').attr('src');
  console.log(image);

  FB.ui({
    method: 'share',
    href: 'https://developers.facebook.com/docs/',
    // method: 'share',
    // name: 'Something',
    // picture: image,
    // href: url.toString(),
    hashtag: '#WhatsBuzz'
    // link: url,
    // description: captionText,
    // caption: 'http://www.whatsbuzz.co.il'

  }, function(response){
    if (response && response.post_id) {
      console.log('share success');
    } else {
      console.log('share error');
    }
  });
}

/**
 * Handle the quizzes button, when displayed.
 */
$('#quizzes-game').click(function(){
  $('#facebooklogin').hide();
  showSpinner();

  // Show the quiz and hide the play button
  setTimeout(function(){
    $('#quizzes').show();
  }, 5250);
});

/**
 * Handle "show more" button in the nav pages.
 * Get posts, by type, from server and display them on the page.
 */
$('#show-more-in-page').click(function(event){
  event.preventDefault();
  var type = $('.show-more').data('posts-type');
  var start = $('.posts').length;
  $.get("/get-more-posts/" + type + "/" + start, function(data){
    // Convert the data to array of js objects.
    var data = JSON.parse(data[0].posts);

    // Create a post for each of them.
    data.forEach(function(postObject){
      // The title.
      var postTitle = $('<h2>').text(postObject.fields.title);
      // The image banner.
      var postImage = $('<img>').addClass('banner-image').attr('src', MEDIA_URL + postObject.fields.image_banner).attr('alt', postObject.fields.image_banner);
      // The button link.
      var postSlog = $('<a>').attr('href', "/posts/" + postObject.fields.slug).append($('<div>').addClass('row link').append($('<p>').text("התחל משחק")));

      // The whole post card.
      var postObj = $('<div>').addClass('col-sm-6 posts').append($('<div>').addClass('post').append(postTitle).append(postImage).append(postSlog));

      $('#main-posts').append(postObj);
    });
  });
});<commit_msg>without fb share.<commit_after>'use strict';

var MEDIA_URL = '/staticuploads/';

/**
 * Handle login with facebook button.
 */
$('.loginBtn--facebook').click(function(){
  event.preventDefault();

  // Check if the "share" button was clicked.
  if($(this).hasClass('fb-share')) {
    shareThisPost();
  } else {
    checkLoginState();
  }
});

/**
 * Share a post to facebook.
 */
function shareThisPost(){
  var captionText = $('#description').text();
  var url = $(location).attr('href');
  console.log(url);
  url = url.split("/")[0] + '//' + url.split("/")[2];
  var image = url + $('#FB-image-game').attr('src');
  console.log(image);

  // FB.ui({
  //   method: 'share',
  //   href: 'https://developers.facebook.com/docs/',
  //   // method: 'share',
  //   // name: 'Something',
  //   // picture: image,
  //   // href: url.toString(),
  //   hashtag: '#WhatsBuzz'
  //   // link: url,
  //   // description: captionText,
  //   // caption: 'http://www.whatsbuzz.co.il'
  //
  // }, function(response){
  //   if (response && response.post_id) {
  //     console.log('share success');
  //   } else {
  //     console.log('share error');
  //   }
  // });
}

/**
 * Handle the quizzes button, when displayed.
 */
$('#quizzes-game').click(function(){
  $('#facebooklogin').hide();
  showSpinner();

  // Show the quiz and hide the play button
  setTimeout(function(){
    $('#quizzes').show();
  }, 5250);
});

/**
 * Handle "show more" button in the nav pages.
 * Get posts, by type, from server and display them on the page.
 */
$('#show-more-in-page').click(function(event){
  event.preventDefault();
  var type = $('.show-more').data('posts-type');
  var start = $('.posts').length;
  $.get("/get-more-posts/" + type + "/" + start, function(data){
    // Convert the data to array of js objects.
    var data = JSON.parse(data[0].posts);

    // Create a post for each of them.
    data.forEach(function(postObject){
      // The title.
      var postTitle = $('<h2>').text(postObject.fields.title);
      // The image banner.
      var postImage = $('<img>').addClass('banner-image').attr('src', MEDIA_URL + postObject.fields.image_banner).attr('alt', postObject.fields.image_banner);
      // The button link.
      var postSlog = $('<a>').attr('href', "/posts/" + postObject.fields.slug).append($('<div>').addClass('row link').append($('<p>').text("התחל משחק")));

      // The whole post card.
      var postObj = $('<div>').addClass('col-sm-6 posts').append($('<div>').addClass('post').append(postTitle).append(postImage).append(postSlog));

      $('#main-posts').append(postObj);
    });
  });
});<|endoftext|>


<commit_before>/*global socorro:false, json_path:false */
$(function() {
    "use strict";
    var fromDate, toDate,
    graph, graphData, base_url,
    selectedVersion, selectedProduct,
    previousPoint = null,
    previousSeriesIndex = null,
    incorrectDateRange = "
The 'To' date should be greater than the 'From' date.
",
    noProductSelected = "
Please select a product below.
",
    dateFields = $("#nightly_crash_trends input[type='date']"),
    endDateWrapper = $("#end_date").parents(".field"),
    productsWrapper = $("#product").parents(".field"),
    dateSupported = function() {
        var inputElem = document.createElement("input");
        inputElem.setAttribute("type", "date");

        return inputElem.type !== "text" ? true : false;
    },
    setLoader = function() {
        var loader = new Image();
        //set the id, alt and src attributes of the loading image
        loader.setAttribute("id", "loading");
        loader.setAttribute("alt", "graph loading...");
        loader.setAttribute("src", "/img/icons/ajax-loader.gif");

        //append loader to report graph container
        $(".report_graph").append(loader);
    },
    showTooltip = function(x, y, contents) {
        $('
' + contents + '
').css({
            top: y + 5,
            left: x + 5
        }).appendTo("body").fadeIn(200);
    },
    validateDateRange = function(fromDate, toDate) {
        return socorro.date.convertToDateObj(fromDate) < socorro.date.convertToDateObj(toDate);
    },
    showMsg = function(msg, type) {
        $(type).empty().append(msg).show();
    },
    resetForm = function() {
        $("p", endDateWrapper).remove();
        endDateWrapper.removeClass("error-field");

        $("p", productsWrapper).remove();
        productsWrapper.removeClass("error-field");
    },
    validateForm = function(start, end, product) {
        var formValid = true;

        //remove any previous validation messages
        resetForm();

        if(!validateDateRange(fromDate, toDate)) {
            endDateWrapper.addClass("error-field").append(incorrectDateRange);
            formValid = false;
        }

        if(product === "none") {
            productsWrapper.addClass("error-field").append(noProductSelected);
            formValid = false;
        }

        return formValid;
    };

    var drawCrashTrends = function(url) {
        var dates = socorro.date.getAllDatesInRange(fromDate, toDate, "US_NUMERICAL"),
        selectedVersion = $("#version option:selected").val(),
        numberOfDates = dates.length,
        ajax_path = url === undefined ? json_path : url,
        i = 0,
        options = {
            colors: ["#058DC7", "#ED561B", "#50B432", "#990099"],
            grid: {
                hoverable: true
            },
            legend: {
                noColumns: 9,
                container: "#graph_legend"
            },
            series: {
                stack: true,
                bars: {
                    show: true,
                    horizontal: true
                },
                points: {
                    show: true
                }
            },
            yaxis: {
                ticks: 0
            }
        };

        graphData = $.getJSON(ajax_path, function(data) {
            // remove the loading animation
            $("#loading").remove();

            if(data.nightlyCrashes.length > 0) {
                graph = $.plot("#nightly_crash_trends_graph", data.nightlyCrashes, options);
                //emty the ul before appending the new dates
                $("#dates").empty();
                for(i = numberOfDates - 1; i >= 0; i--) {
                    $("#dates").append("
" + dates[i] + " " + selectedVersion + "
");
                }
            } else {
                $("#nightly_crash_trends_graph").empty().append("No data found for selected criteria");
            }
        }).error(function(jqXHR, textStatus, errorThrown) {
            // remove the loading animation
            $("#loading").remove();
            $("#nightly_crash_trends_graph").empty().append(errorThrown);
        });
    };

    var init = function() {
        toDate = socorro.date.formatDate(socorro.date.now(), "US_NUMERICAL");
        fromDate = socorro.date.formatDate(new Date(socorro.date.now() - (socorro.date.ONE_DAY * 6)), "US_NUMERICAL");

        //set the value of the input fields
        $("#start_date").val(fromDate);
        $("#end_date").val(toDate);

        //set the dates on the figcaption
        $("#fromdate").empty().append(fromDate);
        $("#todate").empty().append(toDate);

        setLoader();
        drawCrashTrends();
    };

    $("#nightly_crash_trends_graph").bind("plothover", function (event, pos, item) {

        var message = "";

        if (item) {

            //tracking the dataIndex assists with vertical mouse movement across the bars
            //tracking seriesIndex assists with horizontal movemnt across a bar
            if ((previousPoint !== item.dataIndex) || (previousSeriesIndex !== item.seriesIndex)) {

                $("#graph-tooltip").remove();

                previousPoint = item.dataIndex;
                previousSeriesIndex = item.seriesIndex;

                message = item.series.data[previousPoint][0] + " total crashes for builds " + item.series.label + " old.";

                showTooltip(item.pageX, item.pageY, message);
            }
        } else {
            $("#graph-tooltip").remove();
            previousPoint = null;
        }
    });

    $("#nightly_crash_trends").submit(function(event) {
        event.preventDefault();

        selectedProduct = $("#product").val();
        selectedVersion = $("#version").val();

        base_url = "/crash_trends/json_data?";
        fromDate = $("#start_date").val();
        toDate = $("#end_date").val();
        var params = {
            "product" : selectedProduct,
            "version" : selectedVersion,
            "start_date" : socorro.date.formatDate(socorro.date.convertToDateObj(fromDate), "ISO"),
            "end_date" : socorro.date.formatDate(socorro.date.convertToDateObj(toDate), "ISO")
        };

        //validate that toDate is after fromDate and a product is selected
        if(validateForm(fromDate, toDate, selectedProduct)) {
            //set the dates on the figcaption
            $("#fromdate").empty().append(fromDate);
            $("#todate").empty().append(toDate);

            // add the loading animation
            setLoader();
            drawCrashTrends(base_url + $.param(params));
        }
    });

    /*
     * when a selection is made from the product drop-down we need to call
     * the product_versions service to get the appropriate versions for the
     * selected product.
     */
    $("#product").change(function() {
        var versions = [],
        versionSelector = $("#version"),
        selectedProduct = $("#product").val(),
        optionElement = "",
        versionTxt = "",
        optionElements = [];

        // clear any info messages
        $(".info").hide();
        // empty versions selector before loading new versions
        versionSelector.empty();
        // remove error class from field
        $("p", productsWrapper).remove();
        productsWrapper.removeClass("error-field");

        //only make the ajax call if an actual product was selected.
        if(selectedProduct !== "none") {
            $.getJSON('/crash_trends/product_versions', { product: selectedProduct }, function(data) {
                if(data.length) {
                    versions = [];

                    $(data).each(function(i, version) {
                        optionElement = document.createElement('option');
                        optionElement.setAttribute("value", version);
                        versionTxt = document.createTextNode(version);
                        optionElement.appendChild(versionTxt);
                        optionElements.push(optionElement);
                    });

                    versionSelector.empty().append(optionElements);
                } else {
                    showMsg("No versions found for product", ".info");
                }
            }).error(function(jqXHR, textStatus, errorThrown) {
                showMsg(errorThrown, ".error");
            });
        }
    });

    //check if the HTML5 date type is supported else, fallback to jQuery UI
    if(!dateSupported()) {
        dateFields.datepicker({
            dateFormat: "dd/mm/yy"
        });
    }

    /*
     * On DOM ready we do our first call to draw the graph. The date range for this
     * is today minus six days. From here on out, the user can choose to adjust the
     * dates as required.
     */
    init();

});
<commit_msg>dynamically changing title<commit_after>/*global socorro:false, json_path:false */
$(function() {
    "use strict";
    var fromDate, toDate,
    graph, graphData, base_url,
    selectedVersion, selectedProduct,
    previousPoint = null,
    previousSeriesIndex = null,
    incorrectDateRange = "
The 'To' date should be greater than the 'From' date.
",
    noProductSelected = "
Please select a product below.
",
    dateFields = $("#nightly_crash_trends input[type='date']"),
    endDateWrapper = $("#end_date").parents(".field"),
    productsWrapper = $("#product").parents(".field"),
    dateSupported = function() {
        var inputElem = document.createElement("input");
        inputElem.setAttribute("type", "date");

        return inputElem.type !== "text" ? true : false;
    },
    setLoader = function() {
        var loader = new Image();
        //set the id, alt and src attributes of the loading image
        loader.setAttribute("id", "loading");
        loader.setAttribute("alt", "graph loading...");
        loader.setAttribute("src", "/img/icons/ajax-loader.gif");

        //append loader to report graph container
        $(".report_graph").append(loader);
    },
    showTooltip = function(x, y, contents) {
        $('
' + contents + '
').css({
            top: y + 5,
            left: x + 5
        }).appendTo("body").fadeIn(200);
    },
    validateDateRange = function(fromDate, toDate) {
        return socorro.date.convertToDateObj(fromDate) < socorro.date.convertToDateObj(toDate);
    },
    showMsg = function(msg, type) {
        $(type).empty().append(msg).show();
    },
    resetForm = function() {
        $("p", endDateWrapper).remove();
        endDateWrapper.removeClass("error-field");

        $("p", productsWrapper).remove();
        productsWrapper.removeClass("error-field");
    },
    validateForm = function(start, end, product) {
        var formValid = true;

        //remove any previous validation messages
        resetForm();

        if(!validateDateRange(fromDate, toDate)) {
            endDateWrapper.addClass("error-field").append(incorrectDateRange);
            formValid = false;
        }

        if(product === "none") {
            productsWrapper.addClass("error-field").append(noProductSelected);
            formValid = false;
        }

        return formValid;
    };

    var drawCrashTrends = function(url) {
        var dates = socorro.date.getAllDatesInRange(fromDate, toDate, "US_NUMERICAL"),
        selectedVersion = $("#version option:selected").val(),
        numberOfDates = dates.length,
        ajax_path = url === undefined ? json_path : url,
        i = 0,
        options = {
            colors: ["#058DC7", "#ED561B", "#50B432", "#990099"],
            grid: {
                hoverable: true
            },
            legend: {
                noColumns: 9,
                container: "#graph_legend"
            },
            series: {
                stack: true,
                bars: {
                    show: true,
                    horizontal: true
                },
                points: {
                    show: true
                }
            },
            yaxis: {
                ticks: 0
            }
        };

        graphData = $.getJSON(ajax_path, function(data) {
            // remove the loading animation
            $("#loading").remove();

            if(data.nightlyCrashes.length > 0) {
                graph = $.plot("#nightly_crash_trends_graph", data.nightlyCrashes, options);
                //emty the ul before appending the new dates
                $("#dates").empty();
                for(i = numberOfDates - 1; i >= 0; i--) {
                    $("#dates").append("
" + dates[i] + " " + selectedVersion + "
");
                }
            } else {
                $("#nightly_crash_trends_graph").empty().append("No data found for selected criteria");
            }
        }).error(function(jqXHR, textStatus, errorThrown) {
            // remove the loading animation
            $("#loading").remove();
            $("#nightly_crash_trends_graph").empty().append(errorThrown);
        });
    };

    var init = function() {
        toDate = socorro.date.formatDate(socorro.date.now(), "US_NUMERICAL");
        fromDate = socorro.date.formatDate(new Date(socorro.date.now() - (socorro.date.ONE_DAY * 6)), "US_NUMERICAL");

        //set the value of the input fields
        $("#start_date").val(fromDate);
        $("#end_date").val(toDate);

        //set the dates on the figcaption
        $("#fromdate").empty().append(fromDate);
        $("#todate").empty().append(toDate);

        setLoader();
        drawCrashTrends();
    };

    $("#nightly_crash_trends_graph").bind("plothover", function (event, pos, item) {

        var message = "";

        if (item) {

            //tracking the dataIndex assists with vertical mouse movement across the bars
            //tracking seriesIndex assists with horizontal movemnt across a bar
            if ((previousPoint !== item.dataIndex) || (previousSeriesIndex !== item.seriesIndex)) {

                $("#graph-tooltip").remove();

                previousPoint = item.dataIndex;
                previousSeriesIndex = item.seriesIndex;

                message = item.series.data[previousPoint][0] + " total crashes for builds " + item.series.label + " old.";

                showTooltip(item.pageX, item.pageY, message);
            }
        } else {
            $("#graph-tooltip").remove();
            previousPoint = null;
        }
    });

    $("#nightly_crash_trends").submit(function(event) {
        event.preventDefault();

        selectedProduct = $("#product").val();
        selectedVersion = $("#version").val();

        base_url = "/crash_trends/json_data?";
        fromDate = $("#start_date").val();
        toDate = $("#end_date").val();
        var params = {
            "product" : selectedProduct,
            "version" : selectedVersion,
            "start_date" : socorro.date.formatDate(socorro.date.convertToDateObj(fromDate), "ISO"),
            "end_date" : socorro.date.formatDate(socorro.date.convertToDateObj(toDate), "ISO")
        };

        //validate that toDate is after fromDate and a product is selected
        if(validateForm(fromDate, toDate, selectedProduct)) {
            //set the dates on the figcaption
            $("#fromdate").empty().append(fromDate);
            $("#todate").empty().append(toDate);

            $("title").empty().append("Crash Trends Report For " + selectedProduct + " " + selectedVersion);

            // add the loading animation
            setLoader();
            drawCrashTrends(base_url + $.param(params));
        }
    });

    /*
     * when a selection is made from the product drop-down we need to call
     * the product_versions service to get the appropriate versions for the
     * selected product.
     */
    $("#product").change(function() {
        var versions = [],
        versionSelector = $("#version"),
        selectedProduct = $("#product").val(),
        optionElement = "",
        versionTxt = "",
        optionElements = [];

        // clear any info messages
        $(".info").hide();
        // empty versions selector before loading new versions
        versionSelector.empty();
        // remove error class from field
        $("p", productsWrapper).remove();
        productsWrapper.removeClass("error-field");

        //only make the ajax call if an actual product was selected.
        if(selectedProduct !== "none") {
            $.getJSON('/crash_trends/product_versions', { product: selectedProduct }, function(data) {
                if(data.length) {
                    versions = [];

                    $(data).each(function(i, version) {
                        optionElement = document.createElement('option');
                        optionElement.setAttribute("value", version);
                        versionTxt = document.createTextNode(version);
                        optionElement.appendChild(versionTxt);
                        optionElements.push(optionElement);
                    });

                    versionSelector.empty().append(optionElements);
                } else {
                    showMsg("No versions found for product", ".info");
                }
            }).error(function(jqXHR, textStatus, errorThrown) {
                showMsg(errorThrown, ".error");
            });
        }
    });

    //check if the HTML5 date type is supported else, fallback to jQuery UI
    if(!dateSupported()) {
        dateFields.datepicker({
            dateFormat: "dd/mm/yy"
        });
    }

    /*
     * On DOM ready we do our first call to draw the graph. The date range for this
     * is today minus six days. From here on out, the user can choose to adjust the
     * dates as required.
     */
    init();

});
<|endoftext|>


<commit_before>// dialog.js
/*
   Copyright (c) 2011 3 Round Stones Inc, Some Rights Reserved
   Licensed under the Apache License, Version 2.0, http://www.apache.org/licenses/LICENSE-2.0
*/

(function($, jQuery){

if (!window.calli) {
	window.calli = {};
}

window.calli.closeDialog = function(iframe) {
	var e = jQuery.Event("calliCloseDialog");
	var frameElement = findFrameElement(iframe);
	$(frameElement).trigger(e);
	if (!e.isDefaultPrevented()) {
		$(frameElement).dialog('close');
	}
}

function findFrameElement(iframe) {
	var elements = $('iframe').filter(function() {
		return iframe == this.contentWindow;
	});
	if (elements.length)
		return elements[0];
	return iframe.frameElement;
}

function asName(title) {
	if (!title)
		return "iframe";
	return title.toLowerCase().replace(/^\s+/,'').replace(/\s+$/,'').replace(/\s/g,'-').replace(/[^\-\w]/g,'_');
}

function asUniqueName(title) {
	var name = asName(title);
	if (!frames[name] || frames[name].name != name)
		return name;
	var i = 1;
	while (frames[name + i] && frames[name + i].name == name + i) {
		i++;
	}
	return name + i;
}

window.calli.openDialog = function(url, title, options) {
	var width = 450;
	var height = 500;
	height += 50; // title bar
	if (options.buttons) {
		height += 50; // button bar
	}
	var innerHeight = window.innerHeight || document.documentElement.clientHeight;
	if (window.parent != window && parent.postMessage) {
		if (height > innerHeight) {
			parent.postMessage('PUT height\n\n' + height, '*');
		}
		if (width > document.documentElement.clientWidth) {
			parent.postMessage('PUT width\n\n' + width, '*');
		}
	}
	while (height > 200 && width > 200 && (height > innerHeight || width > document.documentElement.clientWidth)) {
		height -= 100;
		width -= 100;
	}
	var settings = jQuery.extend({
		title: title,
		autoOpen: false,
		modal: false,
		draggable: true,
		resizable: false,
		autoResize: true,
		position: ['center', 'center'],
		width: width,
		height: height
	}, options);
	var iframe = $("");
	iframe.attr('src', 'about:blank');
	iframe.attr('name', asUniqueName(title));
	iframe.addClass('dialog');
	iframe.dialog(settings);
	var requestedHeight = height;
	var requestedWidth = width;
	var setDialogOuterHeight = function(outerHeight) {
		outerHeight = Math.min(outerHeight, window.innerHeight || document.documentElement.clientHeight);
		var height = outerHeight - iframe.parent().outerHeight(true) + iframe.parent().height();
		var fheight = height - iframe.outerHeight(true) + iframe.height();
		iframe.siblings().each(function(){
			if ($(this).css('position') != 'absolute') {
				fheight -= $(this).outerHeight(true);
			}
		});
		var previously = iframe.dialog("option", "height");
		iframe.dialog("option", "height", height);
		iframe.height(fheight);
		if (outerHeight - 50 > iframe.parent().outerWidth(true) && previously < height) {
			setDialogOuterWidth(outerHeight - 50);
		}
		iframe.dialog("option", "position", "center");
	};
	var setDialogOuterWidth = function(outerWidth) {
		outerWidth = Math.min(outerWidth, document.documentElement.clientWidth);
		var width = outerWidth - iframe.parent().outerWidth(true) + iframe.parent().width();
		var previously = iframe.dialog("option", "width");
		iframe.dialog("option", "width", width);
		if (outerWidth - 50 > iframe.parent().outerHeight(true) && previously < width) {
			setDialogOuterHeight(outerWidth - 50);
		}
		iframe.dialog("option", "position", "center");
	};
	var handle = function(event) {
		if (event.originalEvent.source == iframe[0].contentWindow) {
			var data = event.originalEvent.data;
			if (data.indexOf('PUT height\n\n') == 0) {
				var height = parseInt(data.substring(data.indexOf('\n\n') + 2));
				requestedHeight = height + iframe.parent().outerHeight(true) - iframe.height();
				var innerHeight = window.innerHeight || document.documentElement.clientHeight;
				if (requestedHeight <= innerHeight) {
					setDialogOuterHeight(requestedHeight);
				} else {
					setDialogOuterHeight(innerHeight);
					if (window.parent != window) {
						parent.postMessage('PUT height\n\n' + requestedHeight, '*');
					}
				}
				iframe[0].contentWindow.postMessage('OK\n\PUT height', '*');
			} else if (data.indexOf('PUT width\n\n') == 0) {
				var width = parseInt(data.substring(data.indexOf('\n\n') + 2));
				requestedWidth = width + iframe.parent().outerWidth(true) - iframe.width();
				if (requestedWidth <= document.documentElement.clientWidth) {
					setDialogOuterWidth(requestedWidth);
				} else {
					setDialogOuterWidth(document.documentElement.clientWidth);
					if (window.parent != window) {
						parent.postMessage('PUT width\n\n' + requestedWidth, '*');
					}
				}
				iframe[0].contentWindow.postMessage('OK\n\nPUT width', '*');
			} else if (typeof options.onmessage == 'function') {
				if (!event.source) {
					event.source = event.originalEvent.source;
				}
				if (!event.data) {
					event.data = event.originalEvent.data;
				}
				options.onmessage(event);
			}
		}
	};
	var onresize = function(){
		var clientWidth = document.documentElement.clientWidth;
		setDialogOuterWidth(Math.min(Math.max(450, requestedWidth), clientWidth));
		var innerHeight = window.innerHeight || document.documentElement.clientHeight;
		if (requestedWidth > clientWidth || requestedHeight > innerHeight) {
			iframe.dialog("option", "position", "center");
		}
		setDialogOuterHeight(Math.min(Math.max(500, requestedHeight), innerHeight));
	};
	$(window).bind('message', handle);
	$(window).bind('resize', onresize);
	iframe.one('load', function(){
		setTimeout(function() {
			iframe.dialog("option", "position", ['center', 'center']);
		}, 0);
	});
	iframe.bind("dialogclose", function(event, ui) {
		$(window).unbind('message', handle);
		$(window).unbind('resize', onresize);
		iframe.remove();
		iframe.parent().remove();
		if (typeof options.onclose == 'function') {
			options.onclose();
		}
	});
	var e = jQuery.Event("calliOpenDialog");
	iframe.trigger(e);
	if (e.isDefaultPrevented()) {
		iframe.trigger("dialogclose");
		return null;
	} else {
		iframe.dialog("open");
		iframe.css('width', '100%');
		iframe[0].src = url;
		if (typeof options.onlookup == 'function') {
			var dialogTitle = iframe.parents(".ui-dialog").find(".ui-dialog-title");
			var form = $("<form></form>");
			var searchTerms = $("<input/>");
			searchTerms.attr("placeholder", "Lookup..");
			form.append(searchTerms);
			form.css('position', "absolute");
			form.css('top', dialogTitle.offset().top - iframe.parent().offset().top - 5);
			form.css('right', 30);
			iframe.before(form);
			form.submit(function(event) {
				event.preventDefault();
				if (searchTerms.val()) {
					options.onlookup(searchTerms.val());
				}
				return false;
			});
		}
		var win = iframe[0].contentWindow;
		try {
			win.close = function() {
				window.setTimeout(function() {
					calli.closeDialog(win);
				}, 0);
			};
		} catch (e) {
			// use calli.closeDialog directly
		}
		return win;
	}
}

})(jQuery, jQuery);

<commit_msg>Commented width style for dialogs; Dialogs should be free to automatically lay out to avoid theming problems.<commit_after>// dialog.js
/*
   Copyright (c) 2011 3 Round Stones Inc, Some Rights Reserved
   Licensed under the Apache License, Version 2.0, http://www.apache.org/licenses/LICENSE-2.0
*/

(function($, jQuery){

if (!window.calli) {
	window.calli = {};
}

window.calli.closeDialog = function(iframe) {
	var e = jQuery.Event("calliCloseDialog");
	var frameElement = findFrameElement(iframe);
	$(frameElement).trigger(e);
	if (!e.isDefaultPrevented()) {
		$(frameElement).dialog('close');
	}
}

function findFrameElement(iframe) {
	var elements = $('iframe').filter(function() {
		return iframe == this.contentWindow;
	});
	if (elements.length)
		return elements[0];
	return iframe.frameElement;
}

function asName(title) {
	if (!title)
		return "iframe";
	return title.toLowerCase().replace(/^\s+/,'').replace(/\s+$/,'').replace(/\s/g,'-').replace(/[^\-\w]/g,'_');
}

function asUniqueName(title) {
	var name = asName(title);
	if (!frames[name] || frames[name].name != name)
		return name;
	var i = 1;
	while (frames[name + i] && frames[name + i].name == name + i) {
		i++;
	}
	return name + i;
}

window.calli.openDialog = function(url, title, options) {
	/* var width = 450; */
	var height = 500;
	height += 50; // title bar
	if (options.buttons) {
		height += 50; // button bar
	}
	var innerHeight = window.innerHeight || document.documentElement.clientHeight;
	if (window.parent != window && parent.postMessage) {
		if (height > innerHeight) {
			parent.postMessage('PUT height\n\n' + height, '*');
		}
		if (width > document.documentElement.clientWidth) {
			parent.postMessage('PUT width\n\n' + width, '*');
		}
	}
	while (height > 200 && width > 200 && (height > innerHeight || width > document.documentElement.clientWidth)) {
		height -= 100;
		width -= 100;
	}
	var settings = jQuery.extend({
		title: title,
		autoOpen: false,
		modal: false,
		draggable: true,
		resizable: false,
		autoResize: true,
		position: ['center', 'center'],
		width: width,
		height: height
	}, options);
	var iframe = $("");
	iframe.attr('src', 'about:blank');
	iframe.attr('name', asUniqueName(title));
	iframe.addClass('dialog');
	iframe.dialog(settings);
	var requestedHeight = height;
	var requestedWidth = width;
	var setDialogOuterHeight = function(outerHeight) {
		outerHeight = Math.min(outerHeight, window.innerHeight || document.documentElement.clientHeight);
		var height = outerHeight - iframe.parent().outerHeight(true) + iframe.parent().height();
		var fheight = height - iframe.outerHeight(true) + iframe.height();
		iframe.siblings().each(function(){
			if ($(this).css('position') != 'absolute') {
				fheight -= $(this).outerHeight(true);
			}
		});
		var previously = iframe.dialog("option", "height");
		iframe.dialog("option", "height", height);
		iframe.height(fheight);
		if (outerHeight - 50 > iframe.parent().outerWidth(true) && previously < height) {
			setDialogOuterWidth(outerHeight - 50);
		}
		iframe.dialog("option", "position", "center");
	};
	var setDialogOuterWidth = function(outerWidth) {
		outerWidth = Math.min(outerWidth, document.documentElement.clientWidth);
		var width = outerWidth - iframe.parent().outerWidth(true) + iframe.parent().width();
		var previously = iframe.dialog("option", "width");
		iframe.dialog("option", "width", width);
		if (outerWidth - 50 > iframe.parent().outerHeight(true) && previously < width) {
			setDialogOuterHeight(outerWidth - 50);
		}
		iframe.dialog("option", "position", "center");
	};
	var handle = function(event) {
		if (event.originalEvent.source == iframe[0].contentWindow) {
			var data = event.originalEvent.data;
			if (data.indexOf('PUT height\n\n') == 0) {
				var height = parseInt(data.substring(data.indexOf('\n\n') + 2));
				requestedHeight = height + iframe.parent().outerHeight(true) - iframe.height();
				var innerHeight = window.innerHeight || document.documentElement.clientHeight;
				if (requestedHeight <= innerHeight) {
					setDialogOuterHeight(requestedHeight);
				} else {
					setDialogOuterHeight(innerHeight);
					if (window.parent != window) {
						parent.postMessage('PUT height\n\n' + requestedHeight, '*');
					}
				}
				iframe[0].contentWindow.postMessage('OK\n\PUT height', '*');
			} else if (data.indexOf('PUT width\n\n') == 0) {
				var width = parseInt(data.substring(data.indexOf('\n\n') + 2));
				requestedWidth = width + iframe.parent().outerWidth(true) - iframe.width();
				if (requestedWidth <= document.documentElement.clientWidth) {
					setDialogOuterWidth(requestedWidth);
				} else {
					setDialogOuterWidth(document.documentElement.clientWidth);
					if (window.parent != window) {
						parent.postMessage('PUT width\n\n' + requestedWidth, '*');
					}
				}
				iframe[0].contentWindow.postMessage('OK\n\nPUT width', '*');
			} else if (typeof options.onmessage == 'function') {
				if (!event.source) {
					event.source = event.originalEvent.source;
				}
				if (!event.data) {
					event.data = event.originalEvent.data;
				}
				options.onmessage(event);
			}
		}
	};
	var onresize = function(){
		var clientWidth = document.documentElement.clientWidth;
		setDialogOuterWidth(Math.min(Math.max(450, requestedWidth), clientWidth));
		var innerHeight = window.innerHeight || document.documentElement.clientHeight;
		if (requestedWidth > clientWidth || requestedHeight > innerHeight) {
			iframe.dialog("option", "position", "center");
		}
		setDialogOuterHeight(Math.min(Math.max(500, requestedHeight), innerHeight));
	};
	$(window).bind('message', handle);
	$(window).bind('resize', onresize);
	iframe.one('load', function(){
		setTimeout(function() {
			iframe.dialog("option", "position", ['center', 'center']);
		}, 0);
	});
	iframe.bind("dialogclose", function(event, ui) {
		$(window).unbind('message', handle);
		$(window).unbind('resize', onresize);
		iframe.remove();
		iframe.parent().remove();
		if (typeof options.onclose == 'function') {
			options.onclose();
		}
	});
	var e = jQuery.Event("calliOpenDialog");
	iframe.trigger(e);
	if (e.isDefaultPrevented()) {
		iframe.trigger("dialogclose");
		return null;
	} else {
		iframe.dialog("open");
		iframe.css('width', '100%');
		iframe[0].src = url;
		if (typeof options.onlookup == 'function') {
			var dialogTitle = iframe.parents(".ui-dialog").find(".ui-dialog-title");
			var form = $("<form></form>");
			var searchTerms = $("<input/>");
			searchTerms.attr("placeholder", "Lookup..");
			form.append(searchTerms);
			form.css('position', "absolute");
			form.css('top', dialogTitle.offset().top - iframe.parent().offset().top - 5);
			form.css('right', 30);
			iframe.before(form);
			form.submit(function(event) {
				event.preventDefault();
				if (searchTerms.val()) {
					options.onlookup(searchTerms.val());
				}
				return false;
			});
		}
		var win = iframe[0].contentWindow;
		try {
			win.close = function() {
				window.setTimeout(function() {
					calli.closeDialog(win);
				}, 0);
			};
		} catch (e) {
			// use calli.closeDialog directly
		}
		return win;
	}
}

})(jQuery, jQuery);

<|endoftext|>


<commit_before>/* global describe, it*/
/* jslint node: true, esnext: true */

"use strict";

const chai = require('chai'),
  assert = chai.assert,
  expect = chai.expect,
  should = chai.should();
chai.use(require('chai-as-promised'));

const http = require('http');

const kronos = require('kronos-service-manager'),
  service = require('../lib/consulService.js');

const srv = http.createServer(function (req, res) {
  res.writeHead(200, {
    'Content-Type': 'text/plain'
  });

  console.log("got request");

  setTimeout(
    () => res.end('okay'), 500);
  //res.end('okay');
});

srv.listen(8500, '127.0.0.1');

describe('consul service', function () {
  it("create", function (done) {
    kronos.manager().then(
      function (manager) {
        describe('create', function () {
          try {
            require('kronos-koa-service').registerWithManager(manager);
            require('kronos-health-check-service').registerWithManager(manager);
            service.registerWithManager(manager);

            const cs = manager.serviceGet('consul');

            assert.equal(cs.name, "consul");
            assert.equal(cs.state, "starting");

            cs.start().then(f => {
              assert.equal(cs.state, "running");
              console.log(`${JSON.stringify(cs.tags)}`);
              done();
            }, done);
          } catch (err) {
            done(err);
          }
        });
      }
    );
  });

});

srv.close();
<commit_msg>fix: renaming typo<commit_after>/* global describe, it*/
/* jslint node: true, esnext: true */

"use strict";

const chai = require('chai'),
  assert = chai.assert,
  expect = chai.expect,
  should = chai.should();
chai.use(require('chai-as-promised'));

const http = require('http');

const kronos = require('kronos-service-manager'),
  service = require('../lib/consulService.js');

const srv = http.createServer(function (req, res) {
  res.writeHead(200, {
    'Content-Type': 'text/plain'
  });

  console.log("got request");

  setTimeout(
    () => res.end('okay'), 500);
  //res.end('okay');
});

srv.listen(8500, '127.0.0.1');

describe('consul service', function () {
  it("create", function (done) {
    kronos.manager().then(
      function (manager) {
        describe('create', function () {
          try {
            require('kronos-koa-service').registerWithManager(manager);
            require('kronos-service-health-check').registerWithManager(manager);
            service.registerWithManager(manager);

            const cs = manager.serviceGet('consul');

            assert.equal(cs.name, "consul");
            assert.equal(cs.state, "starting");

            cs.start().then(f => {
              assert.equal(cs.state, "running");
              console.log(`${JSON.stringify(cs.tags)}`);
              done();
            }, done);
          } catch (err) {
            done(err);
          }
        });
      }
    );
  });

});

srv.close();
<|endoftext|>