<commit_before>import Ember from 'ember';

var ID_FILTERS = ['contributors', 'funders', 'publishers'];

export default Ember.Component.extend({
    filters: null,
    init () {
      this._super(...arguments);
      let parameters = this.get('parameters');
      var filters = Object.keys(parameters).map((key) => {
        return {
          "key": key,
          "value": parameters[key]
        }
      }).filter((item) => {
        return item.key !== "page";
      });

      // Find and format all query params that use ids for the value
      var ids = filters.filter((item) => {
          return ID_FILTERS.indexOf(item.key) !== -1;
      }).map((param) => {
          return {key: param.value, doc_count: 0}
      });

      // Fetch display names
      if (ids.length !== 0) {
        this.fetchAgentDetails(ids).then((data) => {
          for (var i = 0; i < data.length; i++) {
            for (var filter = 0; filter < filters.length; filter++) {
              if (filters[filter].value === data[i].id) {
                  Ember.set(filters[filter], 'value', data[i].name);
              }
            }
          }
        });
      }
      this.set('filters', filters);
    },

    fetchAgentDetails: async function(data) {
        let agent_details = await Ember.$.ajax({
            url: 'https://dev-labs.cos.io/bulk_get_agents',
            crossDomain: true,
            data: JSON.stringify(data),
            type: 'POST',
            contentType: 'application/json'
        });
        return JSON.parse(agent_details);
    },

    actions: {

        removeFilter(filter) {
            let queryParams = {};
            queryParams[filter.key] = undefined;
            this.attrs.transitionToFacet("search", queryParams);
        },

        transitionToFacet(parameter, parameterValue) {
            let queryParams = {};
            queryParams[parameter] = parameterValue;
            this.attrs.transitionToFacet("search", queryParams);
        }

    }

});
<commit_msg>Update component.js<commit_after>import Ember from 'ember';

var ID_FILTERS = ['contributors', 'funders', 'publishers'];

export default Ember.Component.extend({
    filters: null,
    init () {
      this._super(...arguments);
      let parameters = this.get('parameters');
      var filters = Object.keys(parameters).map((key) => {
        return {
          "key": key,
          "value": parameters[key]
        }
      }).filter((item) => {
        return item.key !== "page";
      });

      // Find and format all query params that use ids for the value
      var ids = filters.filter((item) => {
          return ID_FILTERS.indexOf(item.key) !== -1;
      }).map((param) => {
          return {key: param.value, doc_count: 0}
      });

      // Fetch display names
      if (ids.length !== 0) {
        this.fetchAgentDetails(ids).then((data) => {
          for (var i = 0; i < data.length; i++) {
            for (var filter = 0; filter < filters.length; filter++) {
              if (filters[filter].value === data[i].id) {
                  Ember.set(filters[filter], 'value', data[i].name);
              }
            }
          }
        });
      }
      this.set('filters', filters);
    },

    fetchAgentDetails: async function(agentList) {
        let agent_details = await Ember.$.ajax({
            url: 'https://dev-labs.cos.io/bulk_get_agents',
            crossDomain: true,
            data: JSON.stringify(agentList),
            type: 'POST',
            contentType: 'application/json'
        });
        return JSON.parse(agent_details);
    },

    actions: {

        removeFilter(filter) {
            let queryParams = {};
            queryParams[filter.key] = undefined;
            this.attrs.transitionToFacet("search", queryParams);
        },

        transitionToFacet(parameter, parameterValue) {
            let queryParams = {};
            queryParams[parameter] = parameterValue;
            this.attrs.transitionToFacet("search", queryParams);
        }

    }

});
<|endoftext|>


<commit_before>// Data Manager
var DataManager = function() {
    this.contents = [];
    this.json = "{}";
    this.loader = {};
    this.root = createElement("div", null, "canvas", null, null);

    // constants for saving and loading content from cookies
    this.COOKIE_NAME = "GITHUB_SADIKOVI_LEAN_CANVAS_TEMP_CONTENT";
    this.COOKIE_EXPIRE_SEC = 365*24*60*60; // cookie is stored for one year
    // timer for autosave
    this.autosaveTimer = null;
    this.autosaveTime = 1000; // 1 second autosave interval

    // [Public]
    // adds content to existing array
    this.addContent = function(content) {
        this.contents.push(content);
    }

    // [Private]
    // updates json for having the latest changes
    this.updateJSON = function() {
        var res = "{ \"data\" : [ ";

        var a = [];
        for (var i=0; i<this.contents.length; i++) {
            a.push(this.contents[i].getJSON());
        }

        this.json = res + a.join(", ") + "] }";
    }

    // [Public]
    // returns the latest json representation of the data
    this.getJSON = function() {
        this.updateJSON();
        return JSON.parse(this.json);
    }

    // [Public]
    // saves data on github (as gist)
    this.saveGistOnGithub = function(success, error) {
        this.createGist(success, error);
    }

    // [Private]
    // creates gist request and sends it to github
    this.createGist = function (success, error) {
        var id = generateId();
        var filename = "leancanvas-" + id + "-save.json";
        var description = "Public Gist: Lean canvas " + id + " save file to keep up";
        var data = {
            "files": {},
            "description": description,
            "public": true
        };

        data["files"][filename] = {
            "content": JSON.stringify(this.getJSON(), null, 4)
        };

        this.loader = new Loader();
        this.loader.send(
                    "POST",
                    true,
                    "https://api.github.com/gists",
                    "application/x-www-form-urlencoded",
                    JSON.stringify(data),
                    function(result) {
                        success.call(this, result);
                    },
                    function(result) {
                        error.call(this, result);
                    }
        );
    }

    // [Public]
    // loads gist from github
    this.loadGistFromGithub = function(link, success, error) {
        var a = link.split("/");
        var gistid = (a.length==0)?"":a[a.length-1];

        this.loader = new Loader();
        this.loader.send(
            "GET",
            true,
            "https://api.github.com/gists/"+gistid,
            "application/x-www-form-urlencoded",
            null,
            function(result) {
                success.call(this, result);
            },
            function(result) {
                error.call(this, result);
            }
        );
    }

    // [Public]
    // builds content from json representation
    // argument can be json string or json object
    this.buildContentFromJSON = function(content, loadHandler, saveHandler, addNoteHandler, noteEditHandler, noteRemoveHandler) {
        if (!content || !loadHandler || !saveHandler || !addNoteHandler || !noteEditHandler || !noteRemoveHandler) {
            throw ("content is not initiliasid properly is wrong");
        }

        var cont = {};
        try {
            if ((typeof content) === "string") {
                cont = JSON.parse(content);
            } else {
                cont = content;
            }
        } catch (e) {
            cont = DEFAULT_CONTENT;
        }

        var data = cont.data;

        // 0. clear contents
        this.contents = [];
        this.json = "{}";

        // 1. main block
        var main = data[0];
        var MainContent = new Content(main.parentid);
        var main_columns = main.columns;
        for (var i=0; i<main_columns.length; i++) {
            var column = new Column(main_columns[i].id, MainContent);
            MainContent.addColumn(column);
            var main_directories = main_columns[i].directories;

            // load directories
            for (var j=0; j<main_directories.length; j++) {
                var directory = new Directory(main_directories[j].id, main_directories[j].name, column, addNoteHandler);
                directory.setPlaceholder(main_directories[j].placeholder);
                column.addDirectory(directory);
                // load notes
                var main_notes = main_directories[j].notes;
                for (var k=0; k<main_notes.length; k++) {
                    var note = new Note(main_notes[k].id, main_notes[k].text, directory, noteEditHandler, noteRemoveHandler);
                    directory.addNote(note);
                }
            }
        }

        // 2. bottom block
        var bottom = data[1];
        var BottomContent = new Content(bottom.parentid);
        var bottom_columns = bottom.columns;
        for (var i=0; i<bottom_columns.length; i++) {
            var column = new Column(bottom_columns[i].id, BottomContent);
            BottomContent.addColumn(column);
            var bottom_directories = bottom_columns[i].directories;

            // load directories
            for (var j=0; j<bottom_directories.length; j++) {
                var directory = new Directory(bottom_directories[j].id, bottom_directories[j].name, column, addNoteHandler);
                directory.setPlaceholder(bottom_directories[j].placeholder);
                column.addDirectory(directory);
                // load notes
                var bottom_notes = bottom_directories[j].notes;
                for (var k=0; k<bottom_notes.length; k++) {
                    var note = new Note(bottom_notes[k].id, bottom_notes[k].text, directory, noteEditHandler, noteRemoveHandler);
                    directory.addNote(note);
                }
            }
        }

        // 3. add contents to dataManager
        this.contents.push(MainContent);
        this.contents.push(BottomContent);
    }

    // [Private]
    // builds DOM for current state of the data
    this.buildDOM = function() {
        var MainContent = this.contents[0];
        var BottomContent = this.contents[1];

        var mainDiv = createElement("div", MainContent.parentId, "", null, this.root);
        mainDiv.appendChild(MainContent.generateDOM());

        var bottomDiv = createElement("div", BottomContent.parentId, "", null, this.root);
        bottomDiv.appendChild(BottomContent.generateDOM());
    }

    // [Public]
    // update DOM, so it reflects latest changes
    this.updateDOM = function() {
        this.root.innerHTML = "";
        this.buildDOM();
    }

    // [Public]
    // returns content from cookie, if it is not found returns null
    this.getContentFromCookie = function() {
        var c = document.cookie;
        if (!c || c.length == 0)
            return null;

        c = "; " + c;
        var parts = c.split("; " + this.COOKIE_NAME + "=");
        if (parts.length == 2)
            return parts[parts.length-1];

        return null;
    }

    // [Public]
    // saves current content into cookie
    this.saveContentIntoCookie = function() {
        var d = new Date();
        d.setTime(d.getTime() + (this.COOKIE_EXPIRE_SEC*1000));
        document.cookie = this.COOKIE_NAME + "=" + JSON.stringify(this.getJSON()) + "; "
                        + "expires=" + d.toUTCString() + "; "
                        + "Path=/; Domain=.sadikovi.github.io";
    }

    // [Public]
    // toggles autosave feature
    this.toggleAutosaveContent = function(isOn, onAutosaved) {
        if (isOn) {
            // turn on and start updating
            if (this.autosaveTimer)
                this.toggleAutosaveContent(!isOn);

            var te = this;
            this.autosaveTimer = setInterval(
                function() {
                    te.saveContentIntoCookie();
                    if (onAutosaved) {onAutosaved.call(this);}
                }, this.autosaveTime);
            console.log("timer is on");
        } else {
            // turn off and remove all events
            if (this.autosaveTimer)
                clearInterval(this.autosaveTimer);
            console.log("timer is off");
        }
    }
};
<commit_msg>autosave interval fix<commit_after>// Data Manager
var DataManager = function() {
    this.contents = [];
    this.json = "{}";
    this.loader = {};
    this.root = createElement("div", null, "canvas", null, null);

    // constants for saving and loading content from cookies
    this.COOKIE_NAME = "GITHUB_SADIKOVI_LEAN_CANVAS_TEMP_CONTENT";
    this.COOKIE_EXPIRE_SEC = 365*24*60*60; // cookie is stored for one year
    // timer for autosave
    this.autosaveTimer = null;
    this.autosaveTime = 15000; // 15 seconds autosave interval

    // [Public]
    // adds content to existing array
    this.addContent = function(content) {
        this.contents.push(content);
    }

    // [Private]
    // updates json for having the latest changes
    this.updateJSON = function() {
        var res = "{ \"data\" : [ ";

        var a = [];
        for (var i=0; i<this.contents.length; i++) {
            a.push(this.contents[i].getJSON());
        }

        this.json = res + a.join(", ") + "] }";
    }

    // [Public]
    // returns the latest json representation of the data
    this.getJSON = function() {
        this.updateJSON();
        return JSON.parse(this.json);
    }

    // [Public]
    // saves data on github (as gist)
    this.saveGistOnGithub = function(success, error) {
        this.createGist(success, error);
    }

    // [Private]
    // creates gist request and sends it to github
    this.createGist = function (success, error) {
        var id = generateId();
        var filename = "leancanvas-" + id + "-save.json";
        var description = "Public Gist: Lean canvas " + id + " save file to keep up";
        var data = {
            "files": {},
            "description": description,
            "public": true
        };

        data["files"][filename] = {
            "content": JSON.stringify(this.getJSON(), null, 4)
        };

        this.loader = new Loader();
        this.loader.send(
                    "POST",
                    true,
                    "https://api.github.com/gists",
                    "application/x-www-form-urlencoded",
                    JSON.stringify(data),
                    function(result) {
                        success.call(this, result);
                    },
                    function(result) {
                        error.call(this, result);
                    }
        );
    }

    // [Public]
    // loads gist from github
    this.loadGistFromGithub = function(link, success, error) {
        var a = link.split("/");
        var gistid = (a.length==0)?"":a[a.length-1];

        this.loader = new Loader();
        this.loader.send(
            "GET",
            true,
            "https://api.github.com/gists/"+gistid,
            "application/x-www-form-urlencoded",
            null,
            function(result) {
                success.call(this, result);
            },
            function(result) {
                error.call(this, result);
            }
        );
    }

    // [Public]
    // builds content from json representation
    // argument can be json string or json object
    this.buildContentFromJSON = function(content, loadHandler, saveHandler, addNoteHandler, noteEditHandler, noteRemoveHandler) {
        if (!content || !loadHandler || !saveHandler || !addNoteHandler || !noteEditHandler || !noteRemoveHandler) {
            throw ("content is not initiliasid properly is wrong");
        }

        var cont = {};
        try {
            if ((typeof content) === "string") {
                cont = JSON.parse(content);
            } else {
                cont = content;
            }
        } catch (e) {
            cont = DEFAULT_CONTENT;
        }

        var data = cont.data;

        // 0. clear contents
        this.contents = [];
        this.json = "{}";

        // 1. main block
        var main = data[0];
        var MainContent = new Content(main.parentid);
        var main_columns = main.columns;
        for (var i=0; i<main_columns.length; i++) {
            var column = new Column(main_columns[i].id, MainContent);
            MainContent.addColumn(column);
            var main_directories = main_columns[i].directories;

            // load directories
            for (var j=0; j<main_directories.length; j++) {
                var directory = new Directory(main_directories[j].id, main_directories[j].name, column, addNoteHandler);
                directory.setPlaceholder(main_directories[j].placeholder);
                column.addDirectory(directory);
                // load notes
                var main_notes = main_directories[j].notes;
                for (var k=0; k<main_notes.length; k++) {
                    var note = new Note(main_notes[k].id, main_notes[k].text, directory, noteEditHandler, noteRemoveHandler);
                    directory.addNote(note);
                }
            }
        }

        // 2. bottom block
        var bottom = data[1];
        var BottomContent = new Content(bottom.parentid);
        var bottom_columns = bottom.columns;
        for (var i=0; i<bottom_columns.length; i++) {
            var column = new Column(bottom_columns[i].id, BottomContent);
            BottomContent.addColumn(column);
            var bottom_directories = bottom_columns[i].directories;

            // load directories
            for (var j=0; j<bottom_directories.length; j++) {
                var directory = new Directory(bottom_directories[j].id, bottom_directories[j].name, column, addNoteHandler);
                directory.setPlaceholder(bottom_directories[j].placeholder);
                column.addDirectory(directory);
                // load notes
                var bottom_notes = bottom_directories[j].notes;
                for (var k=0; k<bottom_notes.length; k++) {
                    var note = new Note(bottom_notes[k].id, bottom_notes[k].text, directory, noteEditHandler, noteRemoveHandler);
                    directory.addNote(note);
                }
            }
        }

        // 3. add contents to dataManager
        this.contents.push(MainContent);
        this.contents.push(BottomContent);
    }

    // [Private]
    // builds DOM for current state of the data
    this.buildDOM = function() {
        var MainContent = this.contents[0];
        var BottomContent = this.contents[1];

        var mainDiv = createElement("div", MainContent.parentId, "", null, this.root);
        mainDiv.appendChild(MainContent.generateDOM());

        var bottomDiv = createElement("div", BottomContent.parentId, "", null, this.root);
        bottomDiv.appendChild(BottomContent.generateDOM());
    }

    // [Public]
    // update DOM, so it reflects latest changes
    this.updateDOM = function() {
        this.root.innerHTML = "";
        this.buildDOM();
    }

    // [Public]
    // returns content from cookie, if it is not found returns null
    this.getContentFromCookie = function() {
        var c = document.cookie;
        if (!c || c.length == 0)
            return null;

        c = "; " + c;
        var parts = c.split("; " + this.COOKIE_NAME + "=");
        if (parts.length == 2)
            return parts[parts.length-1];

        return null;
    }

    // [Public]
    // saves current content into cookie
    this.saveContentIntoCookie = function() {
        var d = new Date();
        d.setTime(d.getTime() + (this.COOKIE_EXPIRE_SEC*1000));
        document.cookie = this.COOKIE_NAME + "=" + JSON.stringify(this.getJSON()) + "; "
                        + "expires=" + d.toUTCString() + "; "
                        + "Path=/; Domain=.sadikovi.github.io";
    }

    // [Public]
    // toggles autosave feature
    this.toggleAutosaveContent = function(isOn, onAutosaved) {
        if (isOn) {
            // turn on and start updating
            if (this.autosaveTimer)
                this.toggleAutosaveContent(!isOn);

            var te = this;
            this.autosaveTimer = setInterval(
                function() {
                    te.saveContentIntoCookie();
                    if (onAutosaved) {onAutosaved.call(this);}
                }, this.autosaveTime);
            console.log("timer is on");
        } else {
            // turn off and remove all events
            if (this.autosaveTimer)
                clearInterval(this.autosaveTimer);
            console.log("timer is off");
        }
    }
};
<|endoftext|>


<commit_before>export { PassThrough_FiltersArray_BiasesArray };
export { PassThrough_FiltersArray_BiasesArray_Root };
export { PassThrough_FiltersArray_BiasesArray_Bag };
export { PassThrough };

import * as Pool from "../../util/Pool.js";
import * as Recyclable from "../../util/Recyclable.js";
import * as ValueDesc from "../../Unpacker/ValueDesc.js";
import * as TwoTensors from "../../util/TwoTensors.js";
import * as MultiLayerMap from "../../util/MultiLayerMap.js";
import { PadInfoCalculator } from "./Depthwise_PadInfoCalculator.js";

/**
 * A depthwise convolution and bias which just pass the input to output.
 *
 * It is usually used in passing the higher half channels of the input to output (for achieving ShuffleNetV2_ByMopbileNetV1's body/tail).
 *
 * Note1: Although depthwise (and pointwise) convolution could be past-through, the activation function will destroy the past-through
 * result. Using Pointwise_FiltersArray_BiasesArray may be better to handle this issue.
 *
 * Note2: If both effectFilterValue and surroundingFilterValue are the same as ( 1 / ( filterHeight * filter Width ) ), the result filter
 * will have the same effect as average pooling.
 *
 *
 *
 * @member {number} effectFilterValue
 *   The value used as the effect value of the pass-through depthwise convolution filter. Default is 1. If there will be no activation
 * function after this pass-through operation, value 1 is enough. However, if there wiil be an activation function, this past-through
 * result might be destroyed by the activation function. In order to alleviate this issue, a non-one filter value should be used. For
 * example, if every input value's range is [ 0,255 ] and RELU6 will be used as activation function, using 0.015625 (= 1 / 64 ) as
 * filterValue is appropriate because input values will be shrinked from [ 0, 255 ] into [ 0, 3.984375 ] which will still be kept
 * linear by RELU6.
 *
 * @member {number} surroundingFilterValue
 *   The value used as the surrounding value of the pass-through depthwise convolution filter. Default is 0.
 *
 * @member {number} biasValue
 *   The value used as the pass-through bias (used only if ( bBias == true ) ). Default is 0. If there will be no activation function
 * after this pass-through operation, value 0 is enough. However, if there wiil be an activation function, this past-through result
 * might be destroyed by the activation function. In order to alleviate this issue, a non-zero bias value should be used. For example,
 * if every input value's range is [ -2, +2 ] and RELU6 will be used as activation function, using +2 as biasValue is appropriate
 * because input values will be shifted from [ -2, +2 ] into [ 0, 4 ] which will still be kept linear by RELU6.
 *
 * @member {number[]} filtersShape
 *   The shape of the pass-through filters array.
 *
 * @member {number[]} biasesShape
 *   The shape of the pass-through biases array.
 *
 * @member {number[]} filtersArray
 *   The pass-through filters array.
 *
 * @member {number[]} biasesArray
 *   The pass-through biases array.
 *
 * @see PadInfoCalculator
 */
let PassThrough_FiltersArray_BiasesArray
  = ( ParentClass = Object ) => class PassThrough_FiltersArray_BiasesArray extends PadInfoCalculator( ParentClass ) {

  /**
   * Used as default Depthwise.PassThrough_FiltersArray_BiasesArray provider for conforming to Recyclable interface.
   */
  static Pool = new Pool.Root( "Depthwise.PassThrough_FiltersArray_BiasesArray.Pool",
    PassThrough_FiltersArray_BiasesArray, PassThrough_FiltersArray_BiasesArray.setAsConstructor );

  /**
   */
  constructor(
    inputHeight, inputWidth, inputChannelCount, AvgMax_Or_ChannelMultiplier, filterHeight, filterWidth, stridesPad,
    bBias, effectFilterValue = 1, surroundingFilterValue = 0, biasValue = 0 ) {

    super( inputHeight, inputWidth, inputChannelCount, AvgMax_Or_ChannelMultiplier, filterHeight, filterWidth, stridesPad );
    PassThrough_FiltersArray_BiasesArray.setAsConstructor_self.call( this,
      bBias, effectFilterValue, surroundingFilterValue, biasValue );
  }

  /** @override */
  static setAsConstructor(
    inputHeight, inputWidth, inputChannelCount, AvgMax_Or_ChannelMultiplier, filterHeight, filterWidth, stridesPad,
    bBias, effectFilterValue = 1, surroundingFilterValue = 0, biasValue = 0 ) {

    super.setAsConstructor( inputHeight, inputWidth, inputChannelCount, AvgMax_Or_ChannelMultiplier, filterHeight, filterWidth, stridesPad );
    PassThrough_FiltersArray_BiasesArray.setAsConstructor_self.call( this,
      bBias, effectFilterValue, surroundingFilterValue, biasValue );
    return this;
  }

  /** @override */
  static setAsConstructor_self( bBias, effectFilterValue = 1, surroundingFilterValue = 0, biasValue = 0 ) {
    this.bBias = bBias;
    this.effectFilterValue = effectFilterValue;
    this.surroundingFilterValue = surroundingFilterValue;
    this.biasValue = biasValue;

    this.filtersShape = Recyclable.Array.Pool.get_or_create_by( 4 );
    this.filtersShape[ 0 ] = this.filterHeight;
    this.filtersShape[ 1 ] = this.filterWidth;
    this.filtersShape[ 2 ] = this.inputChannelCount;
    this.filtersShape[ 3 ] = this.channelMultiplier;

    PassThrough_FiltersArray_BiasesArray.generate_PassThrough_FiltersArray.call( this, effectFilterValue, surroundingFilterValue );

    if ( this.bBias ) {
      this.biasesShape = Recyclable.Array.Pool.get_or_create_by( 3 );
      this.biasesShape[ 0 ] = 1;
      this.biasesShape[ 1 ] = 1;
      this.biasesShape[ 2 ] = this.outputChannelCount;

      this.biasesArray = Recyclable.Array.Pool.get_or_create_by( this.outputChannelCount );
      this.biasesArray.fill( biasValue );
    }
  }

  /** @override */
  disposeResources() {
    if ( this.biasesArray ) {
      this.biasesArray.disposeResources_and_recycleToPool();
      this.biasesArray = null;
    }

    if ( this.biasesShape ) {
      this.biasesShape.disposeResources_and_recycleToPool();
      this.biasesShape = null;
    }

    if ( this.filtersArray ) {
      this.filtersArray.disposeResources_and_recycleToPool();
      this.filtersArray = null;
    }

    if ( this.filtersShape ) {
      this.filtersShape.disposeResources_and_recycleToPool();
      this.filtersShape = null;
    }

    super.disposeResources();
  }

  /**
   * Determine the following properties:
   *   - this.filtersArray
   *
   * It is an number array representing the depthwise convolution filters which could pass the input to output unchangely.
   *
   * If both effectFilterValue and otherFilterValue are the same as ( 1 / ( filterHeight * filter Width ) ), the result filter
   * will have the same effect as average pooling.
   *
   *
   * @param {number} effectFilterValue
   *   The filter value used for the effect input pixel of the depthwise convolution. For pass-through, it is usually 1.
   * Note: It is not always just at center of filter according to the filter shape and paddding.
   *
   * @param {number} surroundingFilterValue
   *   The filter value used for the surrounding input pixel of the depthwise convolution. For pass-through, it is usually 0.
   *
   */
  static generate_PassThrough_FiltersArray( effectFilterValue, surroundingFilterValue ) {

    if (   ( ValueDesc.AvgMax_Or_ChannelMultiplier.Singleton.Ids.AVG === this.AvgMax_Or_ChannelMultiplier )
        || ( ValueDesc.AvgMax_Or_ChannelMultiplier.Singleton.Ids.MAX === this.AvgMax_Or_ChannelMultiplier ) ) {

      if ( this.filtersArray ) {
        this.filtersArray.disposeResources_and_recycleToPool();
        this.filtersArray = null;
      }

      return; // The depthwise filter of AVG pooling and MAX pooling can not be manipulated.
    }

    // Make up a depthwise convolution filter.
    this.filtersArray = Recyclable.Array.Pool.get_or_create_by(
      this.filterHeight * this.filterWidth * this.inputChannelCount * this.channelMultiplier );

    // There is only one position (inside the effect depthwise filter) uses effectFilterValue.
    // All other positions of the filter should be surroundingFilterValue.
    //
    let oneEffectFilterY = this.padHeightTop;
    let oneEffectFilterX = this.padWidthLeft;

    // Note: Unfortunately, this may not work for ( dilation > 1 ) because the non-zero-filter-value might be just at the dilation
    //       position which does not exist in a filter. So, only ( dilation == 1 ) is supported.

    let filterIndex = 0; // The index in the filter weights array.

    for ( let filterY = 0, effectFilterY = 0; filterY < this.filterHeight; ++filterY ) {
      for ( let dilationFilterY = 0; dilationFilterY < this.dilationHeight; ++dilationFilterY, ++effectFilterY ) {

        for ( let filterX = 0, effectFilterX = 0; filterX < this.filterWidth; ++filterX ) {
          for ( let dilationFilterX = 0; dilationFilterX < this.dilationWidth; ++dilationFilterX, ++effectFilterX ) {

            // The filter's dilation part can not be manipulated. (They are always zero.)
            if ( ( 0 != dilationFilterY ) || ( 0 != dilationFilterX ) )
              continue;

            for ( let inChannel = 0; inChannel < this.inputChannelCount; ++inChannel ) {

              for ( let outChannelSub = 0; outChannelSub < this.channelMultiplier; ++outChannelSub ) {

                if ( ( effectFilterY == oneEffectFilterY ) && ( effectFilterX == oneEffectFilterX ) ) {
                  this.filtersArray[ filterIndex ] = effectFilterValue;
                } else {
                  this.filtersArray[ filterIndex ] = surroundingFilterValue;
                }

                ++filterIndex;
              }
            }
          }
        }
      }
    }
  }

}

/**
 * Almost the same as Depthwise.PassThrough_FiltersArray_BiasesArray class except its parent class is fixed to Object. In other words,
 * caller can not specify the parent class of Depthwise.PassThrough_FiltersArray_BiasesArray_Root (so it is named "Root" which can not
 * have parent class).
 */
class PassThrough_FiltersArray_BiasesArray_Root extends PassThrough_FiltersArray_BiasesArray() {
}

/**
 * A pool for PassThrough_FiltersArray_BiasesArray with various parameters. It could reduce re-creating them of same parameters again
 * and again to improve performance.
 *
 */
class PassThrough_FiltersArray_BiasesArray_Bag extends Recyclable.Base( MultiLayerMap.Base ) {

  /**
   * Used as default Depthwise.PassThrough_FiltersArray_BiasesArray_Bag provider for conforming to Recyclable interface.
   */
  static Pool = new Pool.Root( "Depthwise.PassThrough_FiltersArray_BiasesArray_Bag.Pool",
    PassThrough_FiltersArray_BiasesArray_Bag, PassThrough_FiltersArray_BiasesArray_Bag.setAsConstructor );

  /**
   */
  constructor() {
    super();
    PassThrough_FiltersArray_BiasesArray_Bag.setAsConstructor_self.call( this );
  }

  /** @override */
  static setAsConstructor() {
    super.setAsConstructor();
    PassThrough_FiltersArray_BiasesArray_Bag.setAsConstructor_self.call( this );
    return this;
  }

  /** @override */
  static setAsConstructor_self() {
  }

  /** @override */
  disposeResources() {
    this.clear();
    super.disposeResources();
  }

  /** @override */
  clear() {
    for ( let aPassThrough_FiltersArray_BiasesArray of this.values() ) {
      aPassThrough_FiltersArray_BiasesArray.disposeResources_and_recycleToPool();
    }
    super.clear();
  }

  /**
   *
   */
  get_by_PassThroughStyleId(
    inputHeight, inputWidth, inputChannelCount, AvgMax_Or_ChannelMultiplier, filterHeight, filterWidth, stridesPad,
    bBias, nPassThroughStyleId ) {

    const thePassThroughStyleInfo = ValueDesc.PassThroughStyle.Singleton.getInfoById( nPassThroughStyleId );
    return this.get_by_effectFilterValue_surroundingFilterValue_biasValue(
      inputHeight, inputWidth, inputChannelCount, AvgMax_Or_ChannelMultiplier, filterHeight, filterWidth, stridesPad,
      bBias, thePassThroughStyleInfo.filterValue, 0, thePassThroughStyleInfo.biasValue );
  }

  /**
   *
   */
  get_by_effectFilterValue_surroundingFilterValue_biasValue(
    inputHeight, inputWidth, inputChannelCount, AvgMax_Or_ChannelMultiplier, filterHeight, filterWidth, stridesPad,
    bBias, effectFilterValue = 1, surroundingFilterValue = 0, biasValue = 0 ) {

    return this.get_or_create_by_arguments1_etc( PassThrough_FiltersArray_BiasesArray_Bag.create_by, this,
      inputHeight, inputWidth, inputChannelCount, AvgMax_Or_ChannelMultiplier, filterHeight, filterWidth, stridesPad,
      bBias, effectFilterValue, surroundingFilterValue, biasValue );
  }

  /** */
  static create_by(
    inputHeight, inputWidth, inputChannelCount, AvgMax_Or_ChannelMultiplier, filterHeight, filterWidth, stridesPad,
    bBias, effectFilterValue, surroundingFilterValue, biasValue ) {

    return new PassThrough_FiltersArray_BiasesArray_Root(
      inputHeight, inputWidth, inputChannelCount, AvgMax_Or_ChannelMultiplier, filterHeight, filterWidth, stridesPad,
      bBias, effectFilterValue, surroundingFilterValue, biasValue );
  }

}

/**
 * A depthwise convolution and bias which just pass the input to output.
 *
 * It is usually used in passing the higher half channels of the input to output (for achieving ShuffleNetV2_ByMopbileNetV1's body/tail).
 *
 *
 * @see PassThrough_FiltersArray_BiasesArray
 * @see PadInfoCalculator
 * @see TwoTensors.filtersTensor4d_biasesTensor3d
 */
class PassThrough extends PassThrough_FiltersArray_BiasesArray( TwoTensors.filtersTensor4d_biasesTensor3d() ) {

  /**
   * Used as default Depthwise.PassThrough provider for conforming to Recyclable interface.
   */
  static Pool = new Pool.Root( "Depthwise.PassThrough.Pool", PassThrough, PassThrough.setAsConstructor );

  /**
   */
  constructor(
    inputHeight, inputWidth, inputChannelCount, AvgMax_Or_ChannelMultiplier, filterHeight, filterWidth, stridesPad,
    bBias, effectFilterValue = 1, surroundingFilterValue = 0, biasValue = 0 ) {

    super( inputHeight, inputWidth, inputChannelCount, AvgMax_Or_ChannelMultiplier, filterHeight, filterWidth, stridesPad,
      bBias, effectFilterValue, surroundingFilterValue, biasValue );

    PassThrough.setAsConstructor_self.call( this );
  }

  /** @override */
  static setAsConstructor(
    inputHeight, inputWidth, inputChannelCount, AvgMax_Or_ChannelMultiplier, filterHeight, filterWidth, stridesPad,
    bBias, effectFilterValue = 1, surroundingFilterValue = 0, biasValue = 0 ) {

    super.setAsConstructor( inputHeight, inputWidth, inputChannelCount, AvgMax_Or_ChannelMultiplier, filterHeight, filterWidth, stridesPad,
      bBias, effectFilterValue, surroundingFilterValue, biasValue );

    PassThrough.setAsConstructor_self.call( this );
    return this;
  }

  /** @override */
  static setAsConstructor_self() {
    this.filtersTensor4d = tf.tensor4d( this.filtersArray, this.filtersShape );
    if ( this.bBias ) {
      this.biasesTensor3d = tf.tensor3d( this.biasesArray, this.biasesShape );
    }
    this.bInitOk = true;
  }

  /** @override */
  disposeResources() {
    this.bInitOk = false;
    super.disposeResources(); // Release .filtersTensor4d and biasesTensor3d.
  }

}

<commit_msg>Update Depthwise_PassThrough.js<commit_after>export { PassThrough_FiltersArray_BiasesArray };
export { PassThrough_FiltersArray_BiasesArray_Root };
export { PassThrough_FiltersArray_BiasesArray_Bag };
export { PassThrough };

import * as Pool from "../../util/Pool.js";
import * as Recyclable from "../../util/Recyclable.js";
import * as ValueDesc from "../../Unpacker/ValueDesc.js";
import * as TwoTensors from "../../util/TwoTensors.js";
import * as MultiLayerMap from "../../util/MultiLayerMap.js";
import { PadInfoCalculator } from "./Depthwise_PadInfoCalculator.js";

/**
 * A depthwise convolution and bias which just pass the input to output.
 *
 * It is usually used in passing the higher half channels of the input to output (for achieving ShuffleNetV2_ByMopbileNetV1's body/tail).
 *
 * Note1: Although depthwise (and pointwise) convolution could be past-through, the activation function will destroy the past-through
 * result. Using Pointwise_FiltersArray_BiasesArray may be better to handle this issue.
 *
 * Note2: If both effectFilterValue and surroundingFilterValue are the same as ( 1 / ( filterHeight * filter Width ) ), the result filter
 * will have the same effect as average pooling.
 *
 *
 *
 * @member {number} effectFilterValue
 *   The value used as the effect value of the pass-through depthwise convolution filter. Default is 1. If there will be no activation
 * function after this pass-through operation, value 1 is enough. However, if there wiil be an activation function, this past-through
 * result might be destroyed by the activation function. In order to alleviate this issue, a non-one filter value should be used. For
 * example, if every input value's range is [ 0,255 ] and RELU6 will be used as activation function, using 0.015625 (= 1 / 64 ) as
 * filterValue is appropriate because input values will be shrinked from [ 0, 255 ] into [ 0, 3.984375 ] which will still be kept
 * linear by RELU6.
 *
 * @member {number} surroundingFilterValue
 *   The value used as the surrounding value of the pass-through depthwise convolution filter. Default is 0.
 *
 * @member {number} biasValue
 *   The value used as the pass-through bias (used only if ( bBias == true ) ). Default is 0. If there will be no activation function
 * after this pass-through operation, value 0 is enough. However, if there wiil be an activation function, this past-through result
 * might be destroyed by the activation function. In order to alleviate this issue, a non-zero bias value should be used. For example,
 * if every input value's range is [ -2, +2 ] and RELU6 will be used as activation function, using +2 as biasValue is appropriate
 * because input values will be shifted from [ -2, +2 ] into [ 0, 4 ] which will still be kept linear by RELU6.
 *
 * @member {number[]} filtersShape
 *   The shape of the pass-through filters array.
 *
 * @member {number[]} biasesShape
 *   The shape of the pass-through biases array.
 *
 * @member {number[]} filtersArray
 *   The pass-through filters array.
 *
 * @member {number[]} biasesArray
 *   The pass-through biases array.
 *
 * @see PadInfoCalculator
 */
let PassThrough_FiltersArray_BiasesArray
  = ( ParentClass = Object ) => class PassThrough_FiltersArray_BiasesArray extends PadInfoCalculator( ParentClass ) {

  /**
   * Used as default Depthwise.PassThrough_FiltersArray_BiasesArray provider for conforming to Recyclable interface.
   */
  static Pool = new Pool.Root( "Depthwise.PassThrough_FiltersArray_BiasesArray.Pool",
    PassThrough_FiltersArray_BiasesArray, PassThrough_FiltersArray_BiasesArray.setAsConstructor );

  /**
   */
  constructor(
    inputHeight, inputWidth, inputChannelCount, AvgMax_Or_ChannelMultiplier, filterHeight, filterWidth, stridesPad,
    bBias, effectFilterValue = 1, surroundingFilterValue = 0, biasValue = 0 ) {

    super( inputHeight, inputWidth, inputChannelCount, AvgMax_Or_ChannelMultiplier, filterHeight, filterWidth, stridesPad );
    PassThrough_FiltersArray_BiasesArray.setAsConstructor_self.call( this,
      bBias, effectFilterValue, surroundingFilterValue, biasValue );
  }

  /** @override */
  static setAsConstructor(
    inputHeight, inputWidth, inputChannelCount, AvgMax_Or_ChannelMultiplier, filterHeight, filterWidth, stridesPad,
    bBias, effectFilterValue = 1, surroundingFilterValue = 0, biasValue = 0 ) {

    super.setAsConstructor( inputHeight, inputWidth, inputChannelCount, AvgMax_Or_ChannelMultiplier, filterHeight, filterWidth, stridesPad );
    PassThrough_FiltersArray_BiasesArray.setAsConstructor_self.call( this,
      bBias, effectFilterValue, surroundingFilterValue, biasValue );
    return this;
  }

  /** @override */
  static setAsConstructor_self( bBias, effectFilterValue = 1, surroundingFilterValue = 0, biasValue = 0 ) {
    this.bBias = bBias;
    this.effectFilterValue = effectFilterValue;
    this.surroundingFilterValue = surroundingFilterValue;
    this.biasValue = biasValue;

    this.filtersShape = Recyclable.Array.Pool.get_or_create_by( 4 );
    this.filtersShape[ 0 ] = this.filterHeight;
    this.filtersShape[ 1 ] = this.filterWidth;
    this.filtersShape[ 2 ] = this.inputChannelCount;
    this.filtersShape[ 3 ] = this.channelMultiplier;

    PassThrough_FiltersArray_BiasesArray.generate_PassThrough_FiltersArray.call( this, effectFilterValue, surroundingFilterValue );

    if ( this.bBias ) {
      this.biasesShape = Recyclable.Array.Pool.get_or_create_by( 3 );
      this.biasesShape[ 0 ] = 1;
      this.biasesShape[ 1 ] = 1;
      this.biasesShape[ 2 ] = this.outputChannelCount;

      this.biasesArray = Recyclable.Array.Pool.get_or_create_by( this.outputChannelCount );
      this.biasesArray.fill( biasValue );
    }
  }

  /** @override */
  disposeResources() {
    if ( this.biasesArray ) {
      this.biasesArray.disposeResources_and_recycleToPool();
      this.biasesArray = null;
    }

    if ( this.biasesShape ) {
      this.biasesShape.disposeResources_and_recycleToPool();
      this.biasesShape = null;
    }

    if ( this.filtersArray ) {
      this.filtersArray.disposeResources_and_recycleToPool();
      this.filtersArray = null;
    }

    if ( this.filtersShape ) {
      this.filtersShape.disposeResources_and_recycleToPool();
      this.filtersShape = null;
    }

    super.disposeResources();
  }

  /**
   * Determine the following properties:
   *   - this.filtersArray
   *
   * It is an number array representing the depthwise convolution filters which could pass the input to output unchangely.
   *
   * If both effectFilterValue and otherFilterValue are the same as ( 1 / ( filterHeight * filter Width ) ), the result filter
   * will have the same effect as average pooling.
   *
   *
   * @param {number} effectFilterValue
   *   The filter value used for the effect input pixel of the depthwise convolution. For pass-through, it is usually 1.
   * Note: It is not always just at center of filter according to the filter shape and paddding.
   *
   * @param {number} surroundingFilterValue
   *   The filter value used for the surrounding input pixel of the depthwise convolution. For pass-through, it is usually 0.
   *
   */
  static generate_PassThrough_FiltersArray( effectFilterValue, surroundingFilterValue ) {

    if (   ( ValueDesc.AvgMax_Or_ChannelMultiplier.Singleton.Ids.AVG === this.AvgMax_Or_ChannelMultiplier )
        || ( ValueDesc.AvgMax_Or_ChannelMultiplier.Singleton.Ids.MAX === this.AvgMax_Or_ChannelMultiplier ) ) {

      if ( this.filtersArray ) {
        this.filtersArray.disposeResources_and_recycleToPool();
        this.filtersArray = null;
      }

      return; // The depthwise filter of AVG pooling and MAX pooling can not be manipulated.
    }

    // Make up a depthwise convolution filter.
    this.filtersArray = Recyclable.Array.Pool.get_or_create_by(
      this.filterHeight * this.filterWidth * this.inputChannelCount * this.channelMultiplier );

    // There is only one position (inside the effect depthwise filter) uses effectFilterValue.
    // All other positions of the filter should be surroundingFilterValue.
    //
    let oneEffectFilterY = this.padHeightTop;
    let oneEffectFilterX = this.padWidthLeft;

    // Note: Unfortunately, this may not work for ( dilation > 1 ) because the non-zero-filter-value might be just at the dilation
    //       position which does not exist in a filter. So, only ( dilation == 1 ) is supported.

    let filterIndex = 0; // The index in the filter weights array.

    for ( let filterY = 0, effectFilterY = 0; filterY < this.filterHeight; ++filterY ) {
      for ( let dilationFilterY = 0; dilationFilterY < this.dilationHeight; ++dilationFilterY, ++effectFilterY ) {

        for ( let filterX = 0, effectFilterX = 0; filterX < this.filterWidth; ++filterX ) {
          for ( let dilationFilterX = 0; dilationFilterX < this.dilationWidth; ++dilationFilterX, ++effectFilterX ) {

            // The filter's dilation part can not be manipulated. (They are always zero.)
            if ( ( 0 != dilationFilterY ) || ( 0 != dilationFilterX ) )
              continue;

            for ( let inChannel = 0; inChannel < this.inputChannelCount; ++inChannel ) {

              for ( let outChannelSub = 0; outChannelSub < this.channelMultiplier; ++outChannelSub ) {

                if ( ( effectFilterY == oneEffectFilterY ) && ( effectFilterX == oneEffectFilterX ) ) {
                  this.filtersArray[ filterIndex ] = effectFilterValue;
                } else {
                  this.filtersArray[ filterIndex ] = surroundingFilterValue;
                }

                ++filterIndex;
              }
            }
          }
        }
      }
    }
  }

}

/**
 * Almost the same as Depthwise.PassThrough_FiltersArray_BiasesArray class except its parent class is fixed to Object. In other words,
 * caller can not specify the parent class of Depthwise.PassThrough_FiltersArray_BiasesArray_Root (so it is named "Root" which can not
 * have parent class).
 */
class PassThrough_FiltersArray_BiasesArray_Root extends PassThrough_FiltersArray_BiasesArray() {
}

/**
 * A pool for PassThrough_FiltersArray_BiasesArray with various parameters. It could reduce re-creating them of same parameters again
 * and again to improve performance.
 *
 */
class PassThrough_FiltersArray_BiasesArray_Bag extends Recyclable.Base( MultiLayerMap.Base ) {

  /**
   * Used as default Depthwise.PassThrough_FiltersArray_BiasesArray_Bag provider for conforming to Recyclable interface.
   */
  static Pool = new Pool.Root( "Depthwise.PassThrough_FiltersArray_BiasesArray_Bag.Pool",
    PassThrough_FiltersArray_BiasesArray_Bag, PassThrough_FiltersArray_BiasesArray_Bag.setAsConstructor );

  /**
   */
  constructor() {
    super();
    PassThrough_FiltersArray_BiasesArray_Bag.setAsConstructor_self.call( this );
  }

  /** @override */
  static setAsConstructor() {
    super.setAsConstructor();
    PassThrough_FiltersArray_BiasesArray_Bag.setAsConstructor_self.call( this );
    return this;
  }

  /** @override */
  static setAsConstructor_self() {
  }

  /** @override */
  disposeResources() {
    this.clear();
    super.disposeResources();
  }

  /** @override */
  clear() {
    for ( let aPassThrough_FiltersArray_BiasesArray of this.values() ) {
      aPassThrough_FiltersArray_BiasesArray.disposeResources_and_recycleToPool();
    }
    super.clear();
  }

  /**
   *
   */
  get_by_PassThroughStyleId(
    inputHeight, inputWidth, inputChannelCount, AvgMax_Or_ChannelMultiplier, filterHeight, filterWidth, stridesPad,
    bBias, nPassThroughStyleId ) {

    const thePassThroughStyleInfo = ValueDesc.PassThroughStyle.Singleton.getInfoById( nPassThroughStyleId );
    return this.get_by_effectFilterValue_surroundingFilterValue_biasValue(
      inputHeight, inputWidth, inputChannelCount, AvgMax_Or_ChannelMultiplier, filterHeight, filterWidth, stridesPad,
      bBias, thePassThroughStyleInfo.filterValue, 0, thePassThroughStyleInfo.biasValue );
  }

  /**
   *
   */
  get_by_effectFilterValue_surroundingFilterValue_biasValue(
    inputHeight, inputWidth, inputChannelCount, AvgMax_Or_ChannelMultiplier, filterHeight, filterWidth, stridesPad,
    bBias, effectFilterValue = 1, surroundingFilterValue = 0, biasValue = 0 ) {

    return this.get_or_create_by_arguments1_etc( PassThrough_FiltersArray_BiasesArray_Bag.create_by, this,
      inputHeight, inputWidth, inputChannelCount, AvgMax_Or_ChannelMultiplier, filterHeight, filterWidth, stridesPad,
      bBias, effectFilterValue, surroundingFilterValue, biasValue );
  }

  /** */
  static create_by(
    inputHeight, inputWidth, inputChannelCount, AvgMax_Or_ChannelMultiplier, filterHeight, filterWidth, stridesPad,
    bBias, effectFilterValue, surroundingFilterValue, biasValue ) {

    return PassThrough_FiltersArray_BiasesArray_Root.Pool.get_or_create_by(
      inputHeight, inputWidth, inputChannelCount, AvgMax_Or_ChannelMultiplier, filterHeight, filterWidth, stridesPad,
      bBias, effectFilterValue, surroundingFilterValue, biasValue );
  }

}

/**
 * A depthwise convolution and bias which just pass the input to output.
 *
 * It is usually used in passing the higher half channels of the input to output (for achieving ShuffleNetV2_ByMopbileNetV1's body/tail).
 *
 *
 * @see PassThrough_FiltersArray_BiasesArray
 * @see PadInfoCalculator
 * @see TwoTensors.filtersTensor4d_biasesTensor3d
 */
class PassThrough extends PassThrough_FiltersArray_BiasesArray( TwoTensors.filtersTensor4d_biasesTensor3d() ) {

  /**
   * Used as default Depthwise.PassThrough provider for conforming to Recyclable interface.
   */
  static Pool = new Pool.Root( "Depthwise.PassThrough.Pool", PassThrough, PassThrough.setAsConstructor );

  /**
   */
  constructor(
    inputHeight, inputWidth, inputChannelCount, AvgMax_Or_ChannelMultiplier, filterHeight, filterWidth, stridesPad,
    bBias, effectFilterValue = 1, surroundingFilterValue = 0, biasValue = 0 ) {

    super( inputHeight, inputWidth, inputChannelCount, AvgMax_Or_ChannelMultiplier, filterHeight, filterWidth, stridesPad,
      bBias, effectFilterValue, surroundingFilterValue, biasValue );

    PassThrough.setAsConstructor_self.call( this );
  }

  /** @override */
  static setAsConstructor(
    inputHeight, inputWidth, inputChannelCount, AvgMax_Or_ChannelMultiplier, filterHeight, filterWidth, stridesPad,
    bBias, effectFilterValue = 1, surroundingFilterValue = 0, biasValue = 0 ) {

    super.setAsConstructor( inputHeight, inputWidth, inputChannelCount, AvgMax_Or_ChannelMultiplier, filterHeight, filterWidth, stridesPad,
      bBias, effectFilterValue, surroundingFilterValue, biasValue );

    PassThrough.setAsConstructor_self.call( this );
    return this;
  }

  /** @override */
  static setAsConstructor_self() {
    this.filtersTensor4d = tf.tensor4d( this.filtersArray, this.filtersShape );
    if ( this.bBias ) {
      this.biasesTensor3d = tf.tensor3d( this.biasesArray, this.biasesShape );
    }
    this.bInitOk = true;
  }

  /** @override */
  disposeResources() {
    this.bInitOk = false;
    super.disposeResources(); // Release .filtersTensor4d and biasesTensor3d.
  }

}

<|endoftext|>


<commit_before>var express = require('express'),
    pass    = require('passport'),
    errors  = require('./errors'),
    sse     = require('./sse');

function loader(Model, id) {
  return function(req, res, next) {
    var param = req.params[id];
    if (!('string' === typeof param && 24 === param.length)) {
      return next(new errors.BadRequest());
    }
    Model.findById(param, function(err, model) {
      if (err) return next(err);
      if (!model) return next(new errors.NotFound('wrong id'));
      req[Model.modelName] = model;
      next();
    });
  };
}

function ensureAuthenticated(req, res, next) {
  if (req.isAuthenticated()) { return next(); }
  req.session.destroy();
  res.send(403, 'Forbidden');
}

module.exports = function(app, db, pass) {
  var models = require('./models')(db),
      Board  = models.Board,
      Postit = models.Postit,
      Line   = models.Line;

  function touchBoard(req, res, next) {
    Board.findByIdAndUpdate(req.params.bid, { updated_at: new Date() }, function() {});
    return next();
  }

  // Show the login page
  app.get('/login', function(req, res) {
    res.render('login');
  });

  // Check the persona assertion
  app.post( '/api/user',
           pass.authenticate('browserid'),
           function(req,res) { res.send(204); }
          );

  // Logout
  app.post('/logout', function(req, res) {
    req.logout();
    res.send(204);
  });

  // Show the help
  app.get('/help', function(req, res) {
    res.render('help');
  });

  // Show the list of boards
  app.get('/', function(req, res, next) {
    if (!req.isAuthenticated()) { return res.redirect('/login'); }
    Board.find().sort('-updated_at').paginate({ page: req.query.page }, function(err, boards) {
      if(err) return next(err);
      res.render('index', { boards: boards });
    });
  });

  // Create Board
  app.post('/boards', function(req, res, next) {
    if (!req.isAuthenticated()) { return res.redirect('/login'); }
    req.body.updated_at = new Date();
    var board = new Board(req.body);
    board.save(function(err) {
      if (err) return next(err);
      res.redirect('/boards/' + board.id);
    });
  });

  // Show a board
  app.get('/boards/:bid', loader(Board, 'bid'), function(req, res) {
    if (!req.isAuthenticated()) { return res.redirect('/login'); }
    var bid = req.params.bid;
    Postit.find({ board_id : bid }, function(err, postits) {
      if (err) return next(new Error());
      Line.find({ board_id : bid }, function(err, lines) {
        if (err) return next(new Error());
        res.render('board', { board: JSON.stringify(req.board),
                              postits: JSON.stringify(postits),
                              lines: JSON.stringify(lines)
                            });
      });
    });
  });

  // Create Postit
  app.post('/api/boards/:bid/postits', ensureAuthenticated, touchBoard, function(req, res, next) {
    req.body.board_id = req.params.bid;
    req.body.updated_at = new Date();
    var postit = new Postit(req.body);
    postit.save(function(err) {
      if (err) return next(err);
      res.send(201, postit);
      sse.broadcast(postit.board_id, { action: 'create', type: 'postit', model: postit });
    });
  });

  // Update postit
  app.put('/api/boards/:bid/postits/:pid', ensureAuthenticated, touchBoard, function(req, res, next) {
    delete req.body._id;
    req.body.updated_at = new Date();
    Postit.findByIdAndUpdate(req.params.pid, req.body, function(err, postit) {
      if (err) return next(err);
      if (!postit) return next(new errors.NotFound());
      res.send(200, postit);
      sse.broadcast(postit.board_id, { action: 'update', type: 'postit', model: postit });
    });
  });

  // Delete postit
  app.del('/api/boards/:bid/postits/:pid', ensureAuthenticated, loader(Postit, 'pid'), touchBoard, function(req, res, next) {
    req.postit.remove(function(err) {
      if (err) return next(err);
      res.send(204);
      sse.broadcast(req.postit.board_id, { action: 'delete', type: 'postit', model: req.postit });
    });
  });

  // Create line
  app.post('/api/boards/:bid/lines', ensureAuthenticated, touchBoard, function(req, res, next) {
    req.body.board_id = req.params.bid;
    var line = new Line(req.body);
    line.save(function(err) {
      if (err) return next(err);
      res.send(201, line);
      sse.broadcast(line.board_id, { action: 'create', type: 'line', model: line });
    });
  });

  // Update line
  app.put('/api/boards/:bid/lines/:lid', ensureAuthenticated, function(req, res, next) {
    delete req.body._id;
    Line.findByIdAndUpdate(req.params.lid, req.body, function(err, line) {
      if (err) return next(err);
      res.send(200, line);
      sse.broadcast(line.board_id, { action: 'update', type: 'line', model: line });
    });
  });

  // Delete line
  app.del('/api/boards/:bid/lines/:lid', ensureAuthenticated, loader(Line, 'lid'), touchBoard, function(req, res, next) {
    req.line.remove(function(err) {
      if (err) return next(err);
      res.send(204);
      sse.broadcast(req.line.board_id, { action: 'delete', type: 'line', model: req.line });
    });
  });

  app.get('/api/boards/:bid/sse', sse.middleware);
};
<commit_msg>Quick fix for error handling<commit_after>var express = require('express'),
    pass    = require('passport'),
    errors  = require('./errors'),
    sse     = require('./sse');

function loader(Model, id) {
  return function(req, res, next) {
    var param = req.params[id];
    if (!('string' === typeof param && 24 === param.length)) {
      return next(new errors.BadRequest());
    }
    Model.findById(param, function(err, model) {
      if (err) return next(err);
      if (!model) return next(new errors.NotFound('wrong id'));
      req[Model.modelName] = model;
      next();
    });
  };
}

function ensureAuthenticated(req, res, next) {
  if (req.isAuthenticated()) { return next(); }
  req.session.destroy();
  res.send(403, 'Forbidden');
}

module.exports = function(app, db, pass) {
  var models = require('./models')(db),
      Board  = models.Board,
      Postit = models.Postit,
      Line   = models.Line;

  function touchBoard(req, res, next) {
    Board.findByIdAndUpdate(req.params.bid, { updated_at: new Date() }, function() {});
    return next();
  }

  // Show the login page
  app.get('/login', function(req, res) {
    res.render('login');
  });

  // Check the persona assertion
  app.post( '/api/user',
           pass.authenticate('browserid'),
           function(req,res) { res.send(204); }
          );

  // Logout
  app.post('/logout', function(req, res) {
    req.logout();
    res.send(204);
  });

  // Show the help
  app.get('/help', function(req, res) {
    res.render('help');
  });

  // Show the list of boards
  app.get('/', function(req, res, next) {
    if (!req.isAuthenticated()) { return res.redirect('/login'); }
    Board.find().sort('-updated_at').paginate({ page: req.query.page }, function(err, boards) {
      if(err) return next(err);
      res.render('index', { boards: boards });
    });
  });

  // Create Board
  app.post('/boards', function(req, res, next) {
    if (!req.isAuthenticated()) { return res.redirect('/login'); }
    req.body.updated_at = new Date();
    var board = new Board(req.body);
    board.save(function(err) {
      if (err) return next(err);
      res.redirect('/boards/' + board.id);
    });
  });

  // Show a board
  app.get('/boards/:bid', loader(Board, 'bid'), function(req, res, next) {
    if (!req.isAuthenticated()) { return res.redirect('/login'); }
    var bid = req.params.bid;
    Postit.find({ board_id : bid }, function(err, postits) {
      if (err) return next(new Error());
      Line.find({ board_id : bid }, function(err, lines) {
        if (err) return next(new Error());
        res.render('board', { board: JSON.stringify(req.board),
                              postits: JSON.stringify(postits),
                              lines: JSON.stringify(lines)
                            });
      });
    });
  });

  // Create Postit
  app.post('/api/boards/:bid/postits', ensureAuthenticated, touchBoard, function(req, res, next) {
    req.body.board_id = req.params.bid;
    req.body.updated_at = new Date();
    var postit = new Postit(req.body);
    postit.save(function(err) {
      if (err) return next(err);
      res.send(201, postit);
      sse.broadcast(postit.board_id, { action: 'create', type: 'postit', model: postit });
    });
  });

  // Update postit
  app.put('/api/boards/:bid/postits/:pid', ensureAuthenticated, touchBoard, function(req, res, next) {
    delete req.body._id;
    req.body.updated_at = new Date();
    Postit.findByIdAndUpdate(req.params.pid, req.body, function(err, postit) {
      if (err) return next(err);
      if (!postit) return next(new errors.NotFound());
      res.send(200, postit);
      sse.broadcast(postit.board_id, { action: 'update', type: 'postit', model: postit });
    });
  });

  // Delete postit
  app.del('/api/boards/:bid/postits/:pid', ensureAuthenticated, loader(Postit, 'pid'), touchBoard, function(req, res, next) {
    req.postit.remove(function(err) {
      if (err) return next(err);
      res.send(204);
      sse.broadcast(req.postit.board_id, { action: 'delete', type: 'postit', model: req.postit });
    });
  });

  // Create line
  app.post('/api/boards/:bid/lines', ensureAuthenticated, touchBoard, function(req, res, next) {
    req.body.board_id = req.params.bid;
    var line = new Line(req.body);
    line.save(function(err) {
      if (err) return next(err);
      res.send(201, line);
      sse.broadcast(line.board_id, { action: 'create', type: 'line', model: line });
    });
  });

  // Update line
  app.put('/api/boards/:bid/lines/:lid', ensureAuthenticated, function(req, res, next) {
    delete req.body._id;
    Line.findByIdAndUpdate(req.params.lid, req.body, function(err, line) {
      if (err) return next(err);
      res.send(200, line);
      sse.broadcast(line.board_id, { action: 'update', type: 'line', model: line });
    });
  });

  // Delete line
  app.del('/api/boards/:bid/lines/:lid', ensureAuthenticated, loader(Line, 'lid'), touchBoard, function(req, res, next) {
    req.line.remove(function(err) {
      if (err) return next(err);
      res.send(204);
      sse.broadcast(req.line.board_id, { action: 'delete', type: 'line', model: req.line });
    });
  });

  app.get('/api/boards/:bid/sse', sse.middleware);
};
<|endoftext|>


<commit_before><commit_msg>Fix syntax error on line 39<commit_after><|endoftext|>


<commit_before>var View = require('ampersand-view');

module.exports = View.extend({
    template: [
        '',
            '',
            '<input>',
            '
',
                '

',
            '
',
        ''
    ].join(''),
    bindings: {
        'name': {
            type: 'attribute',
            selector: 'input, textarea',
            name: 'name'
        },
        'label': [
            {
                role: 'label'
            },
            {
                type: 'toggle',
                role: 'label'
            }
        ],
        'message': {
            type: 'text',
            role: 'message-text'
        },
        'showMessage': {
            type: 'toggle',
            role: 'message-container'
        },
        'placeholder': {
            type: 'attribute',
            selector: 'input, textarea',
            name: 'placeholder'
        },
        'validityClass': {
            type: 'class',
            selector: 'input, textarea'
        }
    },
    initialize: function (spec) {
        this.tests = this.tests || spec.tests || [];
        this.on('change:type', this.handleTypeChange, this);
        this.handleBlur = this.handleBlur.bind(this);
        this.handleInputChanged = this.handleInputChanged.bind(this);
        this.startingValue = this.value;
        this.on('change:valid change:value', this.reportToParent, this);
    },
    render: function () {
        this.renderWithTemplate();
        this.input = this.get('input') || this.get('textarea');
        // switches out input for textarea if that's what we want
        this.handleTypeChange();
        this.initInputBindings();
    },
    props: {
        value: 'string',
        startingValue: 'string',
        name: 'string',
        type: ['string', true, 'text'],
        placeholder: ['string', true, ''],
        label: ['string', true, ''],
        required: ['boolean', true, true],
        directlyEdited: ['boolean', true, false],
        shouldValidate: ['boolean', true, false],
        message: ['string', true, ''],
        requiredMessage: ['string', true, 'This fields is required.'],
        validClass: ['string', true, 'input-valid'],
        invalidClass: ['string', true, 'input-invalid']
    },
    derived: {
        valid: {
            deps: ['value'],
            fn: function () {
                return !this.runTests();
            }
        },
        showMessage: {
            deps: ['message', 'shouldValidate'],
            fn: function () {
                return this.shouldValidate && this.message;
            }
        },
        changed: {
            deps: ['value', 'startingValue'],
            fn: function () {
                return this.value !== this.startingValue;
            }
        },
        validityClass: {
            deps: ['valid', 'validClass', 'invalidClass', 'shouldValidate'],
            fn: function () {
                if (!this.shouldValidate) {
                    return '';
                } else {
                    return this.valid ? this.validClass : this.invalidClass;
                }
            }
        }
    },
    getErrorMessage: function () {
        var message = '';
        if (this.required && !this.value) {
            return this.requiredMessage;
        } else {
            (this.tests || []).some(function (test) {
                message = test.call(this, this.value) || '';
                return message;
            }, this);
            return message;
        }
    },
    handleTypeChange: function () {
        if (this.type === 'textarea' && this.input.tagName.toLowerCase() !== 'textarea') {
            var parent = this.input.parentNode;
            var textarea = document.createElement('textarea');
            parent.replaceChild(textarea, this.input);
            this.input = textarea;
            this._applyBindingsForKey('');
        } else {
            this.input.type = this.type;
        }
    },
    handleInputChanged: function () {
        if (document.activeElement === this.input) {
            this.directlyEdited = true;
        }
        this.value = this.input.value;
    },
    handleBlur: function () {
        if (this.value && this.changed) {
            this.shouldValidate = true;
        }
        this.runTests();
    },
    beforeSubmit: function () {
        // at the point where we've tried
        // to submit, we want to validate
        // everything from now on.
        this.shouldValidate = true;
        this.runTests();
    },
    runTests: function () {
        var message = this.getErrorMessage();
        if (!message && this.value && this.changed) {
            // if it's ever been valid,
            // we want to validate from now
            // on.
            this.shouldValidate = true;
        }
        this.message = message;
        return message;
    },
    initInputBindings: function () {
        this.input.addEventListener('blur', this.handleBlur, false);
        this.input.addEventListener('input', this.handleInputChanged, false);
    },
    remove: function () {
        this.input.removeEventListener('input', this.handleInputChanged, false);
        this.input.removeEventListener('blur', this.handleBlur, false);
        View.prototype.remove.apply(this, arguments);
    },
    reportToParent: function () {
        if (this.parent) this.parent.update(this);
    }
});
<commit_msg>set initial value<commit_after>var View = require('ampersand-view');

module.exports = View.extend({
    template: [
        '',
            '',
            '<input>',
            '
',
                '

',
            '
',
        ''
    ].join(''),
    bindings: {
        'name': {
            type: 'attribute',
            selector: 'input, textarea',
            name: 'name'
        },
        'label': [
            {
                role: 'label'
            },
            {
                type: 'toggle',
                role: 'label'
            }
        ],
        'message': {
            type: 'text',
            role: 'message-text'
        },
        'showMessage': {
            type: 'toggle',
            role: 'message-container'
        },
        'placeholder': {
            type: 'attribute',
            selector: 'input, textarea',
            name: 'placeholder'
        },
        'validityClass': {
            type: 'class',
            selector: 'input, textarea'
        }
    },
    initialize: function (spec) {
        this.tests = this.tests || spec.tests || [];
        this.on('change:type', this.handleTypeChange, this);
        this.handleBlur = this.handleBlur.bind(this);
        this.handleInputChanged = this.handleInputChanged.bind(this);
        this.startingValue = this.value;
        this.on('change:valid change:value', this.reportToParent, this);
    },
    render: function () {
        this.renderWithTemplate();
        this.input = this.get('input') || this.get('textarea');
        // switches out input for textarea if that's what we want
        this.handleTypeChange();
        this.initInputBindings();
        this.input.value = this.value;
    },
    props: {
        value: 'string',
        startingValue: 'string',
        name: 'string',
        type: ['string', true, 'text'],
        placeholder: ['string', true, ''],
        label: ['string', true, ''],
        required: ['boolean', true, true],
        directlyEdited: ['boolean', true, false],
        shouldValidate: ['boolean', true, false],
        message: ['string', true, ''],
        requiredMessage: ['string', true, 'This fields is required.'],
        validClass: ['string', true, 'input-valid'],
        invalidClass: ['string', true, 'input-invalid']
    },
    derived: {
        valid: {
            deps: ['value'],
            fn: function () {
                return !this.runTests();
            }
        },
        showMessage: {
            deps: ['message', 'shouldValidate'],
            fn: function () {
                return this.shouldValidate && this.message;
            }
        },
        changed: {
            deps: ['value', 'startingValue'],
            fn: function () {
                return this.value !== this.startingValue;
            }
        },
        validityClass: {
            deps: ['valid', 'validClass', 'invalidClass', 'shouldValidate'],
            fn: function () {
                if (!this.shouldValidate) {
                    return '';
                } else {
                    return this.valid ? this.validClass : this.invalidClass;
                }
            }
        }
    },
    getErrorMessage: function () {
        var message = '';
        if (this.required && !this.value) {
            return this.requiredMessage;
        } else {
            (this.tests || []).some(function (test) {
                message = test.call(this, this.value) || '';
                return message;
            }, this);
            return message;
        }
    },
    handleTypeChange: function () {
        if (this.type === 'textarea' && this.input.tagName.toLowerCase() !== 'textarea') {
            var parent = this.input.parentNode;
            var textarea = document.createElement('textarea');
            parent.replaceChild(textarea, this.input);
            this.input = textarea;
            this._applyBindingsForKey('');
        } else {
            this.input.type = this.type;
        }
    },
    handleInputChanged: function () {
        if (document.activeElement === this.input) {
            this.directlyEdited = true;
        }
        this.value = this.input.value;
    },
    handleBlur: function () {
        if (this.value && this.changed) {
            this.shouldValidate = true;
        }
        this.runTests();
    },
    beforeSubmit: function () {
        // at the point where we've tried
        // to submit, we want to validate
        // everything from now on.
        this.shouldValidate = true;
        this.runTests();
    },
    runTests: function () {
        var message = this.getErrorMessage();
        if (!message && this.value && this.changed) {
            // if it's ever been valid,
            // we want to validate from now
            // on.
            this.shouldValidate = true;
        }
        this.message = message;
        return message;
    },
    initInputBindings: function () {
        this.input.addEventListener('blur', this.handleBlur, false);
        this.input.addEventListener('input', this.handleInputChanged, false);
    },
    remove: function () {
        this.input.removeEventListener('input', this.handleInputChanged, false);
        this.input.removeEventListener('blur', this.handleBlur, false);
        View.prototype.remove.apply(this, arguments);
    },
    reportToParent: function () {
        if (this.parent) this.parent.update(this);
    }
});
<|endoftext|>


<commit_before>/* eslint ember/order-in-components: 0 */
import { inject as service } from '@ember/service';
import Component from '@ember/component';
import { computed } from '@ember/object';
import { isEmpty, isPresent } from '@ember/utils';
import { task } from 'ember-concurrency';
import RSVP from 'rsvp';
import { validator, buildValidations } from 'ember-cp-validations';
import ValidationErrorDisplay from 'ilios-common/mixins/validation-error-display';

const { Promise } = RSVP;

const Validations = buildValidations({
  newTermTitle: [
    validator('presence', true),
    validator('length', {
      min: 1,
      max: 200
    }),
    validator('async-exclusion', {
      dependentKeys: ['model.term.children.@each.title'],
      in: computed('model.term.@each.title', function(){
        return new Promise(resolve => {
          const term = this.get('model.term');
          if (isPresent(term)) {
            term.get('children').then(children => {
              resolve(children.mapBy('title'));
            });
          } else {
            resolve([]);
          }
        });
      }),
      descriptionKey: 'general.term',
    })
  ],
});

export default Component.extend(Validations, ValidationErrorDisplay, {
  store: service(),
  flashMessages: service(),
  term: null,
  canUpdate: false,
  canDelete: false,
  canCreate: false,
  newTerm: null,
  vocabulary: null,
  newTermTitle: null,
  isSavingNewTerm: false,
  description: null,
  title: null,
  isActive: null,
  classNames: ['school-vocabulary-term-manager'],
  'data-test-school-vocabulary-term-manager': true,
  didReceiveAttrs(){
    this._super(...arguments);
    const term = this.term;
    if (term) {
      this.set('description', term.get('description'));
      this.set('title', term.get('title'));
      this.set('isActive', term.get('active'));
    }
  },
  sortedTerms: computed('term.children.[]', 'newTerm', function(){
    return new Promise(resolve => {
      const term = this.term;
      if (isPresent(term)) {
        term.get('children').then(terms => {
          resolve(terms.filterBy('isNew', false).filterBy('isDeleted', false).sortBy('title'));
        });
      }
    });
  }),
  allParents: computed('term.allParents.[]', function(){
    return new Promise(resolve => {
      const term = this.term;
      if (isPresent(term)) {
        term.get('allParents').then(allParents => {
          resolve(allParents);
        });
      }
    });
  }),
  keyUp(event) {
    const keyCode = event.keyCode;
    const target = event.target;

    if ('text' !== target.type) {
      return;
    }

    if (13 === keyCode) {
      this.send('createTerm');
    }
  },

  changeIsActive: task(function * (isActive){
    const term = this.term;
    term.set('active', isActive);
    yield term.save();
    this.set('isActive', term.get('active'));
  }).drop(),

  actions: {
    changeTermTitle(){
      const term = this.term;
      const title = this.title;
      term.set('title', title);
      return term.save();
    },
    revertTermTitleChanges(){
      const term = this.term;
      this.set('title', term.get('title'));
    },
    changeTermDescription(){
      const term = this.term;
      const description = this.description;
      term.set('description', description);
      return term.save();
    },
    revertTermDescriptionChanges(){
      const term = this.term;
      this.set('description', term.get('description'));
    },
    createTerm(){
      this.send('addErrorDisplayFor', 'newTermTitle');
      this.set('isSavingNewTerm', true);
      this.validate().then(({validations}) => {
        if (validations.get('isValid')) {
          this.send('removeErrorDisplayFor', 'newTermTitle');
          let title = this.newTermTitle;
          const parent = this.term;
          const vocabulary = this.vocabulary;
          const store = this.store;
          let term = store.createRecord('term', {title, parent, vocabulary, active: true});
          return term.save().then((newTerm) => {
            this.set('newTermTitle', null);
            this.set('newTerm', newTerm);
          });
        }
      }).finally(() => {
        this.set('isSavingNewTerm', false);
      });
    },
    deleteTerm(){
      const term = this.term;
      const manageTerm = this.manageTerm;
      term.get('parent').then(parent => {
        let goTo = isEmpty(parent)?null:parent.get('id');
        manageTerm(goTo);
        term.deleteRecord();
        term.save().then(() => {
          this.flashMessages.success('general.successfullyRemovedTerm');
        });
      });

    },
    clearVocabAndTerm(){
      const manageVocabulary = this.manageVocabulary;
      const manageTerm = this.manageTerm;
      manageVocabulary(null);
      manageTerm(null);
    }
  }
});
<commit_msg>Refactor component to be a bit more modern<commit_after>import { inject as service } from '@ember/service';
import Component from '@ember/component';
import { computed } from '@ember/object';
import { isEmpty, isPresent } from '@ember/utils';
import { task } from 'ember-concurrency';
import { validator, buildValidations } from 'ember-cp-validations';
import ValidationErrorDisplay from 'ilios-common/mixins/validation-error-display';

const Validations = buildValidations({
  newTermTitle: [
    validator('presence', true),
    validator('length', {
      min: 1,
      max: 200
    }),
    validator('async-exclusion', {
      dependentKeys: ['model.term.children.@each.title'],
      in: computed('model.term.children.@each.title', async function(){
        const term = this.get('model.term');
        if (isPresent(term)) {
          const children = await term.children;
          return children.mapBy('title');
        }
        return [];

      }),
      descriptionKey: 'general.term',
    }),
  ],
});

export default Component.extend(Validations, ValidationErrorDisplay, {
  store: service(),
  flashMessages: service(),
  term: null,
  canUpdate: false,
  canDelete: false,
  canCreate: false,
  newTerm: null,
  vocabulary: null,
  newTermTitle: null,
  isSavingNewTerm: false,
  description: null,
  title: null,
  isActive: null,
  classNames: ['school-vocabulary-term-manager'],
  'data-test-school-vocabulary-term-manager': true,
  sortedTerms: computed('term.children.[]', 'newTerm', async function(){
    if (isPresent(this.term)) {
      const terms = await this.term.children;
      return terms.filterBy('isNew', false).filterBy('isDeleted', false).sortBy('title');
    }
  }),
  allParents: computed('term.allParents.[]', async function(){
    if (isPresent(this.term)) {
      return await this.term.allParents;
    }

    return [];
  }),
  didReceiveAttrs(){
    this._super(...arguments);
    if (this.term) {
      this.set('description', this.term.description);
      this.set('title', this.term.title);
      this.set('isActive', this.term.active);
    }
  },

  actions: {
    async changeTermTitle(){
      this.term.set('title', this.title);
      return this.term.save();
    },
    revertTermTitleChanges(){
      this.set('title', this.term.title);
    },
    async changeTermDescription(){
      this.term.set('description', this.description);
      return this.term.save();
    },
    revertTermDescriptionChanges(){
      this.set('description', this.term.description);
    },
    async createTerm() {
      this.send('addErrorDisplayFor', 'newTermTitle');
      this.set('isSavingNewTerm', true);
      try {
        await this.validate();
        if (this.validations.attrs.newTermTitle.isValid) {
          this.send('removeErrorDisplayFor', 'newTermTitle');
          let title = this.newTermTitle;
          let term = this.store.createRecord('term', {
            title,
            parent: this.term,
            vocabulary: this.vocabulary,
            active: true,
          });

          const newTerm = await term.save();
          this.set('newTermTitle', null);
          this.set('newTerm', newTerm);
          return true;
        }
      } finally {
        this.set('isSavingNewTerm', false);
      }
    },
    async deleteTerm() {
      const parent = await this.term.parent;
      let goTo = isEmpty(parent)?null:parent.id;
      this.manageTerm(goTo);
      this.term.deleteRecord();
      await this.term.save();
      this.flashMessages.success('general.successfullyRemovedTerm');
    },
    clearVocabAndTerm(){
      this.manageVocabulary(null);
      this.manageTerm(null);
    }
  },
  keyUp(event) {
    const keyCode = event.keyCode;
    const target = event.target;

    if ('text' !== target.type) {
      return;
    }

    if (13 === keyCode) {
      this.send('createTerm');
    }
  },
  changeIsActive: task(function * (isActive){
    this.term.set('active', isActive);
    yield this.term.save();
    this.set('isActive', this.term.active);
  }).drop(),
});
<|endoftext|>