<commit_before>(function() {
  module("IndexableCodeMirror");

  function icmTest(cb) {
    return function() {
      var place = $("

").appendTo(document.body);
      var cm = IndexableCodeMirror(place[0], {mode: "text/plain"});
      var content = "hello\nthere";
      cm.setValue(content);
      try {
        cb(cm, content);
      } finally {
        place.remove();
      }
    };
  }

  test("indexFromCoords() works", icmTest(function(cm, content) {
    equal(cm.indexFromCoords({line: 0, ch: 0}), 0,
          "index of line 0, char 0 is 0");
    equal(cm.indexFromCoords({line: 1, ch: 0}), content.indexOf("there"),
          "index of line 1, char 0 works");
  }));

  test("getCursorIndex() works", icmTest(function(cm, content) {
    cm.setCursor({line: 1, ch: 0});
    equal(cm.getCursorIndex(), content.indexOf("there"));
  }));
})();

module("Editor");

test("ParsingCodeMirror works", function() {
  var place = $("

").appendTo(document.body);
  var events = [];
  var fakeTime = {
    id: 0,
    setTimeout: function(cb, ms) {
      this.cb = cb;
      events.push("time.setTimeout(fn, " + ms + ") -> " + this.id);
      return this.id++;
    },
    clearTimeout: function(id) {
      events.push("time.clearTimeout(" + id + ")");
    }
  };
  var cm = ParsingCodeMirror(place[0], {
    mode: "text/plain",
    parseDelay: 1,
    parse: function(code) {
      return {
        error: "here is an error",
        document: "here is a document"
      };
    },
    time: fakeTime
  });
  cm.on("all", function(eventName, arg) {
    events.push("cm.trigger('" + eventName + "')");
  });

  cm.setValue("hello");
  deepEqual(events, [
    "cm.trigger('change')",
    "time.setTimeout(fn, 1) -> 0",
  ], "change event is triggered and timeout function is set");
  events = [];

  cm.on("reparse", function(arg) {
    equal(arg.document, "here is a document", "document passed on reparse");
    equal(arg.error, "here is an error", "error passed on reparse");
    equal(arg.sourceCode, "hello", "source code passed on reparse");
  });
  cm.reparse();
  deepEqual(events, [
    "cm.trigger('reparse')",
    "cm.trigger('cursor-activity')",
  ], "reparse and cursor activity events are triggered");
  cm.off("reparse");
  events = [];

  cm.setValue("hello goober");
  deepEqual(events, [
    "cm.trigger('change')",
    "time.clearTimeout(0)",
    "time.setTimeout(fn, 1) -> 1"
  ], "old timeout function is cleared");
  events = [];

  cm.on("reparse", function(event) {
    equal(event.sourceCode, "hello goober",
          "correct source code is passed on reparse event");
  });
  fakeTime.cb();
  deepEqual(events, [
    "cm.trigger('reparse')",
    "cm.trigger('cursor-activity')"
  ], "reparse/cursor-activity events are triggered by timeout function");
  events = [];

  cm.setCursor({line: 0, ch: 2});
  deepEqual(events, ["cm.trigger('cursor-activity')"],
            "cursor-activity event is triggered by setCursor()");

  place.remove();
});

test("MarkTracker works", function() {
  var place = $("

").appendTo(document.body);
  var cm = CodeMirror(place[0], {mode: "text/plain"});
  var mt = MarkTracker(cm);

  cm.setValue("hello");
  mt.mark(2, 4, "blah");
  equal(place.find(".blah").text(), "ll", "source code is marked w/ class");
  mt.clear();
  equal(place.find(".blah").length, 0, "source code class is cleared");

  var thing = $("

");
  mt.mark(1, 4, "foo", thing[0]);
  ok(thing.hasClass("foo"), "related element is marked w/ class");
  mt.clear();
  ok(!thing.hasClass("foo"), "related element class is cleared");

  place.remove();
});

asyncTest("LivePreview works", function() {
  var previewArea = $('');
  previewArea.appendTo(document.body).css({
    visibility: "hidden"
  }).load(function() {
    var cm = {};
    _.extend(cm, Backbone.Events);
    var preview = LivePreview({
      codeMirror: cm,
      previewArea: previewArea
    });
    cm.trigger('reparse', {
      error: null,
      sourceCode: '
hi there
'
    });
    equal($("p", previewArea.contents()).html(),
          "hi there",
          "HTML source code is written into preview area");
    equal($('base[target="_blank"]', previewArea.contents()).length, 1,
          '<base target="_blank"> is inserted into document');

    var wind = previewArea.contents()[0].defaultView;
    wind.scroll(5, 6);
    cm.trigger('reparse', {
      error: null,
      sourceCode: '
hi dood
'
    });
    wind = previewArea.contents()[0].defaultView;
    equal(wind.pageXOffset, 5, "x scroll is preserved across refresh");
    equal(wind.pageYOffset, 6, "y scroll is preserved across refresh");
    previewArea.remove();
    start();
  });
});
<commit_msg>split out ParsingCodeMirror tests into multiple different tests<commit_after>(function() {
  module("IndexableCodeMirror");

  function icmTest(cb) {
    return function() {
      var place = $("

").appendTo(document.body);
      var cm = IndexableCodeMirror(place[0], {mode: "text/plain"});
      var content = "hello\nthere";
      cm.setValue(content);
      try {
        cb(cm, content);
      } finally {
        place.remove();
      }
    };
  }

  test("indexFromCoords() works", icmTest(function(cm, content) {
    equal(cm.indexFromCoords({line: 0, ch: 0}), 0,
          "index of line 0, char 0 is 0");
    equal(cm.indexFromCoords({line: 1, ch: 0}), content.indexOf("there"),
          "index of line 1, char 0 works");
  }));

  test("getCursorIndex() works", icmTest(function(cm, content) {
    cm.setCursor({line: 1, ch: 0});
    equal(cm.getCursorIndex(), content.indexOf("there"));
  }));
})();

(function() {
  module("ParsingCodeMirror");

  function pcmTest(cb) {
    return function() {
      var place = $("

").appendTo(document.body);
      var events = [];
      var fakeTime = {
        id: 0,
        setTimeout: function(cb, ms) {
          this.cb = cb;
          events.push("time.setTimeout(fn, " + ms + ") -> " + this.id);
          return this.id++;
        },
        clearTimeout: function(id) {
          events.push("time.clearTimeout(" + id + ")");
        }
      };
      var cm = ParsingCodeMirror(place[0], {
        mode: "text/plain",
        parseDelay: 1,
        parse: function(code) {
          return {
            error: "here is an error",
            document: "here is a document"
          };
        },
        time: fakeTime
      });
      cm.on("all", function(eventName, arg) {
        events.push("cm.trigger('" + eventName + "')");
      });
      try {
        cb(cm, events, fakeTime);
      } finally {
        place.remove();
      }
    };
  }

  test("change triggered and timeout set on codeMirror.setValue()",
       pcmTest(function(cm, events, fakeTime) {
         cm.setValue("hello");
         deepEqual(events, [
           "cm.trigger('change')",
           "time.setTimeout(fn, 1) -> 0",
         ]);
       }));

  test("reparse() triggers events and passes expected arguments",
       pcmTest(function(cm, events, fakeTime) {
         cm.setValue("hello"); events.splice(0);
         cm.on("reparse", function(arg) {
           equal(arg.document, "here is a document", "document passed");
           equal(arg.error, "here is an error", "error passed");
           equal(arg.sourceCode, "hello", "source code passed");
         });
         cm.reparse();
         deepEqual(events, [
           "cm.trigger('reparse')",
           "cm.trigger('cursor-activity')",
          ]);
       }));

  test("old timeout cancelled on multiple content changes",
       pcmTest(function(cm, events, fakeTime) {
         cm.setValue("hello"); events.splice(0);
         cm.setValue("hello goober");
         deepEqual(events, [
           "cm.trigger('change')",
           "time.clearTimeout(0)",
           "time.setTimeout(fn, 1) -> 1"
         ]);
       }));

  test("timeout function triggers events w/ expected args",
       pcmTest(function(cm, events, fakeTime) {
         cm.setValue("hello goober"); events.splice(0);
         cm.on("reparse", function(event) {
           equal(event.sourceCode, "hello goober",
                 "correct source code is passed on reparse event");
         });
         fakeTime.cb();
         deepEqual(events, [
           "cm.trigger('reparse')",
           "cm.trigger('cursor-activity')"
         ], "events are triggered");
       }));

  test("cursor-activity event is triggered by codeMirror.setCursor()",
       pcmTest(function(cm, events, fakeTime) {
         cm.setValue("hello"); events.splice(0);
         cm.setCursor({line: 0, ch: 2});
         deepEqual(events, ["cm.trigger('cursor-activity')"]);
       }));
})();

module("Editor");

test("MarkTracker works", function() {
  var place = $("

").appendTo(document.body);
  var cm = CodeMirror(place[0], {mode: "text/plain"});
  var mt = MarkTracker(cm);

  cm.setValue("hello");
  mt.mark(2, 4, "blah");
  equal(place.find(".blah").text(), "ll", "source code is marked w/ class");
  mt.clear();
  equal(place.find(".blah").length, 0, "source code class is cleared");

  var thing = $("

");
  mt.mark(1, 4, "foo", thing[0]);
  ok(thing.hasClass("foo"), "related element is marked w/ class");
  mt.clear();
  ok(!thing.hasClass("foo"), "related element class is cleared");

  place.remove();
});

asyncTest("LivePreview works", function() {
  var previewArea = $('');
  previewArea.appendTo(document.body).css({
    visibility: "hidden"
  }).load(function() {
    var cm = {};
    _.extend(cm, Backbone.Events);
    var preview = LivePreview({
      codeMirror: cm,
      previewArea: previewArea
    });
    cm.trigger('reparse', {
      error: null,
      sourceCode: '
hi there
'
    });
    equal($("p", previewArea.contents()).html(),
          "hi there",
          "HTML source code is written into preview area");
    equal($('base[target="_blank"]', previewArea.contents()).length, 1,
          '<base target="_blank"> is inserted into document');

    var wind = previewArea.contents()[0].defaultView;
    wind.scroll(5, 6);
    cm.trigger('reparse', {
      error: null,
      sourceCode: '
hi dood
'
    });
    wind = previewArea.contents()[0].defaultView;
    equal(wind.pageXOffset, 5, "x scroll is preserved across refresh");
    equal(wind.pageYOffset, 6, "y scroll is preserved across refresh");
    previewArea.remove();
    start();
  });
});
<|endoftext|>


<commit_before>/*
 * Copyright 2016 EPAM Systems
 *
 *
 * This file is part of EPAM Report Portal.
 * https://github.com/epam/ReportPortal
 *
 * Report Portal is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Report Portal is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Report Portal.  If not, see <http://www.gnu.org/licenses/>.
 */
define(function (require) {
    'use strict';

    var $ = require('jquery');
    var Epoxy = require('backbone-epoxy');
    var Util = require('util');
    var App = require('app');
    var Localization = require('localization');
    var MarkdownViewer = require('components/markdown/MarkdownViewer');
    var SingletonDefectTypeCollection = require('defectType/SingletonDefectTypeCollection');
    var LaunchItemMenuView = require('launches/launchLevel/LaunchItemMenuView');
    var LaunchSuiteDefectsView = require('launches/common/LaunchSuiteDefectsView');
    var ItemDurationView = require('launches/common/ItemDurationView');
    var SingletonLaunchFilterCollection = require('filters/SingletonLaunchFilterCollection');
    var _ = require('underscore');
    var ModalLaunchItemEdit = require('modals/modalLaunchItemEdit');

    var config = App.getInstance();

    var LaunchSuiteItemView = Epoxy.View.extend({
        template: 'tpl-launch-suite-item',
        className: 'row rp-table-row',
        statusTpl: 'tpl-launch-suite-item-status',
        events: {
            'click [data-js-name-link]': 'onClickName',
            'click [data-js-launch-menu]': 'showItemMenu',
            'click [data-js-time-format]': 'toggleStartTimeView',
            'click [data-js-item-edit]': 'onClickEdit',
            'click [data-js-tag]': 'onClickTag',
            'click [data-js-owner-name]': 'onClickOwnerName',
            'click [data-js-statistics-to-investigate]': 'onClickToInvestigate',
            'click [data-js-toggle-open]': 'onClickOpen',
            'click [data-js-select-item]': 'onClickSelect'
        },
        bindings: {
            ':el': 'classes: {"select-state": select}',
            '[data-js-analize-label]': 'classes: {visible: isProcessing}',
            '[data-js-name-link]': 'attr: {href: url}',
            '[data-js-name]': 'text: name, attr: {title: nameTitle}',
            '[data-js-launch-number]': 'text: numberText',
            '[data-js-item-edit]': 'classes: {hide: hideEdit}',
            '[data-js-owner-block]': 'classes: {hide: not(owner)}',
            '[data-js-owner-name]': 'text: owner',
            '[data-js-time-from-now]': 'text: startFromNow',
            '[data-js-time-exact]': 'text: startFormat',
            '[data-js-select-item]': 'checked: select, attr: {disabled: launch_isProcessing}',
            '[data-js-tags-container]': 'sortTags: tags',
            '[data-js-statistics-total]': 'text: executionTotal, attr: {href: executionTotalLink}, classes: {"not-link": not(executionTotalLink)}',
            '[data-js-statistics-failed]': 'text: executionFailed, attr: {href: executionFailedLink}, classes: {"not-link": not(executionTotalLink)}',
            '[data-js-statistics-skipped]': 'text: executionSkipped, attr: {href: executionSkippedLink}, classes: {"not-link": not(executionTotalLink)}',
            '[data-js-statistics-passed]': 'text: executionPassed, attr: {href: executionPassedLink}, classes: {"not-link": not(executionTotalLink)}',
            '[data-js-statistics-to-investigate]': 'text: defectToInvestigate'
        },
        bindingHandlers: {
            sortTags: {
                set: function ($element) {
                    var $tagsBlock;
                    var sortTags = this.view.model.get('sortTags');
                    if (!sortTags.length) {
                        $element.addClass('hide');
                    } else {
                        $element.removeClass('hide');
                    }
                    $tagsBlock = $('[data-js-tags]', $element);
                    $tagsBlock.html('');
                    _.each(sortTags, function (tag) {
                        $tagsBlock.append('  ' + tag.replaceTabs() + '');
                    });
                }
            }
        },
        computeds: {
            hideEdit: {
                deps: ['launch_owner'],
                get: function () {
                    return this.model.validate.edit();
                }
            },
            nameTitle: {
                deps: ['url'],
                get: function (url) {
                    if (!url) {
                        return Localization.launches.noItemsInside;
                    }
                    return '';
                }
            },
            executionTotal: {
                deps: ['statistics'],
                get: function (statistics) {
                    return this.getExecution(statistics, 'total');
                }
            },
            executionTotalLink: function () {
                if (this.hasChild(this.model)) {
                    return this.allCasesUrl('total');
                }
                return undefined;
            },
            executionPassed: {
                deps: ['statistics'],
                get: function (statistics) {
                    return this.getExecution(statistics, 'passed');
                }
            },
            executionPassedLink: function () {
                if (this.hasChild(this.model)) {
                    return this.allCasesUrl('passed');
                }
                return undefined;
            },
            executionSkipped: {
                deps: ['statistics'],
                get: function (statistics) {
                    return this.getExecution(statistics, 'skipped');
                }
            },
            executionSkippedLink: function () {
                if (this.hasChild(this.model)) {
                    return this.allCasesUrl('skipped');
                }
                return undefined;
            },
            executionFailed: {
                deps: ['statistics'],
                get: function (statistics) {
                    return this.getExecution(statistics, 'failed');
                }
            },
            executionFailedLink: function () {
                if (this.hasChild(this.model)) {
                    return this.allCasesUrl('failed');
                }
                return undefined;
            },
            defectToInvestigate: {
                deps: ['statistics'],
                get: function (statistics) {
                    if (statistics.defects && statistics.defects.to_investigate
                        && parseInt(statistics.defects.to_investigate.total, 10)) {
                        return statistics.defects.to_investigate.total;
                    }
                    return '';
                }
            }
        },
        hasChild: function (item) {
            return item.attributes.has_childs;
        },
        getExecution: function (statistics, executionType) {
            if (statistics.executions && statistics.executions[executionType] &&
                parseInt(statistics.executions[executionType], 10)) {
                return statistics.executions[executionType];
            }
            return '';
        },
        allCasesUrl: function (type) {
            var url = this.model.get('url');
            var statusFilter = '';

            switch (type) {
            case 'total':
                statusFilter = '&filter.in.status=PASSED,FAILED,SKIPPED,INTERRUPTED&filter.in.type=STEP';
                break;
            case 'passed':
            case 'failed':
            case 'skipped':
                statusFilter = '&filter.in.status=' + type.toUpperCase() + '&filter.in.type=STEP';
                break;
            default:
                break;
            }
            return url + '|' + decodeURIComponent('filter.eq.has_childs=false' + statusFilter) + '?'
                + '&filter.eq.has_childs=false' + statusFilter;
        },
        initialize: function (options) {
            var defectCollection = new SingletonDefectTypeCollection();
            var self = this;
            var toInvest;
            this.statistics = [];
            this.filterModel = options.filterModel;
            this.render();
            this.applyBindings();

            defectCollection.ready.done(function () {
                if (self.getBinding('defectToInvestigate')) {
                    toInvest = defectCollection.findWhere({ typeRef: 'TO_INVESTIGATE' });
                    if (toInvest) {
                        $('[data-js-statistics-to-investigate]', self.$el).attr({
                            href: self.model.get('url') + '?filter.eq.has_childs=false&filter.in.issue$issue_type=' + toInvest.get('locator')
                        });
                    }
                }
                $('[data-js-label-pb]', self.$el).css({ backgroundColor: defectCollection.getMainColorByType('product_bug') });
                $('[data-js-label-ab]', self.$el).css({ backgroundColor: defectCollection.getMainColorByType('automation_bug') });
                $('[data-js-label-si]', self.$el).css({ backgroundColor: defectCollection.getMainColorByType('system_issue') });
                $('[data-js-label-ti]', self.$el).css({ backgroundColor: defectCollection.getMainColorByType('to_investigate') });
            });
            this.listenTo(this.model, 'scrollToAndHighlight', this.highlightItem);
            this.markdownViewer = new MarkdownViewer({ text: this.model.get('description') });
            $('[data-js-description]', this.$el).html(this.markdownViewer.$el);
            this.listenTo(this.model, 'change:description', function (model, description) { self.markdownViewer.update(description); });
            this.listenTo(this.model, 'change:description change:tags', this.activateAccordion);
            this.listenTo(this.markdownViewer, 'load', this.activateAccordion);
        },
        render: function () {
            this.$el.html(Util.templates(this.template, { type: this.model.get('type') }));
            this.renderDuration();
            this.renderDefects();

            // this.renderStatistics();
        },
        highlightItem: function () {
            var self;
            this.$el.prepend('

');
            self = this;
            config.mainScrollElement.animate({ scrollTop: this.el.offsetTop }, 500, function () {
                self.$el.addClass('hide-highlight');
            });
        },
        toggleStartTimeView: function () {
            this.model.collection.trigger('change:time:format');
        },
        renderDuration: function () {
            this.duration && this.duration.destroy();
            this.duration = new ItemDurationView({
                model: this.model,
                el: $('[data-js-item-status]', this.$el)
            });
        },
        renderDefects: function () {
            this.productBug && this.productBug.destroy();
            this.productBug = new LaunchSuiteDefectsView({
                model: this.model, el: $('[data-js-statistics-product-bug]', this.$el), type: 'product_bug' });
            this.autoBug && this.autoBug.destroy();
            this.autoBug = new LaunchSuiteDefectsView({
                model: this.model, el: $('[data-js-statistics-automation-bug]', this.$el), type: 'automation_bug' });
            this.systemIssue && this.systemIssue.destroy();
            this.systemIssue = new LaunchSuiteDefectsView({
                model: this.model, el: $('[data-js-statistics-system-issue]', this.$el), type: 'system_issue' });
        },
        onClickToInvestigate: function () {
            if (this.model.get('type') === 'SUITE') {
                config.trackingDispatcher.trackEventNumber(131);
            } else {
                config.trackingDispatcher.trackEventNumber(60);
            }
        },
        showItemMenu: function (e) {
            var $link;
            config.trackingDispatcher.trackEventNumber(24);
            if (!$(e.currentTarget).hasClass('rendered')) {
                $link = $(e.currentTarget);
                this.menu = new LaunchItemMenuView({
                    model: this.model
                });
                $link
                    .after(this.menu.$el)
                    .addClass('rendered')
                    .dropdown();
            }
        },
        onClickTag: function (e) {
            var tag = $(e.currentTarget).data('js-tag');
            this.addFastFilter('tags', tag);
        },
        onClickOwnerName: function () {
            this.addFastFilter('user', this.model.get('owner'));
        },
        addFastFilter: function (filterId, value) {
            var launchFilterCollection;
            var tempFilterModel;
            if (this.model.get('mode') === 'DEBUG') {
                this.filterModel.trigger('add_entity', filterId, value);
                return;
            }
            if (this.filterModel.get('id') === 'all') {
                launchFilterCollection = new SingletonLaunchFilterCollection();
                tempFilterModel = launchFilterCollection.generateTempModel();
                config.router.navigate(tempFilterModel.get('url'), { trigger: true });
                tempFilterModel.trigger('add_entity', filterId, value);
            } else {
                this.filterModel.trigger('add_entity', filterId, value);
            }
        },
        onClickSelect: function () {
            if (this.model.get('type') === 'SUITE') {
                config.trackingDispatcher.trackEventNumber(100.1);
            } else {
                config.trackingDispatcher.trackEventNumber(61.2);
            }
        },
        onClickName: function (e) {
            var href;
            config.trackingDispatcher.trackEventNumber(23);
            e.preventDefault();
            var href = $(e.currentTarget).attr('href');
            if(this.model.get('has_childs')) {
                this.model.trigger('drill:item', this.model);
            }
            config.router.navigate(href, {trigger: true});
        },
        onClickEdit: function () {
            var modal;
            if (this.model.get('type') === 'SUITE') {
                config.trackingDispatcher.trackEventNumber(98);
            } else {
                config.trackingDispatcher.trackEventNumber(53);
            }
            modal = new ModalLaunchItemEdit({
                item: this.model
            });
            modal.show();
        },
        onClickOpen: function () {
            this.$el.toggleClass('open');
        },
        activateAccordion: function () {
            var innerHeight = 198;
            if ($(window).width() < 900) {
                innerHeight = 318;
            }
            if (this.$el.innerHeight() > innerHeight) {
                this.$el.addClass('show-accordion');
            } else {
                this.$el.removeClass('show-accordion');
            }
        },
        destroy: function () {
            this.menu && this.menu.destroy();
            this.duration && this.duration.destroy();
            _.each(this.statistics, function (v) {
                if (_.isFunction(v.destroy)) {
                    v.destroy();
                }
            });
            this.undelegateEvents();
            this.stopListening();
            this.unbind();
            this.$el.html('');
            delete this;
        }
    });

    return LaunchSuiteItemView;
});
<commit_msg>EPMRPP-25373: Open link in a new tab by Ctrl+click<commit_after>/*
 * Copyright 2016 EPAM Systems
 *
 *
 * This file is part of EPAM Report Portal.
 * https://github.com/epam/ReportPortal
 *
 * Report Portal is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Report Portal is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Report Portal.  If not, see <http://www.gnu.org/licenses/>.
 */
define(function (require) {
    'use strict';

    var $ = require('jquery');
    var Epoxy = require('backbone-epoxy');
    var Util = require('util');
    var App = require('app');
    var Localization = require('localization');
    var MarkdownViewer = require('components/markdown/MarkdownViewer');
    var SingletonDefectTypeCollection = require('defectType/SingletonDefectTypeCollection');
    var LaunchItemMenuView = require('launches/launchLevel/LaunchItemMenuView');
    var LaunchSuiteDefectsView = require('launches/common/LaunchSuiteDefectsView');
    var ItemDurationView = require('launches/common/ItemDurationView');
    var SingletonLaunchFilterCollection = require('filters/SingletonLaunchFilterCollection');
    var _ = require('underscore');
    var ModalLaunchItemEdit = require('modals/modalLaunchItemEdit');

    var config = App.getInstance();

    var LaunchSuiteItemView = Epoxy.View.extend({
        template: 'tpl-launch-suite-item',
        className: 'row rp-table-row',
        statusTpl: 'tpl-launch-suite-item-status',
        events: {
            'click [data-js-name-link]': 'onClickName',
            'click [data-js-launch-menu]': 'showItemMenu',
            'click [data-js-time-format]': 'toggleStartTimeView',
            'click [data-js-item-edit]': 'onClickEdit',
            'click [data-js-tag]': 'onClickTag',
            'click [data-js-owner-name]': 'onClickOwnerName',
            'click [data-js-statistics-to-investigate]': 'onClickToInvestigate',
            'click [data-js-toggle-open]': 'onClickOpen',
            'click [data-js-select-item]': 'onClickSelect'
        },
        bindings: {
            ':el': 'classes: {"select-state": select}',
            '[data-js-analize-label]': 'classes: {visible: isProcessing}',
            '[data-js-name-link]': 'attr: {href: url}',
            '[data-js-name]': 'text: name, attr: {title: nameTitle}',
            '[data-js-launch-number]': 'text: numberText',
            '[data-js-item-edit]': 'classes: {hide: hideEdit}',
            '[data-js-owner-block]': 'classes: {hide: not(owner)}',
            '[data-js-owner-name]': 'text: owner',
            '[data-js-time-from-now]': 'text: startFromNow',
            '[data-js-time-exact]': 'text: startFormat',
            '[data-js-select-item]': 'checked: select, attr: {disabled: launch_isProcessing}',
            '[data-js-tags-container]': 'sortTags: tags',
            '[data-js-statistics-total]': 'text: executionTotal, attr: {href: executionTotalLink}, classes: {"not-link": not(executionTotalLink)}',
            '[data-js-statistics-failed]': 'text: executionFailed, attr: {href: executionFailedLink}, classes: {"not-link": not(executionTotalLink)}',
            '[data-js-statistics-skipped]': 'text: executionSkipped, attr: {href: executionSkippedLink}, classes: {"not-link": not(executionTotalLink)}',
            '[data-js-statistics-passed]': 'text: executionPassed, attr: {href: executionPassedLink}, classes: {"not-link": not(executionTotalLink)}',
            '[data-js-statistics-to-investigate]': 'text: defectToInvestigate'
        },
        bindingHandlers: {
            sortTags: {
                set: function ($element) {
                    var $tagsBlock;
                    var sortTags = this.view.model.get('sortTags');
                    if (!sortTags.length) {
                        $element.addClass('hide');
                    } else {
                        $element.removeClass('hide');
                    }
                    $tagsBlock = $('[data-js-tags]', $element);
                    $tagsBlock.html('');
                    _.each(sortTags, function (tag) {
                        $tagsBlock.append('  ' + tag.replaceTabs() + '');
                    });
                }
            }
        },
        computeds: {
            hideEdit: {
                deps: ['launch_owner'],
                get: function () {
                    return this.model.validate.edit();
                }
            },
            nameTitle: {
                deps: ['url'],
                get: function (url) {
                    if (!url) {
                        return Localization.launches.noItemsInside;
                    }
                    return '';
                }
            },
            executionTotal: {
                deps: ['statistics'],
                get: function (statistics) {
                    return this.getExecution(statistics, 'total');
                }
            },
            executionTotalLink: function () {
                if (this.hasChild(this.model)) {
                    return this.allCasesUrl('total');
                }
                return undefined;
            },
            executionPassed: {
                deps: ['statistics'],
                get: function (statistics) {
                    return this.getExecution(statistics, 'passed');
                }
            },
            executionPassedLink: function () {
                if (this.hasChild(this.model)) {
                    return this.allCasesUrl('passed');
                }
                return undefined;
            },
            executionSkipped: {
                deps: ['statistics'],
                get: function (statistics) {
                    return this.getExecution(statistics, 'skipped');
                }
            },
            executionSkippedLink: function () {
                if (this.hasChild(this.model)) {
                    return this.allCasesUrl('skipped');
                }
                return undefined;
            },
            executionFailed: {
                deps: ['statistics'],
                get: function (statistics) {
                    return this.getExecution(statistics, 'failed');
                }
            },
            executionFailedLink: function () {
                if (this.hasChild(this.model)) {
                    return this.allCasesUrl('failed');
                }
                return undefined;
            },
            defectToInvestigate: {
                deps: ['statistics'],
                get: function (statistics) {
                    if (statistics.defects && statistics.defects.to_investigate
                        && parseInt(statistics.defects.to_investigate.total, 10)) {
                        return statistics.defects.to_investigate.total;
                    }
                    return '';
                }
            }
        },
        hasChild: function (item) {
            return item.attributes.has_childs;
        },
        getExecution: function (statistics, executionType) {
            if (statistics.executions && statistics.executions[executionType] &&
                parseInt(statistics.executions[executionType], 10)) {
                return statistics.executions[executionType];
            }
            return '';
        },
        allCasesUrl: function (type) {
            var url = this.model.get('url');
            var statusFilter = '';

            switch (type) {
            case 'total':
                statusFilter = '&filter.in.status=PASSED,FAILED,SKIPPED,INTERRUPTED&filter.in.type=STEP';
                break;
            case 'passed':
            case 'failed':
            case 'skipped':
                statusFilter = '&filter.in.status=' + type.toUpperCase() + '&filter.in.type=STEP';
                break;
            default:
                break;
            }
            return url + '|' + decodeURIComponent('filter.eq.has_childs=false' + statusFilter) + '?'
                + '&filter.eq.has_childs=false' + statusFilter;
        },
        initialize: function (options) {
            var defectCollection = new SingletonDefectTypeCollection();
            var self = this;
            var toInvest;
            this.statistics = [];
            this.filterModel = options.filterModel;
            this.render();
            this.applyBindings();

            defectCollection.ready.done(function () {
                if (self.getBinding('defectToInvestigate')) {
                    toInvest = defectCollection.findWhere({ typeRef: 'TO_INVESTIGATE' });
                    if (toInvest) {
                        $('[data-js-statistics-to-investigate]', self.$el).attr({
                            href: self.model.get('url') + '?filter.eq.has_childs=false&filter.in.issue$issue_type=' + toInvest.get('locator')
                        });
                    }
                }
                $('[data-js-label-pb]', self.$el).css({ backgroundColor: defectCollection.getMainColorByType('product_bug') });
                $('[data-js-label-ab]', self.$el).css({ backgroundColor: defectCollection.getMainColorByType('automation_bug') });
                $('[data-js-label-si]', self.$el).css({ backgroundColor: defectCollection.getMainColorByType('system_issue') });
                $('[data-js-label-ti]', self.$el).css({ backgroundColor: defectCollection.getMainColorByType('to_investigate') });
            });
            this.listenTo(this.model, 'scrollToAndHighlight', this.highlightItem);
            this.markdownViewer = new MarkdownViewer({ text: this.model.get('description') });
            $('[data-js-description]', this.$el).html(this.markdownViewer.$el);
            this.listenTo(this.model, 'change:description', function (model, description) { self.markdownViewer.update(description); });
            this.listenTo(this.model, 'change:description change:tags', this.activateAccordion);
            this.listenTo(this.markdownViewer, 'load', this.activateAccordion);
        },
        render: function () {
            this.$el.html(Util.templates(this.template, { type: this.model.get('type') }));
            this.renderDuration();
            this.renderDefects();

            // this.renderStatistics();
        },
        highlightItem: function () {
            var self;
            this.$el.prepend('

');
            self = this;
            config.mainScrollElement.animate({ scrollTop: this.el.offsetTop }, 500, function () {
                self.$el.addClass('hide-highlight');
            });
        },
        toggleStartTimeView: function () {
            this.model.collection.trigger('change:time:format');
        },
        renderDuration: function () {
            this.duration && this.duration.destroy();
            this.duration = new ItemDurationView({
                model: this.model,
                el: $('[data-js-item-status]', this.$el)
            });
        },
        renderDefects: function () {
            this.productBug && this.productBug.destroy();
            this.productBug = new LaunchSuiteDefectsView({
                model: this.model, el: $('[data-js-statistics-product-bug]', this.$el), type: 'product_bug' });
            this.autoBug && this.autoBug.destroy();
            this.autoBug = new LaunchSuiteDefectsView({
                model: this.model, el: $('[data-js-statistics-automation-bug]', this.$el), type: 'automation_bug' });
            this.systemIssue && this.systemIssue.destroy();
            this.systemIssue = new LaunchSuiteDefectsView({
                model: this.model, el: $('[data-js-statistics-system-issue]', this.$el), type: 'system_issue' });
        },
        onClickToInvestigate: function () {
            if (this.model.get('type') === 'SUITE') {
                config.trackingDispatcher.trackEventNumber(131);
            } else {
                config.trackingDispatcher.trackEventNumber(60);
            }
        },
        showItemMenu: function (e) {
            var $link;
            config.trackingDispatcher.trackEventNumber(24);
            if (!$(e.currentTarget).hasClass('rendered')) {
                $link = $(e.currentTarget);
                this.menu = new LaunchItemMenuView({
                    model: this.model
                });
                $link
                    .after(this.menu.$el)
                    .addClass('rendered')
                    .dropdown();
            }
        },
        onClickTag: function (e) {
            var tag = $(e.currentTarget).data('js-tag');
            this.addFastFilter('tags', tag);
        },
        onClickOwnerName: function () {
            this.addFastFilter('user', this.model.get('owner'));
        },
        addFastFilter: function (filterId, value) {
            var launchFilterCollection;
            var tempFilterModel;
            if (this.model.get('mode') === 'DEBUG') {
                this.filterModel.trigger('add_entity', filterId, value);
                return;
            }
            if (this.filterModel.get('id') === 'all') {
                launchFilterCollection = new SingletonLaunchFilterCollection();
                tempFilterModel = launchFilterCollection.generateTempModel();
                config.router.navigate(tempFilterModel.get('url'), { trigger: true });
                tempFilterModel.trigger('add_entity', filterId, value);
            } else {
                this.filterModel.trigger('add_entity', filterId, value);
            }
        },
        onClickSelect: function () {
            if (this.model.get('type') === 'SUITE') {
                config.trackingDispatcher.trackEventNumber(100.1);
            } else {
                config.trackingDispatcher.trackEventNumber(61.2);
            }
        },
        onClickName: function () {
            config.trackingDispatcher.trackEventNumber(23);
            if (this.model.get('has_childs')) {
                this.model.trigger('drill:item', this.model);
            }
        },
        onClickEdit: function () {
            var modal;
            if (this.model.get('type') === 'SUITE') {
                config.trackingDispatcher.trackEventNumber(98);
            } else {
                config.trackingDispatcher.trackEventNumber(53);
            }
            modal = new ModalLaunchItemEdit({
                item: this.model
            });
            modal.show();
        },
        onClickOpen: function () {
            this.$el.toggleClass('open');
        },
        activateAccordion: function () {
            var innerHeight = 198;
            if ($(window).width() < 900) {
                innerHeight = 318;
            }
            if (this.$el.innerHeight() > innerHeight) {
                this.$el.addClass('show-accordion');
            } else {
                this.$el.removeClass('show-accordion');
            }
        },
        destroy: function () {
            this.menu && this.menu.destroy();
            this.duration && this.duration.destroy();
            _.each(this.statistics, function (v) {
                if (_.isFunction(v.destroy)) {
                    v.destroy();
                }
            });
            this.undelegateEvents();
            this.stopListening();
            this.unbind();
            this.$el.html('');
            delete this;
        }
    });

    return LaunchSuiteItemView;
});
<|endoftext|>


<commit_before>var assert = require('assert'),
    error = require('../../../lib/error/index'),
    math = require('../../../index');

describe('help', function() {

  it('should find documentation for a function by its name', function() {
    var help = math.help('sin');
    assert.ok(help instanceof math.type.Help);
    assert.deepEqual(help.doc, math.expression.docs.sin);
  });

  it('should find documentation for a function by the function itself', function() {
    var help = math.help(math.sin);
    assert.ok(help instanceof math.type.Help);
    assert.deepEqual(help.doc, math.expression.docs.sin);
  });

  it('should throw an error on wrong number of arguments', function() {
    assert.throws(function () {math.help()}, SyntaxError);
    assert.throws(function () {math.help('sin', 2)}, SyntaxError);
  });

  it('should find help from a function name', function() {
    var help = math.help('sin');
    assert(help instanceof math.type.Help);
    assert.equal(help.doc.name, 'sin');
  });

  it('should find help from a function', function() {
    var help = math.help(math.sin);
    assert(help instanceof math.type.Help);
    assert.equal(help.doc.name, 'sin');
  });

  it('should find help from a constant name', function() {
    var help = math.help('pi');
    assert(help instanceof math.type.Help);
    assert.equal(help.doc.name, 'pi');
  });

  it('should find help from a constant', function() {
    var help = math.help(math.pi);
    assert(help instanceof math.type.Help);
    assert.equal(help.doc.name, 'pi');
  });

  it('should throw an error when no help is found', function() {
    assert.throws(function () {math.help(undefined)}, /No documentation found/);
    assert.throws(function () {math.help('nonExistingFunction')}, /No documentation found/);
    assert.throws(function () {math.help('parse')}, /No documentation found/);
  });

});<commit_msg>modify test corresponding to the previous commit<commit_after>var assert = require('assert'),
    error = require('../../../lib/error/index'),
    math = require('../../../index');

describe('help', function() {

  it('should find documentation for a function by its name', function() {
    var help = math.help('sin');
    assert.ok(help instanceof math.type.Help);
    assert.deepEqual(help.doc, math.expression.docs.sin);
  });

  it('should find documentation for a function by the function itself', function() {
    var help = math.help(math.sin);
    assert.ok(help instanceof math.type.Help);
    assert.deepEqual(help.doc, math.expression.docs.sin);
  });

  it('should throw an error on wrong number of arguments', function() {
    assert.throws(function () {math.help()}, SyntaxError);
    assert.throws(function () {math.help('sin', 2)}, SyntaxError);
  });

  it('should find help from a function name', function() {
    var help = math.help('sin');
    assert(help instanceof math.type.Help);
    assert.equal(help.doc.name, 'sin');
  });

  it('should find help from a function', function() {
    var help = math.help(math.sin);
    assert(help instanceof math.type.Help);
    assert.equal(help.doc.name, 'sin');
  });

  it('should find help from a constant name', function() {
    var help = math.help('pi');
    assert(help instanceof math.type.Help);
    assert.equal(help.doc.name, 'pi');
  });

  it('should find help from a constant', function() {
    var help = math.help(math.pi);
    assert(help instanceof math.type.Help);
    assert.equal(help.doc.name, 'pi');
  });

  it('should throw an error when no help is found', function() {
    // assert.throws(function () {math.help(undefined)}, /No documentation found/);
    assert.throws(function () {math.help(undefined)}, /Cannot find/);
    assert.throws(function () {math.help('nonExistingFunction')}, /No documentation found/);
    assert.throws(function () {math.help('parse')}, /No documentation found/);
  });

});<|endoftext|>


<commit_before>describe("luga.data.widgets", function(){

	"use strict";

	var mockButton, mockDiv, mockDs, mockJson, showMoreBase, showMoreButton, showMoreScrollBody, showMoreScrollDiv;
	beforeEach(function(){

		mockButton = jQuery("<button>");
		mockDiv = jQuery("
");

		mockDs = new luga.data.JsonDataSet({
			uuid: "masterDs",
			incrementalLoad: true
		});

		mockJson = {
			results: [],
			params: {
				nextKey: "testId"
			}
		};

		spyOn(mockDs, "getRawJson").and.returnValue(mockJson);

		showMoreBase = new luga.data.widgets.ShowMore({
			dataSet: mockDs,
			url: "/fake/?key={nextKey}",
			paramPath: "params"
		});

		showMoreButton = new luga.data.widgets.ShowMoreButton({
			button: mockButton,
			dataSet: mockDs,
			url: "",
			paramPath: "params"
		});

		showMoreScrollBody = new luga.data.widgets.ShowMoreScrolling({
			dataSet: mockDs,
			url: ""
		});

		showMoreScrollDiv = new luga.data.widgets.ShowMoreScrolling({
			dataSet: mockDs,
			url: "",
			node: mockDiv
		});

	});

	afterEach(function(){
		luga.data.dataSourceRegistry = {};
	});

	describe(".ShowMore", function(){

		it("Is an abstract widget", function(){
			expect(luga.data.widgets.ShowMore).toBeDefined();
			expect(jQuery.isFunction(luga.data.widgets.ShowMore)).toEqual(true);
		});

		it("Register itself as observer of the associated dataSource", function(){
			expect(mockDs.observers[0]).toEqual(showMoreBase);
		});

		describe("Accepts an Options object as single argument", function(){

			describe("options.dataSet", function(){
				it("Is the dataSet to which the widget will be associated", function(){
					expect(showMoreBase.config.dataSet).toEqual(mockDs);
				});
				it("Throws an exception if not specified", function(){
					expect(function(){
						new luga.data.widgets.ShowMore({url: "test"});
					}).toThrow();
				});
			});

			describe("options.url", function(){
				it("Is a string template that will be used to assemble the url for next XHR call", function(){
					var widget = new luga.data.widgets.ShowMore({
						dataSet: mockDs,
						url: "/test/{placeHolder}"
					});
					expect(widget.config.url).toEqual("/test/{placeHolder}");
				});
				it("Throws an exception if not specified", function(){
					expect(function(){
						new luga.data.widgets.ShowMore({dataSet: mockDs});
					}).toThrow();
				});
			});

			describe("options.paramPath", function(){
				it("Is a JSON path that will be used to retrieve params from the HTTP response", function(){
					expect(showMoreBase.config.paramPath).toEqual("params");
				});
				it("Default to an empty string", function(){
					var widget = new luga.data.widgets.ShowMore({
						dataSet: mockDs,
						url: "/test/{placeHolder}"
					});
					expect(widget.config.paramPath).toEqual("");
				});
			});

		});

		describe(".assembleUrl()", function(){

			describe("First:", function(){
				it("Call dataSet's .getRawJson()", function(){
					showMoreBase.assembleUrl();
					expect(mockDs.getRawJson).toHaveBeenCalled();
				});
			});
			describe("Then:", function(){

				describe("If paramPath is not an empty string:", function(){

					it("Call luga.lookupProperty()", function(){
						spyOn(luga, "lookupProperty").and.callFake(function(){
						});
						showMoreBase.assembleUrl();
						expect(luga.lookupProperty).toHaveBeenCalledWith(mockJson, showMoreBase.config.paramPath);
					});

				});

			});
			describe("Finally:", function(){
				it("Returns the generated URL to be used for next XHR call", function(){
					expect(showMoreBase.assembleUrl()).toEqual("/fake/?key=testId");
				});
			});

		});

		describe(".disable()", function(){
			it("Is an abstract method", function(){
				expect(jQuery.isFunction(showMoreBase.disable)).toEqual(true);
			});
		});

		describe(".enable()", function(){
			it("Is an abstract method", function(){
				expect(jQuery.isFunction(showMoreBase.disable)).toEqual(true);
			});
		});

		describe(".fetch()", function(){

			describe("First:", function(){

				it("Call .assembleUrl()", function(){
					spyOn(showMoreBase, "assembleUrl").and.callFake(function(){
					});
					showMoreBase.fetch();
					expect(showMoreBase.assembleUrl).toHaveBeenCalled();
				});

			});
			describe("Then:", function(){

				describe("If .assembleUrl() return something different than options.url:", function(){

					it("Call dataSet.setUrl(), then dataSet.loadData()", function(){
						spyOn(mockDs, "setUrl");
						spyOn(mockDs, "loadData");
						var newUrl = showMoreBase.assembleUrl();
						showMoreBase.fetch();
						expect(mockDs.setUrl).toHaveBeenCalledWith(newUrl);
						expect(mockDs.loadData).toHaveBeenCalled();
					});

				});

				describe("Else:", function(){

					it("Call .disable()", function(){
						spyOn(showMoreBase, "assembleUrl").and.returnValue(showMoreBase.config.url);
						spyOn(showMoreBase, "disable");
						showMoreBase.fetch();
						expect(showMoreBase.disable).toHaveBeenCalled();
					});

				});

			});

		});

		describe(".isEnabled()", function(){
			it("Return false by default", function(){
				expect(showMoreBase.isEnabled()).toEqual(false);
			});
			it("Return true if the dataSet is ready", function(){
				mockDs.setState(luga.data.STATE.READY);
				expect(showMoreBase.isEnabled()).toEqual(true);
			});
		});

		describe(".updateState()", function(){

			describe("If the dataSet is ready:", function(){
				it("Call .enable()", function(){
					spyOn(showMoreBase, "enable");
					mockDs.setState(luga.data.STATE.READY);
					showMoreBase.updateState();
					expect(showMoreBase.enable).toHaveBeenCalled();
				});
			});

			describe("Else:", function(){
				it("Call .disable()", function(){
					spyOn(showMoreBase, "disable");
					showMoreBase.updateState();
					expect(showMoreBase.disable).toHaveBeenCalled();
				});
			});

		});

		describe(".onStateChangedHandler()", function(){
			it("Listen to the dataSet notification", function(){
				spyOn(showMoreBase, "onStateChangedHandler");
				mockDs.setState(luga.data.STATE.READY);
				expect(showMoreBase.onStateChangedHandler).toHaveBeenCalled();
			});
			it("Call .updateState()", function(){
				spyOn(showMoreBase, "updateState");
				mockDs.setState(luga.data.STATE.READY);
				expect(showMoreBase.updateState).toHaveBeenCalled();
			});
		});

	});

	describe(".ShowMoreButton", function(){

		it("Implements the luga.data.widgets.ShowMore abstract class", function(){
			expect(showMoreButton).toMatchDuckType(showMoreBase);
		});

		describe("Accepts an Options object as single argument", function(){

			describe("options.button", function(){
				it("Is mandatory", function(){
					expect(function(){
						new luga.data.widgets.ShowMoreButton({
							dataSet: mockDs
						});
					}).toThrow();
				});
				it("Throws an exception if the associated node does not exists", function(){
					expect(function(){
						new luga.data.widgets.ShowMoreButton({
							dataSet: mockDs,
							url: "",
							button: jQuery("#missing")
						});
					}).toThrow();
				});
			});

			describe("options.disabledClass", function(){
				it("Is a CSS class that will be associated to the button while it's disabled", function(){
					var widget = new luga.data.widgets.ShowMore({
						dataSet: mockDs,
						url: "/test/{placeHolder}",
						disabledClass: "myCss"
					});
					expect(widget.config.disabledClass).toEqual("myCss");
				});
				it("Default to 'disabled'", function(){
					expect(showMoreButton.config.disabledClass).toEqual("disabled");
				});
			});

		});

		describe(".attachEvents()", function(){

			describe("Attach a click event on the button", function(){

				it("If the widget is enabled. It call .fetch()", function(){
					spyOn(showMoreButton, "fetch");
					mockDs.setState(luga.data.STATE.READY);
					mockButton.click();
					expect(showMoreButton.fetch).toHaveBeenCalled();
				});
				it("If the widget is disabled. Nothing happens", function(){
					spyOn(showMoreButton, "fetch");
					mockButton.click();
					expect(showMoreButton.fetch).not.toHaveBeenCalled();
				});

			});

		});

	});

	describe(".ShowMoreScrolling", function(){

		it("Implements the luga.data.widgets.ShowMore abstract class", function(){
			expect(showMoreScrollBody).toMatchDuckType(showMoreBase);
		});

		describe("Accepts an Options object as single argument", function(){

			describe("options.node", function(){
				it("Is a jQuery element containing the records", function(){
					expect(showMoreScrollDiv.config.node).toEqual(mockDiv);
				});
				it("Default to the current document to handle infinite scrolling for the current window", function(){
					expect(showMoreScrollBody.config.node.body).toEqual(jQuery(document).body);
				});
			});

		});

		describe(".attachEvents()", function(){

			describe("Attach a scroll event either:", function(){

				describe("To the document:", function(){

					it("If the widget is enabled and the document scrolls. It call .fetch()", function(){
						spyOn(showMoreScrollBody, "fetch");
						mockDs.setState(luga.data.STATE.READY);
						jQuery(document).scroll();
						expect(showMoreScrollBody.fetch).toHaveBeenCalled();
					});

					it("If the widget is disabled. Nothing happens", function(){
						spyOn(showMoreScrollBody, "fetch");
						jQuery(document).scroll();
						expect(showMoreScrollBody.fetch).not.toHaveBeenCalled();
					});

				});

				describe("To the node:", function(){

					it("If the widget is enabled and the node scrolls. It call .fetch()", function(){
						spyOn(showMoreScrollDiv, "fetch");
						mockDs.setState(luga.data.STATE.READY);
						mockDiv.scroll();
						expect(showMoreScrollDiv.fetch).toHaveBeenCalled();
					});

					it("If the widget is disabled. Nothing happens", function(){
						spyOn(showMoreScrollDiv, "fetch");
						mockDiv.scroll();
						expect(showMoreScrollDiv.fetch).not.toHaveBeenCalled();
					});

				});

			});

		});

	});

});<commit_msg>Tests: luga.data.widgets.ShowMore: Fix after luga.xhr introduction<commit_after>describe("luga.data.widgets", function(){

	"use strict";

	var mockButton, mockDiv, mockDs, mockJson, showMoreBase, showMoreButton, showMoreScrollBody, showMoreScrollDiv;
	beforeEach(function(){

		mockButton = jQuery("<button>");
		mockDiv = jQuery("
");

		mockDs = new luga.data.JsonDataSet({
			uuid: "masterDs",
			incrementalLoad: true
		});

		mockJson = {
			results: [],
			params: {
				nextKey: "testId"
			}
		};

		spyOn(mockDs, "getRawJson").and.returnValue(mockJson);

		showMoreBase = new luga.data.widgets.ShowMore({
			dataSet: mockDs,
			url: "/fake/?key={nextKey}",
			paramPath: "params"
		});

		showMoreButton = new luga.data.widgets.ShowMoreButton({
			button: mockButton,
			dataSet: mockDs,
			url: "",
			paramPath: "params"
		});

		showMoreScrollBody = new luga.data.widgets.ShowMoreScrolling({
			dataSet: mockDs,
			url: ""
		});

		showMoreScrollDiv = new luga.data.widgets.ShowMoreScrolling({
			dataSet: mockDs,
			url: "",
			node: mockDiv
		});

	});

	afterEach(function(){
		luga.data.dataSourceRegistry = {};
	});

	describe(".ShowMore", function(){

		it("Is an abstract widget", function(){
			expect(luga.data.widgets.ShowMore).toBeDefined();
			expect(jQuery.isFunction(luga.data.widgets.ShowMore)).toEqual(true);
		});

		it("Register itself as observer of the associated dataSource", function(){
			expect(mockDs.observers[0]).toEqual(showMoreBase);
		});

		describe("Accepts an Options object as single argument", function(){

			describe("options.dataSet", function(){
				it("Is the dataSet to which the widget will be associated", function(){
					expect(showMoreBase.config.dataSet).toEqual(mockDs);
				});
				it("Throws an exception if not specified", function(){
					expect(function(){
						new luga.data.widgets.ShowMore({url: "test"});
					}).toThrow();
				});
			});

			describe("options.url", function(){
				it("Is a string template that will be used to assemble the url for next XHR call", function(){
					var widget = new luga.data.widgets.ShowMore({
						dataSet: mockDs,
						url: "/test/{placeHolder}"
					});
					expect(widget.config.url).toEqual("/test/{placeHolder}");
				});
				it("Throws an exception if not specified", function(){
					expect(function(){
						new luga.data.widgets.ShowMore({dataSet: mockDs});
					}).toThrow();
				});
			});

			describe("options.paramPath", function(){
				it("Is a JSON path that will be used to retrieve params from the HTTP response", function(){
					expect(showMoreBase.config.paramPath).toEqual("params");
				});
				it("Default to an empty string", function(){
					var widget = new luga.data.widgets.ShowMore({
						dataSet: mockDs,
						url: "/test/{placeHolder}"
					});
					expect(widget.config.paramPath).toEqual("");
				});
			});

		});

		describe(".assembleUrl()", function(){

			describe("First:", function(){
				it("Call dataSet's .getRawJson()", function(){
					showMoreBase.assembleUrl();
					expect(mockDs.getRawJson).toHaveBeenCalled();
				});
			});
			describe("Then:", function(){

				describe("If paramPath is not an empty string:", function(){

					it("Call luga.lookupProperty()", function(){
						spyOn(luga, "lookupProperty").and.callFake(function(){
						});
						showMoreBase.assembleUrl();
						expect(luga.lookupProperty).toHaveBeenCalledWith(mockJson, showMoreBase.config.paramPath);
					});

				});

			});
			describe("Finally:", function(){
				it("Returns the generated URL to be used for next XHR call", function(){
					expect(showMoreBase.assembleUrl()).toEqual("/fake/?key=testId");
				});
			});

		});

		describe(".disable()", function(){
			it("Is an abstract method", function(){
				expect(jQuery.isFunction(showMoreBase.disable)).toEqual(true);
			});
		});

		describe(".enable()", function(){
			it("Is an abstract method", function(){
				expect(jQuery.isFunction(showMoreBase.disable)).toEqual(true);
			});
		});

		describe(".fetch()", function(){

			describe("First:", function(){

				it("Call .assembleUrl()", function(){
					spyOn(showMoreBase, "assembleUrl").and.callFake(function(){
					});
					spyOn(mockDs, "loadData");
					showMoreBase.fetch();
					expect(showMoreBase.assembleUrl).toHaveBeenCalled();
				});

			});
			describe("Then:", function(){

				describe("If .assembleUrl() return something different than options.url:", function(){

					it("Call dataSet.setUrl(), then dataSet.loadData()", function(){
						spyOn(mockDs, "setUrl");
						spyOn(mockDs, "loadData");
						var newUrl = showMoreBase.assembleUrl();
						showMoreBase.fetch();
						expect(mockDs.setUrl).toHaveBeenCalledWith(newUrl);
						expect(mockDs.loadData).toHaveBeenCalled();
					});

				});

				describe("Else:", function(){

					it("Call .disable()", function(){
						spyOn(showMoreBase, "assembleUrl").and.returnValue(showMoreBase.config.url);
						spyOn(showMoreBase, "disable");
						showMoreBase.fetch();
						expect(showMoreBase.disable).toHaveBeenCalled();
					});

				});

			});

		});

		describe(".isEnabled()", function(){
			it("Return false by default", function(){
				expect(showMoreBase.isEnabled()).toEqual(false);
			});
			it("Return true if the dataSet is ready", function(){
				mockDs.setState(luga.data.STATE.READY);
				expect(showMoreBase.isEnabled()).toEqual(true);
			});
		});

		describe(".updateState()", function(){

			describe("If the dataSet is ready:", function(){
				it("Call .enable()", function(){
					spyOn(showMoreBase, "enable");
					mockDs.setState(luga.data.STATE.READY);
					showMoreBase.updateState();
					expect(showMoreBase.enable).toHaveBeenCalled();
				});
			});

			describe("Else:", function(){
				it("Call .disable()", function(){
					spyOn(showMoreBase, "disable");
					showMoreBase.updateState();
					expect(showMoreBase.disable).toHaveBeenCalled();
				});
			});

		});

		describe(".onStateChangedHandler()", function(){
			it("Listen to the dataSet notification", function(){
				spyOn(showMoreBase, "onStateChangedHandler");
				mockDs.setState(luga.data.STATE.READY);
				expect(showMoreBase.onStateChangedHandler).toHaveBeenCalled();
			});
			it("Call .updateState()", function(){
				spyOn(showMoreBase, "updateState");
				mockDs.setState(luga.data.STATE.READY);
				expect(showMoreBase.updateState).toHaveBeenCalled();
			});
		});

	});

	describe(".ShowMoreButton", function(){

		it("Implements the luga.data.widgets.ShowMore abstract class", function(){
			expect(showMoreButton).toMatchDuckType(showMoreBase);
		});

		describe("Accepts an Options object as single argument", function(){

			describe("options.button", function(){
				it("Is mandatory", function(){
					expect(function(){
						new luga.data.widgets.ShowMoreButton({
							dataSet: mockDs
						});
					}).toThrow();
				});
				it("Throws an exception if the associated node does not exists", function(){
					expect(function(){
						new luga.data.widgets.ShowMoreButton({
							dataSet: mockDs,
							url: "",
							button: jQuery("#missing")
						});
					}).toThrow();
				});
			});

			describe("options.disabledClass", function(){
				it("Is a CSS class that will be associated to the button while it's disabled", function(){
					var widget = new luga.data.widgets.ShowMore({
						dataSet: mockDs,
						url: "/test/{placeHolder}",
						disabledClass: "myCss"
					});
					expect(widget.config.disabledClass).toEqual("myCss");
				});
				it("Default to 'disabled'", function(){
					expect(showMoreButton.config.disabledClass).toEqual("disabled");
				});
			});

		});

		describe(".attachEvents()", function(){

			describe("Attach a click event on the button", function(){

				it("If the widget is enabled. It call .fetch()", function(){
					spyOn(showMoreButton, "fetch");
					mockDs.setState(luga.data.STATE.READY);
					mockButton.click();
					expect(showMoreButton.fetch).toHaveBeenCalled();
				});
				it("If the widget is disabled. Nothing happens", function(){
					spyOn(showMoreButton, "fetch");
					mockButton.click();
					expect(showMoreButton.fetch).not.toHaveBeenCalled();
				});

			});

		});

	});

	describe(".ShowMoreScrolling", function(){

		it("Implements the luga.data.widgets.ShowMore abstract class", function(){
			expect(showMoreScrollBody).toMatchDuckType(showMoreBase);
		});

		describe("Accepts an Options object as single argument", function(){

			describe("options.node", function(){
				it("Is a jQuery element containing the records", function(){
					expect(showMoreScrollDiv.config.node).toEqual(mockDiv);
				});
				it("Default to the current document to handle infinite scrolling for the current window", function(){
					expect(showMoreScrollBody.config.node.body).toEqual(jQuery(document).body);
				});
			});

		});

		describe(".attachEvents()", function(){

			describe("Attach a scroll event either:", function(){

				describe("To the document:", function(){

					it("If the widget is enabled and the document scrolls. It call .fetch()", function(){
						spyOn(showMoreScrollBody, "fetch");
						mockDs.setState(luga.data.STATE.READY);
						jQuery(document).scroll();
						expect(showMoreScrollBody.fetch).toHaveBeenCalled();
					});

					it("If the widget is disabled. Nothing happens", function(){
						spyOn(showMoreScrollBody, "fetch");
						jQuery(document).scroll();
						expect(showMoreScrollBody.fetch).not.toHaveBeenCalled();
					});

				});

				describe("To the node:", function(){

					it("If the widget is enabled and the node scrolls. It call .fetch()", function(){
						spyOn(showMoreScrollDiv, "fetch");
						mockDs.setState(luga.data.STATE.READY);
						mockDiv.scroll();
						expect(showMoreScrollDiv.fetch).toHaveBeenCalled();
					});

					it("If the widget is disabled. Nothing happens", function(){
						spyOn(showMoreScrollDiv, "fetch");
						mockDiv.scroll();
						expect(showMoreScrollDiv.fetch).not.toHaveBeenCalled();
					});

				});

			});

		});

	});

});<|endoftext|>


<commit_before>(function() {

    'use strict';

    /**
     * @ngdoc service
     * @name requisition-validation.requisitionValidator
     *
     * @description
     * Responsible for validating requisitions, lineItems and their fields.
     */
    angular
        .module('requisition-validation')
        .factory('requisitionValidator', requisitionValidator);

    requisitionValidator.$inject = ['validations', 'calculations', 'Columns', 'Source'];

    function requisitionValidator(validations, calculations, Columns, Source) {

        var validationsToPass = {
            stockOnHand: [
                validations.nonNegative,
            ],
            totalConsumedQuantity: [
                validations.nonNegative
            ],
            requestedQuantityExplanation: [
                validations.nonEmptyIfPropertyIsSet(Columns.REQUESTED_QUANTITY)
            ]
        };

        var counterpart = {
            stockOnHand: Columns.TOTAL_CONSUMED_QUANTITY,
            totalConsumedQuantity: Columns.STOCK_ON_HAND
        };

        var validator = {
            validateRequisition: validateRequisition,
            validateLineItem: validateLineItem,
            validateLineItemField: validateLineItemField,
            isLineItemValid: isLineItemValid
        };
        return validator;

        /**
         * @ngdoc function
         * @name validateRequisition
         * @methodOf requisition-validation.requisitionValidator
         *
         * @description
         * Validates the given requisitions.
         *
         * @param  {Object} requisition the requisition to be validated
         * @return {Boolean}            true if the requisition is valid, false otherwise
         */
        function validateRequisition(requisition) {
            var valid = true,
                validator = this,
                fullSupplyColumns = requisition.$template.getColumns(),
                nonFullSupplyColumns = requisition.$template.getColumns(true);

            requisition.$fullSupplyCategories.forEach(function(category) {
              category.lineItems.forEach(function(lineItem) {
                valid = validator.validateLineItem(lineItem, fullSupplyColumns) && valid;
              });
            });

            requisition.$nonFullSupplyCategories.forEach(function(category) {
              category.lineItems.forEach(function(lineItem) {
                valid = validator.validateLineItem(lineItem, nonFullSupplyColumns) && valid;
              });
            });

            return valid;
        }

        /**
         * @ngdoc method
         * @name validateLineItem
         * @methodOf requisition-validation.requisitionValidator
         *
         * @description
         * Validates the given line item.
         *
         * @param  {Object} lineItem the line item to be validated
         * @param  {Object} columns  the list of columns to validate the line item for
         * @return {Boolean}         true if the line item is valid, false otherwise
         */
        function validateLineItem(lineItem, columns) {
            var valid = true,
                vlidator = this;

            columns.forEach(function(column) {
                valid = validator.validateLineItemField(lineItem, column, columns) && valid;
            });
            return valid;
        }

        /**
         * @ngdoc method
         * @name validateLineItemField
         * @methodOf requisition-validation.requisitionValidator
         *
         * @description
         * Validates the field of the given requisition for the given column. Columns list is
         * necessary for validating calculations.
         *
         * @param  {Object} lineItem the line item to be validated
         * @param  {Object} column   the column to validate the line item for
         * @param  {Object} columns  the list of columns used for validating the line item
         * @return {Boolean}         true of the line item field is valid, false otherwise
         */
        function validateLineItemField(lineItem, column, columns) {
            var name = column.name,
                error;

            if (name === Columns.TOTAL_LOSSES_AND_ADJUSTMENTS) return true;

            if (column.required) {
                error = error || validations.nonEmpty(lineItem[name]);
            }

            angular.forEach(validationsToPass[name], function(validation) {
                error = error || validation(lineItem[name], lineItem);
            });

            if (shouldValidateCalculation(lineItem, column, columns)) {
                error = error || validateCalculation(calculations[name], lineItem, name);
            }

            return !(lineItem.$errors[name] = error);
        }

        /**
         * @ngdoc method
         * @name isLineItemValid
         * @methodOf requisition-validation.requisitionValidator
         *
         * @description
         * Checks whether any field of the given line item has any error. It does not perform any
         * validation.
         *
         * @param  {Object}  lineItem the line item ot be checked
         * @return {Boolean}          true if any of the fields has error, false otherwise
         */
        function isLineItemValid(lineItem) {
            var valid = true;
            angular.forEach(lineItem.$errors, function(error) {
                valid = valid && !error;
            });
            return valid;
        }

        function shouldValidateCalculation(lineItem, column, columns) {
            return calculations[column.name]
                    && !isCalculated(column)
                    && !isCalculated(getCounterpart(columns, column.name));
        }

        function validateCalculation(calculation, lineItem, name) {
            return validations.validateCalculation(calculation)(lineItem[name], lineItem);
        }

        function isCalculated(column) {
            return column.source === Source.CALCULATED;
        }

        function getCounterpart(columns, name) {
            var match;
            columns.forEach(function(column) {
                if (column.name === counterpart[name]) {
                    match = column;
                }
            });
            return match;
        }
    }

})();
<commit_msg>Fixed issue with validator trying to validate non-existent counterpart column<commit_after>(function() {

    'use strict';

    /**
     * @ngdoc service
     * @name requisition-validation.requisitionValidator
     *
     * @description
     * Responsible for validating requisitions, lineItems and their fields.
     */
    angular
        .module('requisition-validation')
        .factory('requisitionValidator', requisitionValidator);

    requisitionValidator.$inject = ['validations', 'calculations', 'Columns', 'Source'];

    function requisitionValidator(validations, calculations, Columns, Source) {

        var validationsToPass = {
            stockOnHand: [
                validations.nonNegative,
            ],
            totalConsumedQuantity: [
                validations.nonNegative
            ],
            requestedQuantityExplanation: [
                validations.nonEmptyIfPropertyIsSet(Columns.REQUESTED_QUANTITY)
            ]
        };

        var counterpart = {
            stockOnHand: Columns.TOTAL_CONSUMED_QUANTITY,
            totalConsumedQuantity: Columns.STOCK_ON_HAND
        };

        var validator = {
            validateRequisition: validateRequisition,
            validateLineItem: validateLineItem,
            validateLineItemField: validateLineItemField,
            isLineItemValid: isLineItemValid
        };
        return validator;

        /**
         * @ngdoc function
         * @name validateRequisition
         * @methodOf requisition-validation.requisitionValidator
         *
         * @description
         * Validates the given requisitions.
         *
         * @param  {Object} requisition the requisition to be validated
         * @return {Boolean}            true if the requisition is valid, false otherwise
         */
        function validateRequisition(requisition) {
            var valid = true,
                validator = this,
                fullSupplyColumns = requisition.$template.getColumns(),
                nonFullSupplyColumns = requisition.$template.getColumns(true);

            requisition.$fullSupplyCategories.forEach(function(category) {
              category.lineItems.forEach(function(lineItem) {
                valid = validator.validateLineItem(lineItem, fullSupplyColumns) && valid;
              });
            });

            requisition.$nonFullSupplyCategories.forEach(function(category) {
              category.lineItems.forEach(function(lineItem) {
                valid = validator.validateLineItem(lineItem, nonFullSupplyColumns) && valid;
              });
            });

            return valid;
        }

        /**
         * @ngdoc method
         * @name validateLineItem
         * @methodOf requisition-validation.requisitionValidator
         *
         * @description
         * Validates the given line item.
         *
         * @param  {Object} lineItem the line item to be validated
         * @param  {Object} columns  the list of columns to validate the line item for
         * @return {Boolean}         true if the line item is valid, false otherwise
         */
        function validateLineItem(lineItem, columns) {
            var valid = true,
                vlidator = this;

            columns.forEach(function(column) {
                valid = validator.validateLineItemField(lineItem, column, columns) && valid;
            });
            return valid;
        }

        /**
         * @ngdoc method
         * @name validateLineItemField
         * @methodOf requisition-validation.requisitionValidator
         *
         * @description
         * Validates the field of the given requisition for the given column. Columns list is
         * necessary for validating calculations.
         *
         * @param  {Object} lineItem the line item to be validated
         * @param  {Object} column   the column to validate the line item for
         * @param  {Object} columns  the list of columns used for validating the line item
         * @return {Boolean}         true of the line item field is valid, false otherwise
         */
        function validateLineItemField(lineItem, column, columns) {
            var name = column.name,
                error;

            if (name === Columns.TOTAL_LOSSES_AND_ADJUSTMENTS) return true;

            if (column.required) {
                error = error || validations.nonEmpty(lineItem[name]);
            }

            angular.forEach(validationsToPass[name], function(validation) {
                error = error || validation(lineItem[name], lineItem);
            });

            if (shouldValidateCalculation(lineItem, column, columns)) {
                error = error || validateCalculation(calculations[name], lineItem, name);
            }

            return !(lineItem.$errors[name] = error);
        }

        /**
         * @ngdoc method
         * @name isLineItemValid
         * @methodOf requisition-validation.requisitionValidator
         *
         * @description
         * Checks whether any field of the given line item has any error. It does not perform any
         * validation.
         *
         * @param  {Object}  lineItem the line item ot be checked
         * @return {Boolean}          true if any of the fields has error, false otherwise
         */
        function isLineItemValid(lineItem) {
            var valid = true;
            angular.forEach(lineItem.$errors, function(error) {
                valid = valid && !error;
            });
            return valid;
        }

        function shouldValidateCalculation(lineItem, column, columns) {
            var counterpart = getCounterpart(columns, column.name);
            return calculations[column.name]
                    && !isCalculated(column)
                    && counterpart
                    && !isCalculated(counterpart);
        }

        function validateCalculation(calculation, lineItem, name) {
            return validations.validateCalculation(calculation)(lineItem[name], lineItem);
        }

        function isCalculated(column) {
            return column.source === Source.CALCULATED;
        }

        function getCounterpart(columns, name) {
            var match;
            columns.forEach(function(column) {
                if (column.name === counterpart[name]) {
                    match = column;
                }
            });
            return match;
        }
    }

})();
<|endoftext|>


<commit_before>frappe.provide('frappe.views');

frappe.views.KanbanView = class KanbanView extends frappe.views.ListView {
	static load_last_view() {
		const route = frappe.get_route();
		if (route.length === 3) {
			const doctype = route[1];
			const user_settings = frappe.get_user_settings(doctype)['Kanban'] || {};
			if (!user_settings.last_kanban_board) {
				frappe.msgprint({
					title: __('Error'),
					indicator: 'red',
					message: __('Missing parameter Kanban Board Name')
				});
				frappe.set_route('List', doctype, 'List');
				return true;
			}
			route.push(user_settings.last_kanban_board);
			frappe.set_route(route);
			return true;
		}
		return false;
	}

	get view_name() {
		return 'Kanban';
	}

	setup_defaults() {
		super.setup_defaults();
		this.board_name = frappe.get_route()[3];
		this.page_title = this.board_name;
		this.card_meta = this.get_card_meta();

		this.menu_items.push({
			label: __('Save filters'),
			action: () => {
				this.save_kanban_board_filters();
			}
		});

		return this.get_board();
	}

	get_board() {
		return frappe.db.get_doc('Kanban Board', this.board_name)
			.then(board => {
				this.board = board;
				this.board.filters_array = JSON.parse(this.board.filters || '[]');
				this.filters = this.board.filters_array;
			});
	}

	before_refresh() {

	}

	setup_view() {

	}

	set_fields() {
		super.set_fields();
		this._add_field(this.card_meta.title_field);
	}

	before_render() {
		frappe.model.user_settings.save(this.doctype, 'last_view', this.view_name);
		this.save_view_user_settings({
			last_kanban_board: this.board_name
		});
	}

	on_filter_change() {
		if (JSON.stringify(this.board.filters_array) !== JSON.stringify(this.filter_area.get())) {
			this.page.set_indicator(__('Not Saved'), 'orange');
		} else {
			this.page.clear_indicator();
		}
	}

	save_kanban_board_filters() {
		const filters = this.filter_area.get();

		frappe.call({
			method: 'frappe.desk.doctype.kanban_board.kanban_board.save_filters',
			args: {
				board_name: this.board_name,
				filters: filters
			}
		}).then(r => {
			if (r.exc) {
				frappe.show_alert({
					indicator: 'red',
					message: __('There was an error saving filters')
				});
				return;
			}
			frappe.show_alert({
				indicator: 'green',
				message: __('Filters saved')
			});

			this.board.filters_array = filters;
			this.on_filter_change();
		});
	}

	render() {
		const board_name = this.board_name;
		if (this.kanban && board_name === this.kanban.board_name) {
			this.kanban.update(this.data);
			return;
		}

		this.kanban = new frappe.views.KanbanBoard({
			doctype: this.doctype,
			board: this.board,
			board_name: board_name,
			cards: this.data,
			card_meta: this.card_meta,
			wrapper: this.$result,
			cur_list: this,
			user_settings: this.view_user_settings
		});
	}

	get_card_meta() {
		var meta = frappe.get_meta(this.doctype);
		var doc = frappe.model.get_new_doc(this.doctype);
		var title_field = null;
		var quick_entry = false;

		if (this.meta.title_field) {
			title_field = frappe.meta.get_field(this.doctype, this.meta.title_field);
		}

		this.meta.fields.forEach((df) => {
			const is_valid_field =
				in_list(['Data', 'Text', 'Small Text', 'Text Editor'], df.fieldtype)
				&& !df.hidden;

			if (is_valid_field && !title_field) {
				// can be mapped to textarea
				title_field = df;
			}
		});

		// quick entry
		var mandatory = meta.fields.filter((df) => df.reqd && !doc[df.fieldname]);

		if (mandatory.some(df => df.fieldtype === 'Table') || mandatory.length > 1) {
			quick_entry = true;
		}

		if (!title_field) {
			title_field = frappe.meta.get_field(this.doctype, 'name');
		}

		return {
			quick_entry: quick_entry,
			title_field: title_field
		};
	}

	get required_libs() {
		return [
			'assets/frappe/js/lib/fluxify.min.js',
			'assets/frappe/js/frappe/views/kanban/kanban_board.js'
		];
	}
};

frappe.views.KanbanView.setup_dropdown_in_sidebar = function(doctype, $dropdown) {
	// get kanban boards and append to dropdown
	get_kanban_boards()
		.then((kanban_boards) => {
			if (!kanban_boards) return;

			$('

').appendTo($dropdown);

			kanban_boards.forEach(board => {
				const route = ['List', board.reference_doctype, 'Kanban', board.name].join('/');

				$(`

						${__(board.name)}
					${board.private ? '' : ''}

				`).appendTo($dropdown);
			});
		});

	$dropdown.on('click', '.new-kanban-board', () => {
		const dialog = new_kanban_dialog();
		dialog.show();
	});

	function make_kanban_board(board_name, field_name, project) {
		return frappe.call({
			method: 'frappe.desk.doctype.kanban_board.kanban_board.quick_kanban_board',
			args: {
				doctype,
				board_name,
				field_name,
				project
			},
			callback: function(r) {
				var kb = r.message;
				if (kb.filters) {
					frappe.provide('frappe.kanban_filters');
					frappe.kanban_filters[kb.kanban_board_name] = kb.filters;
				}
				frappe.set_route('List', doctype, 'Kanban', kb.kanban_board_name);
			}
		});
	}

	let dialog = null;

	function new_kanban_dialog() {
		if (dialog) return dialog;

		const fields = get_fields_for_dialog();

		let primary_action_label = fields.length > 1 ? __('Save') : '';
		let primary_action = fields.length > 1 ?
			({ board_name, field_name, project }) => {
				make_kanban_board(board_name, field_name, project)
					.then(() => dialog.hide(), (err) => frappe.msgprint(err));
			} : null;

		dialog = new frappe.ui.Dialog({
			title: __('New Kanban Board'),
			fields,
			primary_action_label,
			primary_action
		});
		return dialog;
	}

	function get_fields_for_dialog() {

		let fields = [{
			fieldtype: 'Data',
			fieldname: 'board_name',
			label: __('Kanban Board Name'),
			reqd: 1,
			description: ['Note', 'ToDo'].includes(doctype) ?
				__('This Kanban Board will be private') : ''
		}];

		if (doctype === 'Task') {
			fields.push({
				fieldtype: 'Link',
				fieldname: 'project',
				label: __('Project'),
				options: 'Project'
			});
		}

		const select_fields =
			frappe.get_meta(doctype).fields
				.filter(df => {
					return df.fieldtype === 'Select' &&
						df.fieldname !== 'kanban_column';
				});

		if (select_fields.length > 0) {
			fields.push({
				fieldtype: 'Select',
				fieldname: 'field_name',
				label: __('Columns based on'),
				options: select_fields.map(df => ({label: df.label, value: df.fieldname})),
				default: select_fields[0],
				reqd: 1,
			});
		} else {
			fields = [{
				fieldtype: 'HTML',
				options: `

						${__('No fields found that can be used as a Kanban Column. Use the Customize Form to add a Custom Field of type "Select".')}

							${__('Customize Form')}

				`
			}]
		}

		return fields;
	}

	function get_kanban_boards() {
		return frappe.call('frappe.desk.doctype.kanban_board.kanban_board.get_kanban_boards', { doctype })
			.then(r => r.message);
	}
};
<commit_msg>style: Add semicolon<commit_after>frappe.provide('frappe.views');

frappe.views.KanbanView = class KanbanView extends frappe.views.ListView {
	static load_last_view() {
		const route = frappe.get_route();
		if (route.length === 3) {
			const doctype = route[1];
			const user_settings = frappe.get_user_settings(doctype)['Kanban'] || {};
			if (!user_settings.last_kanban_board) {
				frappe.msgprint({
					title: __('Error'),
					indicator: 'red',
					message: __('Missing parameter Kanban Board Name')
				});
				frappe.set_route('List', doctype, 'List');
				return true;
			}
			route.push(user_settings.last_kanban_board);
			frappe.set_route(route);
			return true;
		}
		return false;
	}

	get view_name() {
		return 'Kanban';
	}

	setup_defaults() {
		super.setup_defaults();
		this.board_name = frappe.get_route()[3];
		this.page_title = this.board_name;
		this.card_meta = this.get_card_meta();

		this.menu_items.push({
			label: __('Save filters'),
			action: () => {
				this.save_kanban_board_filters();
			}
		});

		return this.get_board();
	}

	get_board() {
		return frappe.db.get_doc('Kanban Board', this.board_name)
			.then(board => {
				this.board = board;
				this.board.filters_array = JSON.parse(this.board.filters || '[]');
				this.filters = this.board.filters_array;
			});
	}

	before_refresh() {

	}

	setup_view() {

	}

	set_fields() {
		super.set_fields();
		this._add_field(this.card_meta.title_field);
	}

	before_render() {
		frappe.model.user_settings.save(this.doctype, 'last_view', this.view_name);
		this.save_view_user_settings({
			last_kanban_board: this.board_name
		});
	}

	on_filter_change() {
		if (JSON.stringify(this.board.filters_array) !== JSON.stringify(this.filter_area.get())) {
			this.page.set_indicator(__('Not Saved'), 'orange');
		} else {
			this.page.clear_indicator();
		}
	}

	save_kanban_board_filters() {
		const filters = this.filter_area.get();

		frappe.call({
			method: 'frappe.desk.doctype.kanban_board.kanban_board.save_filters',
			args: {
				board_name: this.board_name,
				filters: filters
			}
		}).then(r => {
			if (r.exc) {
				frappe.show_alert({
					indicator: 'red',
					message: __('There was an error saving filters')
				});
				return;
			}
			frappe.show_alert({
				indicator: 'green',
				message: __('Filters saved')
			});

			this.board.filters_array = filters;
			this.on_filter_change();
		});
	}

	render() {
		const board_name = this.board_name;
		if (this.kanban && board_name === this.kanban.board_name) {
			this.kanban.update(this.data);
			return;
		}

		this.kanban = new frappe.views.KanbanBoard({
			doctype: this.doctype,
			board: this.board,
			board_name: board_name,
			cards: this.data,
			card_meta: this.card_meta,
			wrapper: this.$result,
			cur_list: this,
			user_settings: this.view_user_settings
		});
	}

	get_card_meta() {
		var meta = frappe.get_meta(this.doctype);
		var doc = frappe.model.get_new_doc(this.doctype);
		var title_field = null;
		var quick_entry = false;

		if (this.meta.title_field) {
			title_field = frappe.meta.get_field(this.doctype, this.meta.title_field);
		}

		this.meta.fields.forEach((df) => {
			const is_valid_field =
				in_list(['Data', 'Text', 'Small Text', 'Text Editor'], df.fieldtype)
				&& !df.hidden;

			if (is_valid_field && !title_field) {
				// can be mapped to textarea
				title_field = df;
			}
		});

		// quick entry
		var mandatory = meta.fields.filter((df) => df.reqd && !doc[df.fieldname]);

		if (mandatory.some(df => df.fieldtype === 'Table') || mandatory.length > 1) {
			quick_entry = true;
		}

		if (!title_field) {
			title_field = frappe.meta.get_field(this.doctype, 'name');
		}

		return {
			quick_entry: quick_entry,
			title_field: title_field
		};
	}

	get required_libs() {
		return [
			'assets/frappe/js/lib/fluxify.min.js',
			'assets/frappe/js/frappe/views/kanban/kanban_board.js'
		];
	}
};

frappe.views.KanbanView.setup_dropdown_in_sidebar = function(doctype, $dropdown) {
	// get kanban boards and append to dropdown
	get_kanban_boards()
		.then((kanban_boards) => {
			if (!kanban_boards) return;

			$('

').appendTo($dropdown);

			kanban_boards.forEach(board => {
				const route = ['List', board.reference_doctype, 'Kanban', board.name].join('/');

				$(`

						${__(board.name)}
					${board.private ? '' : ''}

				`).appendTo($dropdown);
			});
		});

	$dropdown.on('click', '.new-kanban-board', () => {
		const dialog = new_kanban_dialog();
		dialog.show();
	});

	function make_kanban_board(board_name, field_name, project) {
		return frappe.call({
			method: 'frappe.desk.doctype.kanban_board.kanban_board.quick_kanban_board',
			args: {
				doctype,
				board_name,
				field_name,
				project
			},
			callback: function(r) {
				var kb = r.message;
				if (kb.filters) {
					frappe.provide('frappe.kanban_filters');
					frappe.kanban_filters[kb.kanban_board_name] = kb.filters;
				}
				frappe.set_route('List', doctype, 'Kanban', kb.kanban_board_name);
			}
		});
	}

	let dialog = null;

	function new_kanban_dialog() {
		if (dialog) return dialog;

		const fields = get_fields_for_dialog();

		let primary_action_label = fields.length > 1 ? __('Save') : '';
		let primary_action = fields.length > 1 ?
			({ board_name, field_name, project }) => {
				make_kanban_board(board_name, field_name, project)
					.then(() => dialog.hide(), (err) => frappe.msgprint(err));
			} : null;

		dialog = new frappe.ui.Dialog({
			title: __('New Kanban Board'),
			fields,
			primary_action_label,
			primary_action
		});
		return dialog;
	}

	function get_fields_for_dialog() {

		let fields = [{
			fieldtype: 'Data',
			fieldname: 'board_name',
			label: __('Kanban Board Name'),
			reqd: 1,
			description: ['Note', 'ToDo'].includes(doctype) ?
				__('This Kanban Board will be private') : ''
		}];

		if (doctype === 'Task') {
			fields.push({
				fieldtype: 'Link',
				fieldname: 'project',
				label: __('Project'),
				options: 'Project'
			});
		}

		const select_fields =
			frappe.get_meta(doctype).fields
				.filter(df => {
					return df.fieldtype === 'Select' &&
						df.fieldname !== 'kanban_column';
				});

		if (select_fields.length > 0) {
			fields.push({
				fieldtype: 'Select',
				fieldname: 'field_name',
				label: __('Columns based on'),
				options: select_fields.map(df => ({label: df.label, value: df.fieldname})),
				default: select_fields[0],
				reqd: 1,
			});
		} else {
			fields = [{
				fieldtype: 'HTML',
				options: `

						${__('No fields found that can be used as a Kanban Column. Use the Customize Form to add a Custom Field of type "Select".')}

							${__('Customize Form')}

				`
			}];
		}

		return fields;
	}

	function get_kanban_boards() {
		return frappe.call('frappe.desk.doctype.kanban_board.kanban_board.get_kanban_boards', { doctype })
			.then(r => r.message);
	}
};
<|endoftext|>


<commit_before>(function () {
  'use strict';

  moj.Modules.SubTotal = {
    el: '.js-SubTotal',

    init: function () {
      _.bindAll(this, 'render', 'showSummary', 'calculate');
      this.cacheEls();
      this.bindEvents();
    },

    bindEvents: function () {
      this.$sets.on('keyup', 'input[type=number]', this.showSummary);

      moj.Events.on('render', this.render);
    },

    cacheEls: function () {
      this.$sets = $(this.el);
    },

    render: function () {
      this.$sets.each(this.showSummary);
    },

    calculate: function (set) {
      var total = 0;

      set.find('input[type=number]').each(function (i, el) {
        var val = parseFloat($(el).val());
        if (!isNaN(val)) {
          total += val;
        }
      });

      return total;
    },

    showSummary: function (e, el) {
      var $set = el !== undefined ? $(el) : $(e.target).closest(this.el),
          value = this.calculate($set);

      moj.log(CLA.templates);

      if ($set.find('.SubTotal').length > 0) {
        $set.find('.SubTotal').replaceWith(CLA.templates.SubTotal({value: value}));
      } else {
        $set.append(CLA.templates.SubTotal({value: value}));
      }
    }
  };
}());<commit_msg>Remove console log<commit_after>(function () {
  'use strict';

  moj.Modules.SubTotal = {
    el: '.js-SubTotal',

    init: function () {
      _.bindAll(this, 'render', 'showSummary', 'calculate');
      this.cacheEls();
      this.bindEvents();
    },

    bindEvents: function () {
      this.$sets.on('keyup', 'input[type=number]', this.showSummary);

      moj.Events.on('render', this.render);
    },

    cacheEls: function () {
      this.$sets = $(this.el);
    },

    render: function () {
      this.$sets.each(this.showSummary);
    },

    calculate: function (set) {
      var total = 0;

      set.find('input[type=number]').each(function (i, el) {
        var val = parseFloat($(el).val());
        if (!isNaN(val)) {
          total += val;
        }
      });

      return total;
    },

    showSummary: function (e, el) {
      var $set = el !== undefined ? $(el) : $(e.target).closest(this.el),
          value = this.calculate($set);

      if ($set.find('.SubTotal').length > 0) {
        $set.find('.SubTotal').replaceWith(CLA.templates.SubTotal({value: value}));
      } else {
        $set.append(CLA.templates.SubTotal({value: value}));
      }
    }
  };
}());<|endoftext|>


<commit_before>import React, { PropTypes } from 'react';
import { Link } from 'react-router';
import { Meteor } from 'meteor/meteor';
import { Session } from 'meteor/session';
import { createContainer } from 'meteor/react-meteor-data';
import Alerts from '../../components/Alerts';

// App component - represents the whole app
// eslint-disable-next-line react/prop-types
const App = ({ currentUser, success, error, children }) => {
  const app = (

        { success ?
{success}
 :
{success}
}
        { error ?
{error}
 :
{error}
}

        <Link className="navbar__brand" to="/">ARCHIVERS</Link>

          { currentUser ? <Link to={`/users/${currentUser.username}`}>{currentUser.username}</Link> : <Link to="/login">Login</Link> }
          { currentUser ? <Link to="/urls">Urls</Link> : undefined }
          { currentUser ? <Link to="/agencies">Agencies</Link> : undefined }
          <Link to="/events">Events</Link>
          { currentUser ? <Link to="/users">Users</Link> : undefined }
          { currentUser ? FAQ : undefined }

        <Alerts />

        {children}

  );
  return app;
};

App.propTypes = {
  // eslint-disable-next-line react/forbid-prop-types
  currentUser: PropTypes.object,
};

export default createContainer(({ params }) => {
  const container = {
    component: params.component,
    currentUser: Meteor.user(),
    // eslint-disable-next-line meteor/no-session
    success: Session.get('messages.success'),
    // eslint-disable-next-line meteor/no-session
    error: Session.get('messages.error'),
  };
  return container;
}, App);
<commit_msg>disabling Alerts element<commit_after>import React, { PropTypes } from 'react';
import { Link } from 'react-router';
import { Meteor } from 'meteor/meteor';
import { Session } from 'meteor/session';
import { createContainer } from 'meteor/react-meteor-data';
// import Alerts from '../../components/Alerts';

// App component - represents the whole app
// eslint-disable-next-line react/prop-types
const App = ({ currentUser, success, error, children }) => {
  const app = (

        { success ?
{success}
 :
{success}
}
        { error ?
{error}
 :
{error}
}

        <Link className="navbar__brand" to="/">ARCHIVERS</Link>

          { currentUser ? <Link to={`/users/${currentUser.username}`}>{currentUser.username}</Link> : <Link to="/login">Login</Link> }
          { currentUser ? <Link to="/urls">Urls</Link> : undefined }
          { currentUser ? <Link to="/agencies">Agencies</Link> : undefined }
          <Link to="/events">Events</Link>
          { currentUser ? <Link to="/users">Users</Link> : undefined }
          { currentUser ? FAQ : undefined }

        { /**
        <Alerts />
           **/ }

        {children}

  );
  return app;
};

App.propTypes = {
  // eslint-disable-next-line react/forbid-prop-types
  currentUser: PropTypes.object,
};

export default createContainer(({ params }) => {
  const container = {
    component: params.component,
    currentUser: Meteor.user(),
    // eslint-disable-next-line meteor/no-session
    success: Session.get('messages.success'),
    // eslint-disable-next-line meteor/no-session
    error: Session.get('messages.error'),
  };
  return container;
}, App);
<|endoftext|>


<commit_before>obtain(['./src/utils.js', 'child_process'], ({ copyConfigFile, call: Call }, { exec })=> {

  var mainDir = __dirname.substring(0, __dirname.indexOf('piFig/src'));
  var startup = '@reboot startx ' + mainDir + 'node_modules/electron/dist/electron ' + mainDir;

  console.log(startup);

  exports.configure = ()=> {
    console.log(startup);
    var command = '(crontab -u pi -l ; echo "' + startup + '") | crontab -u pi -';
    if (__dirname.indexOf('/home/pi') >= 0) exec(command);
    else console.error('System not a pi, preventing install');
  };

  exports.remove = ()=> {
    var command = 'crontab -u pi -l | grep -v "' + startup + '"  | crontab -u pi -';
    if (__dirname.indexOf('/home/pi') >= 0) exec(command);
    else console.error('System not a pi, preventing uninstall');
  };
});
<commit_msg>moved pifig to template<commit_after>obtain(['./src/utils.js', 'child_process'], ({ copyConfigFile, call: Call }, { exec })=> {

  var mainDir = __dirname.substring(0, __dirname.indexOf('piFig/src'));
  var startup = 'sudo startx ' + mainDir + 'node_modules/electron/dist/electron ' + mainDir;

  console.log(startup);

  exports.remove = ()=> {
    var command = 'sed -i".bak" "/node_modules\/.bin\/electron/d" /home/pi/.bashrc > /home/pi/.bashrc';
    if (__dirname.indexOf('/home/pi') >= 0) exec(command);
    else console.error('System not a pi, preventing uninstall');
  };

  exports.configure = ()=> {
    exports.remove();
    var command = 'echo "' + startup + '" >> /home/pi/.bashrc';
    if (__dirname.indexOf('/home/pi') >= 0) exec(command);
    else console.error('System not a pi, preventing install');
  };
});
<|endoftext|>